PHP生成pem公钥私钥对
如果不能生成密钥对,则修改验证变量$confs数组中的config值。
最初我不能生成公钥,是因为函数openssl_pkey_export(),只使用一前面两个参数,后台增加了后面两个参数就可以了。原因是:因为openssl_pkey_new($confs)中使用了$confs,那么函数openssl_pkey_export()也必须加入参数$confs。
实例一:
0 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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
<?php // 生成 openssl // 在浏览器中右键查看源码,可查看到格式字符串 class Rsa { public $privateKey = ''; public $publicKey = ''; public function __construct() { $confs = array( // 在我的环境中,这里必须配置一个存在openssl.cnf文件的目录 // 其实我本地电脑中独立安装着一个 openssl ,环境变量中也是存在的 // 下面路径使用的是WEB集成环境中的路径 // 原因不理解,分析:也许是因为为了防止出错,才要求必须配置cnf路径 'config' => 'D:\tool\Apache\conf\openssl.cnf', // 默认为 1024 ,可改为 512 的倍数(1024/1536/2048/... ),不知道封顶值是多少 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ); $resource = openssl_pkey_new($confs); openssl_pkey_export($resource, $this->privateKey, null, $confs); $detail = openssl_pkey_get_details($resource); $this->publicKey = $detail['key']; // var_dump($this->privateKey); // var_dump($this->publicKey); } // 使用公钥加密 public function publicEncrypt($data, $publicKey) { openssl_public_encrypt($data, $encrypted, $publicKey); return $encrypted; } // 使用公钥解密 public function publicDecrypt($data, $publicKey) { openssl_public_decrypt($data, $decrypted, $publicKey); return $decrypted; } // 使用私钥加密 public function privateEncrypt($data, $privateKey) { openssl_private_encrypt($data, $encrypted, $privateKey); return $encrypted; } // 使用私钥解密 public function privateDecrypt($data, $privateKey) { openssl_private_decrypt($data, $decrypted, $privateKey); return $decrypted; } // 发送数据前 // base64在url传递过程中需要注意的 // 把base64加密后在url传输,会把“+”,“/”分别替换为“-”,“_”,以及会把末尾的等号“=”去掉。 // 另外base64加密后的长度必然是4的倍数,所以可以根据这个还原“=”号 function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); // return $data; //不处理会乱码 // return base64_encode($data); } // 接收数据后 按约定还原base64加密过的数据 function base64url_decode($data) { return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); } } $rsa = new Rsa(); echo "公钥:<br>\n", $rsa->publicKey, "<br>\n"; echo "私钥:<br>\n", $rsa->privateKey, "<br>\n"; // 使用公钥加密 $dataStr = '我的博客mrdede.com'; $str = $rsa->publicEncrypt($dataStr, $rsa->publicKey); // 这里使用base64是为了不出现乱码,默认加密出来的值有乱码 $str = base64_encode($str); echo "公钥加密(base64处理过):\n", $str, "\n"; $str = base64_decode($str); $pubstr = $rsa->publicDecrypt($str, $rsa->publicKey); echo "公钥解密:\n", $pubstr, "\n"; $privstr = $rsa->privateDecrypt($str, $rsa->privateKey); echo "私钥解密:\n", $privstr, "\n"; // 使用私钥加密 $dataStr = '我是好人'; $str = $rsa->privateEncrypt($dataStr, $rsa->privateKey); // 这里使用base64是为了不出现乱码,默认加密出来的值有乱码 $str = base64_encode($str); echo "私钥加密(base64处理过):\n", $str, "\n"; $str = base64_decode($str); $pubstr = $rsa->publicDecrypt($str, $rsa->publicKey); echo "公钥解密:\n", $pubstr, "\n"; $privstr = $rsa->privateDecrypt($str, $rsa->privateKey); echo "私钥解密:\n", $privstr, "\n"; // 使用约定编辑发送/接收数据 $dataStr = '使用约定编辑发送/接收数据'; $str = $rsa->publicEncrypt($dataStr, $rsa->publicKey); echo "加密前未处理过:\n", $str, "\n"; // 这里使用base64是为了不出现乱码,默认加密出来的值有乱码 $str = $rsa->base64url_encode($str); echo "私钥加密(编码处理过):\n", $str, "\n"; $str = $rsa->base64url_decode($str); echo "解密前解码处理过:\n", $str, "\n"; $privstr = $rsa->privateDecrypt($str, $rsa->privateKey); echo "私钥解密:\n", $privstr, "\n"; |
公钥加密(openssl_public_encrypt),
私钥解密(openssl_private_decrypt)。
私钥加密(openssl_private_encrypt),
公钥解密(openssl_public_decrypt)。
都是一个道理,代码类似。
RSA加密解密有个填充方式padding的参数,不同编程语言之间交互,需要注意这个。
padding
can be one of OPENSSL_PKCS1_PADDING
, OPENSSL_SSLV23_PADDING
, OPENSSL_PKCS1_OAEP_PADDING
,OPENSSL_NO_PADDING
值得注意的是,如果选择密钥是1024bit长的(openssl genrsa -out rsa_private_key.pem 1024),那么支持加密的明文长度字节最多只能是1024/8=128byte;
如果加密的padding填充方式选择的是OPENSSL_PKCS1_PADDING(这个要占用11个字节),那么明文长度最多只能就是128-11=117字节。如果超出,那么这些openssl加解密函数会返回false。
这时有个解决办法,把需要加密的源字符串按少于117个长度分开为几组,在解密的时候以172个字节分为几组。
其中的『少于117』(只要不大于117即可)和『172』两个数字是怎么来的,值得一说。
为什么少于117就行,因为rsa encrypt后的字节长度是固定的,就是密钥长1024bit/8=128byte。因此只要encrypt不返回false,即只要不大于117个字节,那么返回加密后的都是128byte。
172是因为什么?因为128个字节base64_encode后的长度固定是172。
这里顺便普及下base64_encode。encode的长度是和原文长度有个计算公式:
$len2 = $len1%3 >0 ? (floor($len1/3)*4 + 4) : ($len1*4/3);
实例二:
明文超出长度的代码(前提是1024bit的密钥长,OPENSSL_PKCS1_PADDING的填充方式,否则数字要变化)
0 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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<?php class Ssl{ // 加密 static function encrypt($source,$type,$key){ //Assumes 1024 bit key and encrypts in chunks. $maxlength = 117; $output = ''; while($source){ $input = substr($source, 0, $maxlength); $source = substr($source, $maxlength); if($type == 'private'){ $ok = openssl_private_encrypt($input,$encrypted,$key); }else{ $ok = openssl_public_encrypt($input,$encrypted,$key); } $output.=$encrypted; } return $output; } // 解密 static function decrypt($source,$type,$key){ // The raw PHP decryption functions appear to work // on 128 Byte chunks. So this decrypts long text // encrypted with ssl_encrypt(). $maxlength = 128; $output = ''; while($source){ $input = substr($source,0,$maxlength); $source = substr($source,$maxlength); if($type == 'private'){ $ok = openssl_private_decrypt($input,$out,$key); }else{ $ok = openssl_public_decrypt($input,$out,$key); } $output .= $out; } return $output; } } // 准备公钥私钥 $private_key = $rsa->privateKey; $public_key = $rsa->publicKey; $pi_key = openssl_pkey_get_private($private_key); $pu_key = openssl_pkey_get_public($public_key); if(!$pi_key || !$pu_key){ $msgs = ''; while($msg = openssl_error_string()){ $msgs = "$msg<br>\n$msgs<br>\n"; } echo $msgs; exit; } // 待处理的数据 $data = array( 'username' => '织梦先生', 'mobile' => '13888888888', 'info' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLZ8fGreYHRnEX93G/2CM2qqXHDiRtOEQIRJZU/ntbXtCWyWZKTKBuLPM+znpPZuf7i9Y1NBEAMYyfcUKOYtFcexSGjngs/Yp3i51gi8bIC8S4UNkKC2Kl5w1t5MRhfdenpPY4/76TfbjEkhiauDUTEZ1xZ7Cs/utFXVDtrLkXgQIDAQAB', 'infos' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLZ8fGreYHRnEX93G/2CM2qqXHDiRtOEQIRJZU/ntbXtCWyWZKTKBuLPM+znpPZuf7i9Y1NBEAMYyfcUKOYtFcexSGjngs/Yp3i51gi8bIC8S4UNkKC2Kl5w1t5MRhfdenpPY4/76TfbjEkhiauDUTEZ1xZ7Cs/utFXVDtrLkXgQIDAQAB', ); $str = json_encode($data); echo "字节数 ".strlen($str)."<br>\n"; $en = ssl::encrypt($str, null, $pu_key); echo "加密后<br>\n$en<br>\n"; $de = ssl::decrypt($en, 'private', $pi_key); echo "解密后<br>\n$de<br>\n"; |
在实际应用中,分析使用场景,再把两个实例再进行一下结合,相信就会更加美妙了,这里就不多写了。