PHP实现RSA报文加密解密以及签名和验签

RSA分段解密

有时和java接口对接,会用到分段解密

参考:https://www.cnblogs.com/meetuj/p/14954533.html

PHP 私钥分段解密

public function superLongPrivateKeyDecrypt($content,$key_file){
                $data = base64_decode($content); //有时需要两次 base64_decode 解码

                $key = openssl_pkey_get_private(file_get_contents($key_file));

                $RSA_DECRYPT_BLOCK_SIZE = 128; //对应 1024位的秘钥

                $result = '';
                $data = str_split($data, $RSA_DECRYPT_BLOCK_SIZE);
                foreach ($data as $block) {
                        // openssl_private_decrypt($block, $dataDecrypt, $key, OPENSSL_PKCS1_PADDING);
                        $r = openssl_private_decrypt($block, $dataDecrypt, $key, OPENSSL_NO_PADDING);
                          if( ! $r ) log('err '. openssl_error_string() );
                        $result .= $dataDecrypt;
                }

                if ($result) {
                        return $result;
                } else {
                        return false;
                }
        }

JAVA 私钥解密

/**
     * 私钥解密
     *
     * @param encryptedData
     *            已加密数据
     * @param privateKey
     *            私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

PHP实现RSA签名和验签

密钥生成

Mac和Linux 用户

安装openssl

生成1024位的私钥,不指定的话默认2048位

后逐条输入如下指令:

$ openssl                                    #进入 OpenSSL 程序
OpenSSL> genrsa -out rsa_private_key.pem 1024        #生成私钥
OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem               #Java开发者需要将私钥转换成PKCS8格式
OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥
OpenSSL> exit                                        #退出 OpenSSL 程序

拓展:RSA加密算法, PKCS#1 和PKCS#8区别是什么?

疑问:使用base64_encode编码之后出现的+和/在http的get传输过程中会出现+变成空格的请。

可以使用下面两个方法解决:

function _base64url_encode($data) {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
        
function _base64url_decode($data) {
        $str = str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT);
        return base64_decode($str);
}

PHP实现RSA用私钥签名

$privatekey = openssl_get_privatekey( file_get_contents($private_key_file_path) );
$signedStr = '';
openssl_sign($signStr, $signedStr, $privatekey, OPENSSL_ALGO_SHA256);
openssl_free_key($privatekey);
$sign = $this->_base64url_encode($signedStr);

PHP实现RSA用公钥验签

$public_key = openssl_get_publickey( file_get_contents($pub_key_file_path) );
if(empty($public_key)){
        return false;
}
$sign = base64_decode($sign);
$ok = openssl_verify( $sign_str, $sign, $public_key, OPENSSL_ALGO_SHA256 ); //SHA256
openssl_free_key( $public_key );
if ($ok == 1) {
        $result = true;
} elseif ($ok == 0) {
        $result = false;
} else {
        MLog::write('DEBUG', __CLASS__.' ' . __FUNCTION__ . ' 0 openssl_error_str '.json_encode(openssl_error_string));
}