在开发过程中,我们通常需要为本地Web服务器配置https,这就涉及到如何让localhost支持https。那么这个问题最终转化为如何获取localhost的ssl证书。
解决方案:
我们可以利用Openssl生成自签名证书(CA),然后利用自签名证书对localhost颁发ssl证书。
在这里,我们使用openssl的x509命令来生成自签名证书,和颁发localhost证书。
x509可以像openssl ca一样对证书或请求执行签名动作。注意,openssl x509 不读取openssl配置文件,所有的一切配置都由x509自行提供,所以openssl x509像是一个"mini CA"
步骤1: 创建相应的工作目录
// 在用户目录下,创建cert目录以及CA,localhost两个字目录
// 根据语意,我们将生成的自签名证书CA放入CA目录下,localhost相关的证书放入localhost目录下
cd $HOME
mkdir cert
cd cert
mkdir CA
mkdir localhost
步骤2: 生成自签名证书(CA)
cd CA
// 使用openssl genrsa生成CA的私钥对
openssl genrsa -out ca.key 2048
// 使用openssl req来生成CA的请求文件
openssl req -new -key ca.key -out ca.csr
// 使用x509来自签署。
// 自签署时,使用"-req"选项明确表示输入文件为证书请求文件,再使用"-signkey"提供自签署时使用的私钥
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
步骤3: 为localhost颁发证书
cd $HOME/cert
cd localhost
touch localhost.ext
在为localhost颁发证书前,我们需要创建一个ext文件,文件的内容如下:
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
ext文件添加localhost和127.0.0.1作为subjectAltName,这一步至关重要。localhost和127.0.0.1必须作为subjectAltName添加到证书中,否则最终签发的证书在浏览器中使用时,会出现CommonName不一致而导致证书不被信任。(个人理解:这是因为我们的本机真正的名字并不叫localhost,并且真实的IP也并不是127.0.0.1。有的说法是浏览器不在信任自签名CA的CommonName。)
// 生成localhost的私钥对
openssl genrsa -out localhost.key 2048
// 生成localhost的证书请求
openssl req -new -key localhost.key -out localhost.csr
// 使用openssl x509签署localhost证书请求,即为localhost颁发证书。
// 注意,openssl x509 不读取openssl配置文件,所有的一切配置都由x509自行提供。
// 因此需要提供
// -CA选项提供CA证书
// -CAkey选项提供CA的私钥
// -extfile声明subjectAltName
// 为他人颁发证书时,必须确保serial文件存在,建议使用自动创建的选项"-CAcreateserial"。
openssl x509 -req -in localhost.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -extfile localhost.ext -out localhost.crt
结果
我们在CA目录下的到了CA证书(ca.crt)和CA私钥(ca.key),通常情况下,CA证书,作为根证书,可以导入到系统的钥匙串中,然后修改其为信任。
localhost目录下我们的到了服务器证书(localhost.crt)和服务器私钥(localhost.key),这两个可以配置在nginx服务器中,用来支持https的访问。
更为便捷的方案
minica
minica封装了openssl的一些列操作,可以让您一个命令行,直接获得自签名的CA证书和服务器证书。相关的操作可以直接参考上述网站的说明。
扩展内容
如果您对证书,签名,ssl,加密,https等内容不是十分了解,那么,下面这些内容可以让您快速的进行了解。
术语
对称加密:例如平时给文件加密,加密和解密用的同一密码,即对称加密。
非对称加密:加密时用一个密码,而解锁时需要用另一个密码,即非对称加密。目前很流行的非对称加密算法是RSA算法,它是基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
公钥:公钥是公开的,只有通过私钥才能解密。
私钥:自己保留,只有通过私钥才能解密公钥加密的数据,对于私钥的使用可以设置密码,设置访问权限。
数字证书:数字证书是一种权威性的电子文档,一般由权威公正的第三方机构,即CA (Certificate Authority)中心签发的证书(也可以由企业级CA系统进行签发)。最简单的证书包含一个公开密钥、名称、有效期以及证书授权中心的数字签名。
一般数字证书分有三类,根证书、服务器证书和客户端证书。
根证书,是生成服务器证书和客户端证书的基础,是信任的源头,也可以叫自签发证书,即CA证书。
服务器证书,由根证书签发,配置在服务器上的证书。
客户端证书,由根证书签发,配置在客户端上的证书。
单向认证的通信过程
总结下上面的流程图,这个最常见的单向认证的通信过程分为如下几个步骤:
客户端向服务器发送请求
服务器将包含公钥的证书通过明文发送给客户端
客户端通过根证书验证服务器证书是否有效
如果有效,客户端生成一个随机密钥,也叫对称加密密钥,使用服务器的公钥进行加密传输
服务器通过私钥解密客户端传输的对称加密密钥
这时只有服务器和客户端知道这个对称加密密钥,双方可以进行对称加密传输
如何验证服务器证书是否有效
我们在术语中提到过,数字证书必须包含证书授权中心的数字签名。我们通过数字签名来验证证书是否有效。
数字证书签名流程:
首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
最后将 Certificate Signature 添加在文件证书上,形成数字证书;
证书验证流程:
首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。
使用openssl ca命令进行证书签名
如果我们摒弃x509命令,而是使用ca命令来为localhost颁布证书,我们又如何处理?ca指令需要用到ssl的配置文件,因此,我们首先需要进行环境的配置
配置ssl环境
首先查看下openssl的版本
openssl version -a
因为MacOS自带openssl,所以你会看到如下的结果:
这边我们需要关注的是OPENSSLDIR: "/Users/xuefengchen/opt/miniconda3/ssl这个目录,openssl的配置文件在这个目录下。
编辑openssl的配置文件
nano /Users/xuefengchen/opt/miniconda3/ssl/openssl.cnf
[ CA_default ]
dir = /Users/xuefengchen/ssl # 指定我们生成ssl证书的目录
certs = $dir/certs # 生成的证书放这个目录里
crl_dir = $dir/crl # 证书吊销列表放这个目录里
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several certs with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/ca.crt # 声明CA证书在哪里
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/ca.key # 声明CA的私钥在哪里
x509_extensions = usr_cert # The extensions to add to the cert
可以看到我们修改了默认的工作文件夹、CA私钥名、CA证书名。不过目前所有的目录和内容都不存在。所以首先我们需要创建这些目录和文件
cd #HOME
mkdir ssl // 创建我们的工作目录 /Users/xuefengchen/ssl
cd ssl
mkdir -pv {certs,csr,crl,newcerts,private} // 生成certs,csr,crl,newcerts,private目录
touch {serial,index.txt,index.txt.attr} // 生成serial,index.txt,index.txt.attr文件
echo 01 >> serial // 指明证书开始的编号,重要,不标记编号,颁发证书时会出错
经过上述的操作,ssl下的目录,文件和配置基本完成。
生成自签名证书(CA证书)
接下来我们来生成CA私钥和CA证书(根证书,自签发证书),即ca.crt 和private/ca.key
第一步:生成CA私钥private/ca.key
openssl genrsa -out private/ca.key 2048
基于RSA算法,生成密钥长度为2048位的秘钥对,这是我们通常说的私钥,但事实上是包含了私钥公钥的密钥对
第二步:生成CA证书ca.crt,此时我们已经有ca.key私钥了,可以通过下面的命令来生成一个自签名证书。
openssl req -new -x509 -key private/ca.key -out ca.crt
选项 -x509 告诉 req 子命令创建一个自签证书, 选项 -new 启用 CSR 信息提问。
回答 CSR 信息提问,完成该过程。
自此,我们完成了ca.crt 和private/ca.key。接下来我们就可以用CA私钥和证书来签发服务器证书了。下面我们就来模拟使用CA私钥和证书为localhost签发服务器证书。
使用自签名证书为localhost颁发ssl证书
第一步:生成localhost的私钥
openssl genrsa -out private/localhost.key 2048
第二步:生成localhost的证书申请
openssl req -new -key private/localhost.key -out csr/localhost.csr
注意,Common Name以上的内容必须和CA证书一致,Common Name为您服务器的域名,在这里就是localhost。
第三步:颁发证书
touch localhost.ext
在为localhost颁发证书前,我们需要创建一个ext文件,文件的内容如下:
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
ext文件添加localhost和127.0.0.1作为subjectAltName,这一步至关重要。localhost和127.0.0.1必须作为subjectAltName添加到证书中,否则最终签发的证书在浏览器中使用时,会出现CommonName不一致而导致证书不被信任。(个人理解:这是因为我们的本机真正的名字并不叫localhost,并且真实的IP也并不是127.0.0.1。有的说法是浏览器不在信任自签名CA的CommonName。)
openssl ca -in csr/localhost.csr -out certs/localhost.crt -extfile localhost.ext
您可以看到颁发的结果如下:
这里需要注意,颁发证书需要用到ca.crt 和private/ca.key,命令行中没有指定是因为我们的配置文件指明的这两个文件的额位置,这也是我们为什么需要在开始时对ssl的配置文件进行修改配置。
至此,我们生成了localhost.key(私钥)和localhost.crt(服务器证书),那这两个文件去Web服务器进行配置,即可实现localhost的https支持。
参考资料:
How to Get SSL HTTPS for Localhost
openssl x509(签署和自签署)
openssl详解与应用教程
使用Openssl生成自签证书