如何使用OpenSSL生成自签名SSL证书

如何使用OpenSSL生成自签名SSL证书

技术背景

在网络通信中,SSL(Secure Sockets Layer)证书用于加密数据传输,确保通信的安全性和完整性。自签名SSL证书是由用户自己创建和签名的证书,不依赖于第三方证书颁发机构(CA)。自签名证书常用于测试环境、内部网络或个人项目,因为它们不需要付费,但在公共互联网上,自签名证书通常不被浏览器等客户端信任。OpenSSL是一个强大的开源工具包,提供了生成自签名SSL证书的功能。

实现步骤

单命令生成自签名证书

可以使用以下命令生成自签名证书:

1
2
3
4
5
# 交互式方式
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

# 非交互式方式,有效期10年
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
  • -nodes 选项表示不使用密码保护私钥。
  • days 参数指定证书的有效期。

包含Subject Alternate Name(SAN)的证书生成

自OpenSSL ≥ 1.1.1版本起,可以使用以下命令生成包含SAN的证书:

1
2
3
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 \
-nodes -keyout example.com.key -out example.com.crt -subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:*.example.com,IP:10.0.0.1"

对于旧版本的OpenSSL(≤ 1.1.0),如Debian ≤ 9或CentOS ≤ 7,需要使用更长的命令:

1
2
3
4
5
6
7
8
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 \
-nodes -keyout example.com.key -out example.com.crt -extensions san -config \
<(echo "[req]";
echo distinguished_name=req;
echo "[san]";
echo subjectAltName=DNS:example.com,DNS:*.example.com,IP:10.0.0.1
) \
-subj "/CN=example.com"

通过配置文件生成证书

创建一个配置文件 example-com.conf

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
[ req ]
default_bits = 2048
default_keyfile = server-key.pem
distinguished_name = subject
req_extensions = req_ext
x509_extensions = x509_ext
string_mask = utf8only

[ subject ]
countryName = Country Name (2 letter code)
countryName_default = US

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = NY

localityName = Locality Name (eg, city)
localityName_default = New York

organizationName = Organization Name (eg, company)
organizationName_default = Example, LLC

commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Example Company

emailAddress = Email Address
emailAddress_default = [email protected]

[ x509_ext ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated Certificate"

[ req_ext ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated Certificate"

[ alternate_names ]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = mail.example.com
DNS.4 = ftp.example.com

使用配置文件生成自签名证书:

1
2
openssl req -config example-com.conf -new -x509 -sha256 -newkey rsa:2048 -nodes \
-keyout example-com.key.pem -days 365 -out example-com.cert.pem

生成签名请求:

1
2
openssl req -config example-com.conf -new -sha256 -newkey rsa:2048 -nodes \
-keyout example-com.key.pem -days 365 -out example-com.req.pem

核心代码

Python脚本生成自签名证书

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
from OpenSSL import crypto, SSL
from secrets import randbelow

def cert_gen(
emailAddress=input("Enter Email Address: "),
commonName=input("Enter Common Name: "),
countryName=input("Enter Country Name (2 characters): "),
localityName=input("Enter Locality Name: "),
stateOrProvinceName=input("Enter State of Province Name: "),
organizationName=input("Enter Organization Name: "),
organizationUnitName=input("Enter Organization Unit Name: "),
serialNumber=randbelow(1000000),
validityStartInSeconds=0,
validityEndInSeconds=10*365*24*60*60,
KEY_FILE = "private.key",
CERT_FILE="selfsigned.crt"
):
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 4096)

cert = crypto.X509()
cert.get_subject().C = countryName
cert.get_subject().ST = stateOrProvinceName
cert.get_subject().L = localityName
cert.get_subject().O = organizationName
cert.get_subject().OU = organizationUnitName
cert.get_subject().CN = commonName
cert.get_subject().emailAddress = emailAddress
cert.set_serial_number(serialNumber)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(validityEndInSeconds)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, 'sha512')

with open(CERT_FILE, "wt") as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8"))
with open(KEY_FILE, "wt") as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8"))
print("GENERATED")
input("Press enter to close program.")

cert_gen()

Bash脚本生成自签名证书

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
#!/bin/bash

subj='//SKIP=skip/C=IN/ST=Country/L=City/O=MyCompany/OU=Technology'
red='\033[31m' # Red
yellow='\033[33m' # Yellow
green='\033[32m' # Green
blue='\033[34m' # Blue
purple='\033[35m' # Purple
cyan='\033[36m' # Cyan
white='\033[37m' # White

gencerts(){
certname=$1
pkname=$2
alias=$3
$(openssl genrsa -out $pkname'pem.pem' 4096)
$(openssl req -new -sha256 -key $pkname'pem.pem' -out $certname'csr.csr' -subj $subj)
$(openssl x509 -req -sha256 -days 3650 -in $certname'csr.csr' -signkey $pkname'pem.pem' -out $certname'.crt')
$(openssl pkcs12 -export -out $pkname'.p12' -name $alias -inkey $pkname'pem.pem' -in $certname'.crt')
}

verify(){
pkname=$1
keytool -v -list -storetype pkcs12 -keystore $pkname'.p12'
}

echo -e "${purple}WELCOME TO KEY PAIR GENERATOR"
echo -e "${yellow} Please enter the name of the certificate required: "
read certname
echo -e "${green}Please enter the name of the Private Key p12 file required: "
read pkname
echo -e "${cyan}Please enter the ALIAS of the Private Key p12 file : "
read pkalias
echo -e "${white}Please wait while we generate your Key Pair"

gencerts $certname $pkname $pkalias
echo -e "${white}Now lets verify the private key :)"

tput bel # Play a bell

verify $pkname

最佳实践

  • 使用强加密算法:建议使用至少2048位的RSA密钥和SHA-256哈希算法,以提高证书的安全性。
  • 包含Subject Alternate Name(SAN):现代浏览器要求证书包含SAN,以支持多个域名或IP地址。
  • 设置合理的有效期:根据实际需求设置证书的有效期,避免过长或过短。
  • 成为自己的证书颁发机构(CA):如果需要在多个设备或客户端上信任自签名证书,可以创建自己的CA,并使用CA签名服务器证书。

常见问题

浏览器提示证书不受信任

这是因为自签名证书没有经过第三方CA的验证,浏览器无法将其与已知的信任锚链接起来。解决方法有:

  • 导入证书:手动将自签名证书导入到浏览器的信任存储中。
  • 成为自己的CA:创建自己的CA,并使用CA签名服务器证书,然后将CA证书导入到客户端的信任存储中。

证书验证失败

可能是由于证书配置不正确或缺少必要的扩展字段(如SAN)导致的。确保在生成证书时包含所有必要的信息,并遵循相关的标准和规范。

MySQL SSL连接问题

  • 无法获取证书:可能是由于MySQL没有读取证书文件的权限,可以将证书文件保存到AppArmor或SELinux允许访问的目录中。
  • 无法获取私钥:可能是由于MySQL版本不支持默认的rsa:2048格式,可以使用openssl rsa命令将私钥转换为普通的RSA格式。

如何使用OpenSSL生成自签名SSL证书
https://119291.xyz/posts/how-to-generate-self-signed-ssl-certificate-using-openssl/
作者
ww
发布于
2025年5月22日
许可协议