RSA加解密及RSA签名验证

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/maosilu_ICE/article/details/86217266

工作中用到了非对称加密:RSA加解密及签名验证,根据查到的信息及工作中的问题总结,现在整理如下:

1. 准备好公钥和私钥,使用openssl工具生成RSA公钥和私钥对

1)生成RSA私钥:

genrsa -out rsa_private_key.pem 1024

该命令会生成1024位的私钥,可以在当前路径下看到rsa_private_key.pem文件。

2)把RSA私钥转换成PKCS8格式
输入命令pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt,并回车
得到生成功的结果,这个结果就是PKCS8格式的私钥,这个将密钥转换为PKCS8格式格式的只用在java语言中,PHP中不需要使用

3) 生成RSA公钥

输入命令rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem,并回车,
得到生成成功的结果,此时,我们可以看到一个文件名为rsa_public_key.pem的文件,打开它,可以看到-----BEGIN PUBLIC KEY-----开头,-----END PUBLIC KEY-----结尾的字符串,这个就是公钥。

2. RSA加密解密,项目中加解密使用的公私钥可能不同,但思路都是一样,以下贴出我加解密的方法及需要注意的问题:

<?php
    /**
     * RSA私钥加密,使用规定的私钥进行加密
     * @param $data array 需要加密的数据,加密数据的格式是json还是其他格式字串需要根据项目需求来确定
     * @return $encrypted String 加密后的数据
    */
    public function rsaPrivateEncrypt($data){
//        $pi_key = key($this->_shangmei_privateKey, 'private'); // 私钥,调用key()函数拼接正确格式私钥字串,并验证私钥可用
        $pri_key = file_get_contents('./Public/RSA/rsa_private_key.pem'); // 私钥,直接读取私钥文件,获取私钥
        $pi_key = openssl_pkey_get_private($pri_key); // 获取私钥,这个函数可用来判断公钥是否是可用的,必须要有
        $encrypted = '';
        $data = json_encode($data);
        //注意:最大允许加密长度为117,加密数据过长得分段加密
        $plainData = str_split($data, 100);//生成密钥位数 1024 bit key
        foreach($plainData as $chunk){
            $partialEncrypted = '';
            $encryptionOk = openssl_private_encrypt($chunk,$partialEncrypted,$pi_key);//私钥加密
            if($encryptionOk === false){
                $this->_message['response_status'] = -8;
                $this->_message['data'] = '数据加密不成功';
                sendJson($this->_message);
                exit();
            }else{
                $encrypted .= $partialEncrypted;
            }
        }
        $encrypted = base64_encode($encrypted);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
        return $encrypted;
    }

    /**
     * RSA公钥解密,使用项目中规定的公钥进行解密
     * @return $decrypted json 返回解密后的json数据
    */
    public function rsaPublicDecrypt(){
        $pu_key = key($this->_zudian_publicKey, 'public'); // 调用key()方法
        $decrypted = '';
        $plainData = str_split(base64_decode(urldecode($this->_data)), 128);//生成密钥位数 1024 bit key
        // 分段解密
        foreach($plainData as $chunk){
            $str = '';
            $decryptionOk = openssl_public_decrypt($chunk,$str,$pu_key);//公钥解密
            if($decryptionOk === false){
                $this->_message['response_status'] = "-11";
                $this->_message['data']="data解密不成功";
                sendJson($this->_message);
                exit();
            }
            $decrypted .= $str;
        }
        return $decrypted;
    }

    /**
     * 公私钥字符串处理,拼接为正确的公私钥格式
     * @param $key String 密钥字符串
     * @param $type String 密钥类型,public:公钥    private:私钥
     * @return $key String 返回获取的密钥
    */
    public function key($key, $type){
        //读取公钥文件
//        $pubKey = file_get_contents('./rsa_public_key.pem');
        //64个英文字符后接换行符"\n",最后再接换行符"\n"
        $key = (wordwrap($key, 64, "\n", true))."\n";
        //添加pem格式头和尾
        if($type == 'public'){
            $pubKey = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";
            $key = openssl_pkey_get_public($pubKey); // 获取公钥,这个函数可用来判断公钥是否是可用的
        }elseif ($type == 'private'){
            $priKey = "-----BEGIN RSA PRIVATE KEY-----\n" . $key . "-----END RSA PRIVATE KEY-----\n";
            $key = openssl_pkey_get_private($priKey); // 获取私钥,这个函数可用来判断公钥是否是可用的
        }

        return $key;
    }
?>

3. RSA签名验证

<?php
/**
     * 生成RSA签名的待签名字符串
     * @param string $data 解析得到的用户数据,根据项目需求拼接为待签名字符串,一下是我在一个项目中根据项目需求拼接成的待签名字符串
     * return 待签名字符串
    */
    public function rsaUnsign($data){
        $argus = $data;
        if(isset($argus['sign'])){
            unset($argus['sign']);
        }
        if(isset($argus['sign_type'])){
            unset($argus['sign_type']);
        }
        file_put_contents("cardfun_login_log.txt", '接收待签名数据:'.date("Y-m-d H:i:s")."||".json_encode($argus, JSON_UNESCAPED_UNICODE).PHP_EOL,FILE_APPEND);
        ksort($argus);
        reset($argus);
        $unsign = '';
        foreach($argus as $k => $v){
            $unsign .= $k.'='.$v.'&';
        }
        $unsign = substr($unsign, 0, strlen($unsign)-1); // 待签名字符串,去掉最后一个&字符
        //如果存在转义字符,那么去掉转义
        if(get_magic_quotes_gpc()){$unsign = stripslashes($unsign);}

        unset($data);
        unset($argus);
        return $unsign;
    }

    /**
     * 生成RSA签名,这个暂时没有用到,就先放在这
     * @param array $data 待签名的数据
     * return 签名结果
    */
    public function rsaSign($unsign){
        $priKey = file_get_contents('/rsa/rsa_private_key.pem'); // 本地私钥
        $res = openssl_get_privatekey($priKey);
        openssl_sign($unsign, $sign, $res);
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);

        //对签名进行urlencode转码
        $sign = urlencode($sign);
        //生成最终签名信息
        $orderInfor = $unsign."&sign=".$sign."&sign_type=RSA";
        return $orderInfor;
    }

    /**
     * 验证RSA签名信息,用户的私钥与对接方的公钥做交换
     * @param string $unsign 用户传过来的待签名字符串,即根据rsaUnsign()生成的待签名数据
     * @param string $rawSign 对接方传过来的待签名字符串,注意一般都会经过base64_decode编码,get传递还要经过urldecode编码,否则特殊字符在传递过程中容易被浏览器解析
     * return 验签结果
    */
    public function rsaVerify($unsign, $rawSign){
        header('content-type:text/html;charset=utf-8');
        $sign = urldecode($rawSign);
//        $sign = base64_decode(str_replace(' ', '+', $sign));
        $sign = base64_decode($sign);
        //读取公钥文件
//        $pubKey = file_get_contents('./rsa_public_key.pem');
        //64个英文字符后接换行符"\n",最后再接换行符"\n"
        $key = (wordwrap($this->_secretkey, 64, "\n", true))."\n";
        //添加pem格式头和尾
        $pubKey = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";
        //转换为openssl格式密钥
        $res = openssl_get_publickey($pubKey);
        //调用openssl内置方法验签,返回bool值
        $result = (bool)openssl_verify($unsign, $sign, $res);
        file_put_contents("cardfun_login_log.txt", date("Y-m-d H:i:s")."||".$result.PHP_EOL,FILE_APPEND);
        //释放资源
        openssl_free_key($res);
        //返回资源是否成功
        if($result != 1){
            $this->_message['err_msg']="sign签名验签失败";
            $this->_message['err_code'] = "-1";
            sendJson($this->_message);
            exit();
        }
    }
?>

猜你喜欢

转载自blog.csdn.net/maosilu_ICE/article/details/86217266