概念
- 首先要有一个CA根证书,然后用CA根证书来签发用户证书
- 用户进行证书申请:一般先生成一个私钥,然后用私钥生成证书请求(证书请求里应包含有公钥信息),再利用证书服务器的CA根证书来签发证书
文件
- key:私有的密钥
- pem:用于导出,导入证书时候的证书的格式,有证书开头,结尾的格式。
- csr:证书签名请求(证书请求文件),含有公钥信息,certificate signing request的缩写
- crt:证书文件,certificte的缩写
- crl:证书吊销列表,Certificate Revocation List的缩写
证书
根证书
根证书生成步骤:
生成私钥(key)–> 生成证书请求(csr) --> 用CA根证书签名得到证书(crt)
# 生成ca私有密钥
openssl genrsa -out ca.key 2048
# 生成csr文件
openssl req -new -key ca.key -out ca.csr
# 生成ca根证书
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
服务端
服务端证书生成步骤:
生成私钥(key) --> 生成证书请求(csr) --> 用CA根证书签名来得到证书(crt)
# 生成私钥
openssl genrsa -des3 -out server.key 1024
# 生成csr文件
openssl req -new -key server.key -out server.csr
# 生成crt
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key
# 去除server.key密码的口令
openssl rsa -in server.key -out server.key
# 生成pem文件
cat server.crt server.key > server.pem
客户端
# 生成私钥
openssl genrsa -des3 -out client.key 1024
# 生成csr文件
openssl req -new -key client.key -out client.csr
# 生成crt文件
openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key
# 去除client,key的密码口令
openssl rsa -in client.key -out client.key
# 生成pem文件
cat client.crt client.key > client.pem
Notice Tip
- 问题描述:
The mandatory stateOrProvinceName field was missing
解决方法:
# 在生成csr的时候,需要把信息填写完,不能填空,不然后面生成的crt文件会为空,导致pem出错
# 修改后的配置文件如下:
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
- 问题描述:
Using configuration from /etc/pki/tls/openssl.cnf
/etc/pki/CA/index.txt: No such file or directory
unable to open ‘/etc/pki/CA/index.txt’
解决方法:
touch /etc/pki/CA/index.txt
touch /etc/pki/CA/serial
echo "01" > /etc/pki/CA/serial
实现
服务端
function generateSslSocket() {
# 创建socket流
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', 'server.pem'); # 服务端证书
stream_context_set_option($context, 'ssl', 'cafile', 'ca.crt'); # ca根证书
stream_context_set_option($context, 'ssl', 'allow_self_signed', true); # 是否允许自签名证书,需要配合peer使用
stream_context_set_option($context, 'ssl', 'verify_peer', true); # 是否需要验证 SSL证书
stream_context_set_option($context, 'ssl', 'verify_peer_name', true); # 是否需要验证 peer name
# socket监听端口
$socket = stream_socket_server('ssl://127.0.0.1:8801', $errNo, $errStr,
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,$context);
if ($socket === False) {
exit();
}
return $socket;
}
客户端
function sendDataToSslSocket() {
$errStr = "";
$retRes = "";
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', 'client.pem');
stream_context_set_option($context, 'ssl', 'cafile', 'ca.crt');
# 这里客户端没有验证自签名,因为在创建证书的时候,我这边主动把签名删除了
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
stream_context_set_option($context, 'ssl', 'verify_peer_name', false);
$fp = stream_socket_client('ssl://127.0.0.1:8801', $errno, $errStr, 20, STREAM_CLIENT_CONNECT, $context);
# 发送数据
fwrite($this->fp, json_encode($data));
$retRes = fread($this->fp, 2048);
# 关闭连接
fclose($this->fp);
return $retRes;
}
总结
我最终的目标是完成一个SSL认证的代理,该代理可以**[接收请求]、[处理请求]、[多进程自动处理]、[任务进程监控]**。
准备使用的技术包括 SSL、Libevent、ftok、thread、pipe、semphore,目前只是完成了第一步,生成SSL证书,客户端和服务端socket连接。