PHP通过OpenSSL生成证书、密钥并且加密解密数据,以及公钥,私钥和数字签名的理解

一、公钥加密

二、私钥签名

公钥和私钥是成对的,它们互相解密。

公钥加密,私钥解密。

私钥数字签名,公钥验证


三、准备工作


找到php.ini 去掉openssl.dll前面的’;’。找到openssl.cnf的位置,我的在D:\xampp\apache\conf。
配置apache以支持SSL:打开apache的配置文件conf/httpd.conf
LoadModule ssl_module modules/mod_ssl.so、Include conf/extra/httpd-ssl.conf

四、示例代码

$config = array(
    "private_key_bits" => 1024,                     //字节数    512 1024  2048   4096 等
    "private_key_type" => OPENSSL_KEYTYPE_RSA,     //加密类型
    "config" => "D:/phpStudy/PHPTutorial/Apache/conf/openssl.cnf" //找到apache中openssl.cnf的目录
);

$privkeypass = '123456789'; //私钥密码
$numberofdays = 365;     //有效时长 天
$cerpath = "./test.cer"; //生成证书路径
$pfxpath = "./test.pfx"; //密钥文件路径

$dn = array(    
    "countryName" => "CN",                                  //所在国家
    "stateOrProvinceName" => "shanghia",                    //所在省份
    "localityName" => "Glastonbury",                        //所在城市
    "organizationName" => "name",         //注册人姓名
    "organizationalUnitName" => "llll",   //组织名称
    "commonName" => "lllll",                          //公共名称
    "emailAddress" => "[email protected]"                     //邮箱
);

// 生成公钥私钥资源
$res = openssl_pkey_new($config);

// 导出私钥 $priKey
openssl_pkey_export($res, $priKey,null,$config);

//  导出公钥 $pubKey
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
//print_r($priKey); 私钥
//print_r($pubKey); 公钥

//直接测试私钥 公钥
echo '-------------------公私钥加解密-START---------------------','
'; $data = '测试公私钥加解密成功!'; // 公钥加密 openssl_public_encrypt($data, $encrypted, $pubKey); // 私钥解密 openssl_private_decrypt($encrypted, $decrypted, $priKey); echo '公钥加密:',base64_encode($encrypted),'私钥解密:','
',$decrypted,'
'; echo '-------------------公私钥加解密-END---------------------','
'; //生成文件 $csr = openssl_csr_new($dn, $priKey,$config); //基于$dn生成新的 CSR (证书签名请求) $sscert = openssl_csr_sign($csr, null, $priKey, $numberofdays,$config);//根据配置自己对证书进行签名 openssl_x509_export($sscert, $csrkey); //将公钥证书存储到一个变量 $csrkey,由 PEM 编码格式命名。 openssl_pkcs12_export($sscert, $privatekey, $priKey, $privkeypass); //将私钥存储到名为的出 PKCS12 文件格式的字符串。 导出密钥$privatekey //生成证书文件 $fp = fopen($cerpath, "w"); fwrite($fp, $csrkey); fclose($fp); //生成密钥文件 $fp = fopen($pfxpath, "w"); fwrite($fp, $privatekey); fclose($fp); echo '
','
','
','
'; echo '----------------------自签名验证-START----------------------','
'; // 测试私钥 秘钥 $privkeypass = '123456789'; //私钥密码 $pfxpath = "./test.pfx"; //密钥文件路径 $priv_key = file_get_contents($pfxpath); //获取密钥文件内容 $data = "测试数据!"; //加密数据测试test //私钥加密 openssl_pkcs12_read($priv_key, $certs, $privkeypass); //读取公钥、私钥 $prikeyid = $certs['pkey']; //私钥 openssl_sign($data, $signMsg, $prikeyid,OPENSSL_ALGO_SHA1); //注册生成加密信息 $signMsg = base64_encode($signMsg); //base64转码加密信息 //公钥解密 $unsignMsg=base64_decode($signMsg);//base64解码加密信息 openssl_pkcs12_read($priv_key, $certs, $privkeypass); //读取公钥、私钥 $pubkeyid = $certs['cert']; //公钥 $res = openssl_verify($data, $unsignMsg, $pubkeyid); //验证 echo $res?'证书测试成功!':'证书测试失败!';echo '
'; //输出验证结果,1:验证成功,0:验证失败 echo '-----------------------签名验证-END------------------------','
'; openssl_sign 默认signature_alg参数是OPENSSL_ALGO_SHA1

如果使用DSA加密方式需要使用OPENSSL_ALGO_DSS1参数

signature_alg 其他参数

OPENSSL_ALGO_DSS1 (integer)

OPENSSL_ALGO_SHA1 (integer)

OPENSSL_ALGO_SHA224 (integer)

OPENSSL_ALGO_SHA256 (integer)

OPENSSL_ALGO_SHA384 (integer)

OPENSSL_ALGO_SHA512 (integer)

OPENSSL_ALGO_RMD160 (integer)

OPENSSL_ALGO_MD5 (integer)

OPENSSL_ALGO_MD4 (integer)

OPENSSL_ALGO_MD2 (integer)

五、特别说明:

x509,公钥证书,只有公钥。
p7,签名或加密。可以往里面塞x509,同时没有签名或加密内容。
p12,含有私钥,同时可以有公钥,有口令保护。
p7的作用就是电子信封。
X509是基本规范
P7和P12是两个实现规范,P7是数字信封,P12是带有私钥的证书规范。
x509是数字证书的规范,P7和P12是两种封装形式。比如说同样的电影,有的是avi格式,有的是mpg,大概就这个意思。

P7一般是把证书分成两个文件,一个公钥一个私钥,有PEM和DER两种编码方式。PEM比较多见,就是纯文本的,P7一般是分发公钥用,看到的就是一串可见字符串,扩展名经常是.crt,.cer,.key等。DER是二进制编码。
P12是把证书压成一个文件,.pfx 。主要是考虑分发证书,私钥是要绝对保密的,不能随便以文本方式散播。所以P7格式不适合分发。.pfx中可以加密码保护,所以相对安全些。
在实践中要中,用户证书都是放在USB Key中分发,服务器证书经常还是以文件方式分发。服务器证书和用户证书,都是X509证书,就是里面的属性有区别。

X509 是证书规范
PKCS#7 是消息语法 (常用于数字签名与加密)
PKCS#12 个人消息交换与打包语法 (如.PFX .P12)打包成带公钥与私钥

六、过程出现的问题集锦

最近利用php开发ssl的相关功能,由于第一次做相关的事情,遇到了很多问题,庆幸的是最终都顺利解决了。不过相关的资料很少,都是综合了国内外的相关信息才解决的。现在整理一下,方便后来者遇到问题时解决。

  1. 首先扫下盲,什么是数字签名
    推荐一个通俗易懂的教程:中文版 英文版
    其实整个过程就是:
    数据->hash-》摘要-》私钥对摘要加密-》数字签名
    | |
    hash 公钥解密
    | |
    摘要-------------是否相等--------------摘要
  2. Fatal error: Call to undefined function openssl_pkey_get_public() in 。。。
    这个问题主要是因为php.ini中,extension=php_openssl.dll的选项没有打开,把分号去掉,重启apache即可。
  3. 用php的openssl_verify函数无法验证由其他函数库生成的签名
    比如用c++的函数库生成了一个签名,将其发送给服务器端验签,用php的openssl_verify函数,总是无法通过,并且有以下错误提示:
    error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long
    error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header
    error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error
    主要原因是openssl_verify函数是对上面讲的验签过程的高度封装,包括公钥解密,数据校验,比较摘要等过程。这个函数的特别之处是,它需要这个签名必须包含hash头,而有些函数生成的签名是不包含这个hash头的,所以总是有参数错误。
    解决的办法就是自己实现这个verify函数。简单的过程如下:
    s h a 1 = s h a 1 ( sha1 = sha1( sha1=sha1(data, true);
    s i g n = b a s e 6 4 d e c o d e ( sign = base64_decode( sign=base64decode(sign);
    $deco = ‘’;
    p u b k i d = o p e n s s l p k e y g e t p u b l i c ( pubkid = openssl_pkey_get_public( pubkid=opensslpkeygetpublic(cert);
    openssl_public_decrypt($sign, $deco, p u b k i d ) ; 最 后 比 较 pubkid); 最后比较 pubkid);sha1和$deco是否相等。
    4.Warning: openssl_verify(): Don’t know how to get public key from this private key in。。。
    Warning: openssl_verify(): supplied key param cannot be coerced into a public key in。。。
    Warning: openssl_public_decrypt(): Don’t know how to get public key from this private key in。。。
    Warning: openssl_public_decrypt(): key parameter is not a valid public key in 。。。
    明明各种配置都正确,为什么会有这样的警告?而且函数无法得到正确结果。
    这个原因是apache和php的openssl版本不一致。apache和php的openssl功能都依赖于libeay32.dll和 ssleay32.dll这两个库,但是这两个文件并非被apache和php共用,而是各用各的。比如apache中这两个文件一般在bin目录,而 php中就在php目录中。仔细看他们的信息会发现,这些文件都有版本信息,关键就是这个版本不一致导致。有人说将php中的这两个文件直接复制到 apache的bin目录中可以解决问题,但是不推荐这样做,因为也许在其他地方会出现错误。如果是自己配置,就下载apache和php中这两个文件版 本一致的。如果不想自己配置,可以使用xampp1.7.1版本。下载地址。
    5.最后说一下https的配置 (转自这里,并做了适当修改)
    SSL: SSl是为Http传输提供安全的协议,通过证书认证来确保客户端和网站服务器之间的数据是安全。也就是说在SSL下http传输是安全的,我们成为 https.
    配置过程如下:
    步骤一:安装apache,使其支持SSL,并安装php
    1.安装配有SSL模块的apache,比如apache_2.2.8-win32-x86-openssl-0.9.8g
    2.配置apache以支持SSL:打开apache的配置文件conf/httpd.conf
    1)LoadModule ssl_module modules/mod_ssl.so、Include conf/extra/httpd-ssl.conf
    去掉两行前面的#
    2)注意修改httpd-ssl.conf 文件里的两个字段:
    SSLCertificateFile “C:/Apache2.2/conf/server.crt”
    SSLCertificateKeyFile “C:/Apache2.2/conf/server.key”
    3.安装php,整合apache和Php(略)
    为下面你所生成的证书和密钥地址。
    步骤二:为网站服务器生成证书及私钥文件
  4. 生成服务器的私钥
    进入命令行:
    D:\local\apache2\bin\openssl genrsa -out server.key 1024
    在当前目录下生成了一个server.key生成签署申请
  5. 生成签署申请
    D:\local\apache2\bin>openssl req -new –out server.csr -key server.key -config…\conf\openssl.cnf
    此时生成签署文件server.csr.
    步骤三:通过CA为网站服务器签署证书
    1.生成CA私钥
    D:\local\apache2\bin\openssl genrsa -out ca.key 1024
    多出ca.key文件
    2.利用CA的私钥产生CA的自签署证书
    D:\local\apache2\bin\openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config …\conf\openssl.cnf
    此时需要输入一些信息,注意Common Name为服务器域名,如果在本机,为本机IP。
    3.CA为网站服务器签署证书
    D:\local\apache2\bin\openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config …\conf\openssl.cnf
    但,此时会报错,于是在在当前目录创建demoCA,里面创建以下文件,index.txt,serial,serial中内容为01,其他为空,以及文件夹 newcert.再执行一遍,即可生成server.crt文件
    步骤四:然后将 server.crt,server.key复制到apache的conf文件夹下,重启apache
    若是使用了xampp,其中自带了证书和私钥,不用自己配置,直接支持https。只需修改http-ssl.conf中的documentroot字段,想用https访问哪个目录就改为哪个。
    若以http访问,则http.conf中documentroot指向哪里,就访问哪里;若以https访问,则http-ssl.conf中的documentroot指向哪里就访问哪里。

你可能感兴趣的:(PHP通过OpenSSL生成证书、密钥并且加密解密数据,以及公钥,私钥和数字签名的理解)