Featured image of post 使用自签名Ca证书签名ssl

使用自签名Ca证书签名ssl

因为搭建Emby服务器想上https,又不想使用Let's Encrypt那些需要续签的脚本,只能自己生成CA,自己给自己签一个10年的ssl证书了

使用自签名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编码的,内容没有区别。