概念

  1. 首先要有一个CA根证书,然后用CA根证书来签发用户证书
  2. 用户进行证书申请:一般先生成一个私钥,然后用私钥生成证书请求(证书请求里应包含有公钥信息),再利用证书服务器的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

  1. 问题描述:
    The mandatory stateOrProvinceName field was missing
    解决方法:
# 在生成csr的时候,需要把信息填写完,不能填空,不然后面生成的crt文件会为空,导致pem出错
# 修改后的配置文件如下:
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
  1. 问题描述:
    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连接。