使用自签名Ca证书签名ssl
最近在家里的服务器搭建了一个Emby服务器,(主要是因为Jellyfin太拉跨了,体验还是拉Emby一大截)并且通过自建的frp给了个公网端口。
然后想想看,Emby被代理出来的端口还是http协议的,在这个全明ssl加密的时代,http显得格格不入啊。本来想用 Let’s Encrypt 的免费ssl证书的,但是就三个月,虽然有那种自动续签的脚本。但是我觉得自己用没必要这么奢侈。自己签一个10年的管饱的不就好了吗!
通用证书签名
生成CA私钥
1
2
|
openssl genrsa -des3 -out ca.key 4096 #需要密码,后面签名的时候会用到
openssl genrsa -out ca.key 4096 #不想需要密码
|
过程:
1
2
3
4
|
Generating RSA private key, 4096 bit long modulus (2 primes)
......++++
..............................................................++++
e is 65537 (0x010001)
|
我是以不要密码的 做个示范,后面过程都是不要密码的。
签发证书(公钥)
1
|
openssl req -new -x509 -days 3651 -key ca.key -out ca.crt
|
过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:JokemeBlog
Email Address []:
|
这里的信息自己随便填!注意 Organization Name 下面ssl私钥要填和这里一样的就可以了。
生成ssl私钥
1
|
openssl genrsa -out ssl.key 2048
|
过程:
1
2
3
4
|
Generating RSA private key, 2048 bit long modulus (2 primes)
.......................................+++++
........+++++
e is 65537 (0x010001)
|
生成签名请求
1
|
openssl req -new -key ssl.key -out ssl.csr
|
过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:*.jokeme.top
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
|
签发证书
1
2
|
echo "1234" > ca.srl
openssl x509 -req -in ssl.csr -CA ca.crt -CAkey ca.key -out ssl.crt -days 3650 -sha256
|
过程:
1
2
3
|
Signature ok
subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = *.jokeme.top
Getting CA Private Key
|
查看证书信息
1
|
openssl x509 -in ssl.crt -text
|
过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 1122869 (0x112235)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = JokemeBlog
Validity
Not Before: Oct 12 18:29:41 2022 GMT
Not After : Oct 9 18:29:41 2032 GMT
Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = *.jokeme.top
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:b8:b4:8a:ba:16:da:eb:ac:f7:64:f5:13:77:ed:
xxx xxx
6d:97:3e:cf:59:58:3d:19:0e:9a:d0:bd:91:eb:1a:
85:65
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
7d:9c:07:4e:d2:46:1f:ad:e2:70:f6:63:72:89:9b:83:57:af:
xxx xxx
66:23:09:a5:cc:67:4f:b9
-----BEGIN CERTIFICATE-----
MIIELDCCAhQCAxEiNTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJBVTETMBEG
xxx xxx
880sCRdh9cZfwv0F0/zSnZfgk3IWHjVVcCFArvAbrf3V9SBItlK+LQy/YOWWRgpI
fx/Y9eDvFcNmIwmlzGdPuQ==
-----END CERTIFICATE-----
|
这样通用证书就签发好了,用的时候只需要把 ca.crt
导出,安装在系统的 ⌈信任根证书签发机构⌋ 里面就可以正常使用了。
还有一点需要说明!就是这个证书是通用的安全加密证书,但是它不是被浏览器所信任的 SAN证书
,那什么是SAN证书呢?
SAN(Subject Alternate Names),证书备用名称。其实就是给浏览器加上下面的信息
1
2
3
|
DNS.1 = baidu.com
DNS.2 = baifubao.com
DNS.3 = www.baidu.cn
|
来达到佐证签名本身应该工作在什么网站上的目的。
如果直接使用自签的证书,现代浏览器还是不会信任这种缺失SAN的证书滴。因为一旦没有通过所谓的SAN检查,来证明证书本身应该工作在当前网站,那该网站就有可能是被入侵的,任何人访问都有可能造成风险。
那么如何才能让浏览器信任我们的自签证书呢?很明显,只需要自签的证书里包含 SAN 信息就可以了。
SAN证书签发
这里我参考照抄了 stackoverflow 上的答案,自己稍微的整理了一下,写了个一键生成的脚本。StackOverflow上那个老哥建议加密,那我也就把密码给加上了。
话不多说,直接上脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
cat << EOF > ca.cnf
HOME = .
RANDFILE = \$ENV::HOME/.rnd
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
[ CA_default ]
default_days = 3651 # How long to certify for
default_crl_days = 3650 # How long before next CRL
default_md = sha256 # Use public key default MD
preserve = no # Keep passed DN ordering
x509_extensions = ca_extensions # The extensions to add to the cert
email_in_dn = no # Don't concat the email in the DN
copy_extensions = copy # Required to copy SANs from CSR to cert
####################################################################
[ req ]
default_bits = 4096
default_keyfile = ca.key
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
string_mask = utf8only
####################################################################
[ ca_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = OGS
localityName = Locality Name (eg, city)
localityName_default = OGS
organizationName = Organization Name (eg, company)
organizationName_default = JokemeBlog Team
organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = dev
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = JokemeBlog
emailAddress = Email Address
emailAddress_default = [email protected]
####################################################################
[ ca_extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true
keyUsage = keyCertSign, cRLSign
EOF
openssl req -x509 -config ca.cnf -newkey rsa:4096 -days 3651 -sha256 -out ca.pem -outform PEM
cat << EOF > ssl.cnf
HOME = .
RANDFILE = \$ENV::HOME/.rnd
####################################################################
[ req ]
default_bits = 2048
default_keyfile = ssl.key
distinguished_name = server_distinguished_name
req_extensions = server_req_extensions
string_mask = utf8only
####################################################################
[ server_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = OGS
localityName = Locality Name (eg, city)
localityName_default = OGS
organizationName = Organization Name (eg, company)
organizationName_default = JokemeBlog Team
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = *.jokeme.top
emailAddress = Email Address
emailAddress_default = [email protected]
####################################################################
[ server_req_extensions ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated Certificate"
####################################################################
[ alternate_names ]
DNS.1 = jokeme.top
DNS.2 = *.jokeme.top
DNS.3 = *.*.jokeme.top
EOF
openssl req -config ssl.cnf -newkey rsa:2048 -sha256 -days 3650 -out ssl.csr
cat << EOF > ca.cnf
HOME = .
RANDFILE = \$ENV::HOME/.rnd
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
[ CA_default ]
base_dir = .
certificate = \$base_dir/ca.pem # The CA certifcate
private_key = \$base_dir/ca.key # The CA private key
new_certs_dir = \$base_dir # Location for new certs after signing
database = \$base_dir/index.txt # Database index file
serial = \$base_dir/serial.txt # The current serial number
unique_subject = no # Set to 'no' to allow creation of
# several certificates with same subject.
default_days = 3651 # How long to certify for
default_crl_days = 3650 # How long before next CRL
default_md = sha256 # Use public key default MD
preserve = no # Keep passed DN ordering
x509_extensions = ca_extensions # The extensions to add to the cert
email_in_dn = no # Don't concat the email in the DN
copy_extensions = copy # Required to copy SANs from CSR to cert
####################################################################
[ req ]
default_bits = 4096
default_keyfile = ca.key
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
string_mask = utf8only
####################################################################
[ ca_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = OGS
localityName = Locality Name (eg, city)
localityName_default = OGS
organizationName = Organization Name (eg, company)
organizationName_default = JokemeBlog Team
organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = dev
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = JokemeBlog
emailAddress = Email Address
emailAddress_default = [email protected]
####################################################################
[ ca_extensions ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true
keyUsage = keyCertSign, cRLSign
####################################################################
[ signing_policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ signing_req ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
EOF
touch index.txt && echo '01' > serial.txt
openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out ssl.pem -infiles ssl.csr
|
过程还是那个过程,只不过中间加SAN的时候就不能用系统默认的配置了,会抹掉SAN信息,所以就使用自己的配置文件,让签名的时候继承这些SAN扩展信息。
脚本执行完了,大概会有以下信息,其中 ss.sh 是上面脚本的内容,ssl.passwd是给nginx用的,因为ssl加密的时候输入了密码,nginx如果不知道密码也就不能处理https的证书了。
1
2
|
01.pem ca.key index.txt index.txt.old serial.txt.old ssl.cnf ssl.key ssl.pem
ca.cnf ca.pem index.txt.attr serial.txt ss.sh ssl.csr ssl.passwd
|
oh!nginx添加有密码的ssl证书需要添加上下面这句配置:
1
|
ssl_password_file /home/xx/cert/ssl.passwd;
|
这样可以被浏览器承认的ssl证书就签发好了,把ca.pem导出安装到你的系统中就可以了。以后所有的用该ca签名的证书都会被信任。并且有效期十年。根本不折腾,我就不相信十年以后你还在用现在的电脑💻/手机📱
什么?你说你不会在Windows上安装pem格式的CA证书?
直接改后缀名为 .crt
就可以了。因为Linux上默认使用PEM编码的,这个pem和crt都是PEM编码的,内容没有区别。