如何使用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.pemdistinguished_name = subjectreq_extensions = req_extx509_extensions = x509_extstring_mask = utf8only[ subject ] countryName = Country Name (2 letter code)countryName_default = USstateOrProvinceName = State or Province Name (full name)stateOrProvinceName_default = NYlocalityName = Locality Name (eg, city)localityName_default = New YorkorganizationName = Organization Name (eg, company)organizationName_default = Example, LLCcommonName = Common Name (e.g. server FQDN or YOUR name)commonName_default = Example CompanyemailAddress = Email AddressemailAddress_default = [email protected] [ x509_ext ] subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerbasicConstraints = CA:FALSE keyUsage = digitalSignature, keyEnciphermentsubjectAltName = @alternate_namesnsComment = "OpenSSL Generated Certificate" [ req_ext ] subjectKeyIdentifier = hashbasicConstraints = CA:FALSE keyUsage = digitalSignature, keyEnciphermentsubjectAltName = @alternate_namesnsComment = "OpenSSL Generated Certificate" [ alternate_names ] DNS.1 = example.comDNS.2 = www.example.comDNS.3 = mail.example.comDNS.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, SSLfrom secrets import randbelowdef 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' yellow='\033[33m' green='\033[32m' blue='\033[34m' purple='\033[35m' cyan='\033[36m' white='\033[37m' 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 certnameecho -e "${green} Please enter the name of the Private Key p12 file required: " read pknameecho -e "${cyan} Please enter the ALIAS of the Private Key p12 file : " read pkaliasecho -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 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格式。