近期对接一项目,对方是Java,用到了rsa的加解密以及签名。熬了一天才解决。。。
rsa的加解密需要用到OpenSSL的公钥私钥,在我上一篇的文章又说到,不懂得可以去看。
先上代码,然后再说这次开发中踩的坑::
一、RSA 加解密类
<?php namespace util; use Exception; class RSA { const PKCS1 = 'PKCS#1'; const PKCS8 = 'PKCS#8'; private $rsa_public; private $rsa_private; private $key_format; private $padding; public function __construct($rsa_public, $rsa_private, $key_format = self::PKCS8, $padding = OPENSSL_PKCS1_PADDING) { $this->rsa_public = $rsa_public; $this->rsa_private = $rsa_private; $this->key_format = $key_format; $this->padding = $padding; } public function sign($str) { openssl_sign($str, $sign, $this->getPrivateKey()); return base64_encode($sign); } // 加密 public function encrypt($content) { $crypto = ''; $pu_key = $this->getPublicKey(); foreach (str_split($content, 117) as $chunk) { openssl_public_encrypt($chunk, $encryptData, $pu_key,$this->padding); $crypto .= $encryptData; } return base64_encode($crypto); } // 解密 public function decrypt($content) { if (!is_string($content)) return null; $decrypted = ''; $chunks = str_split(base64_decode($content), 128); foreach ($chunks as $chunk) { $partial = ''; $decryptIsTrue = openssl_private_decrypt($chunk, $partial, $this->getPrivateKey()); if ($decryptIsTrue === false) { return null; } $decrypted .= $partial; } return $decrypted; } public function getPublicKey() { if ($this->key_format == self::PKCS1) { $search = [ "-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", "\n", "\r", "\r\n" ]; $public_key = str_replace($search, "", $this->rsa_public); $public_key = $search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1]; } else { $public_key = $this->rsa_public; } $key = openssl_pkey_get_public($public_key);//这个函数可用来判断公钥是否是可用的 if (!$key) { $this->throwError('公钥不可用'); } return $key; } public function getPrivateKey() { if ($this->key_format == self::PKCS1) { $search = [ "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----", "\n", "\r", "\r\n" ]; $private_key = str_replace($search, "", $this->rsa_private); $private_key = $search[0] . PHP_EOL . wordwrap($private_key, 64, "\n", true) . PHP_EOL . $search[1]; } else { $private_key = $this->rsa_private; } $pi_key = openssl_pkey_get_private($private_key); if (!$pi_key) { $this->throwError('私钥不可用'); } return $pi_key; } public function throwError($msg) { throw new Exception($msg); } }
二、调用方法
rsa加解密,普遍的是把自己的公钥给对方,使用彼此的公钥进行加密,然后使用
私钥进行解密的操作。这里只提供加解密以及签名案例
//我自己的私钥 private $private_key = '-----BEGIN RSA PRIVATE KEY----- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -----END RSA PRIVATE KEY-----'; //对方的公钥 private $public_key = '-----BEGIN PUBLIC KEY----- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -----END PUBLIC KEY-----'; //定义需要加密的测试数据 $bizContent=array( "supplierCode"=>"202372000142021000064", "type"=>"1", "surrenderTime"=>"2021-09-10 15:30:00" ); //TODO 对数据进行签名以及加密 $bizContent=json_encode($bizContent); $utilRsa = new RSA($this->public_key , ''); $bizContent_k = $utilRsa->encrypt($bizContent); //需要进行签名的数据串 $sign='appKey=123123&bizContent='.$bizContent_k.'&charset=UTF-8&format=JSON&serviceName=serviceName&signType=RSA×tamp=111111&version=1.0.0'; //对sign进行私钥加密 $sign_k= (new RSA('',$this->private_key))->sign($sign); //数据解密 $res_bizContent = '需要解密的密文'; $res = (new RSA('',$this->private_key))->decrypt($res_bizContent); $bizContent = json_decode($res,true);
三、注意细节
1、加密用的是对方的公钥,解密使用自己的私钥。
2、加解密的时候,会受到密文长度的限制报错,这时需要将密文分段加解密然后把结果在拼接起来
3、PHP的rsa加解密方法和Java还是有区别的,需要进行详细的沟通,了解具体的加密方法才可以匹配成功
4、第三方提供的java rsa秘钥(pkcs8格式),PHP不能直接使用;只能将rsa秘钥转成pkcs1格式,然后再按照PHP rsa秘钥格式处理。上面的加解密类有类似的方法