diffnest / myblog

0 stars 0 forks source link

php 使用openssl进行RSA2加解密以及签名算法 #4

Open diffnest opened 7 years ago

diffnest commented 7 years ago

在对接玖富债权项目中,用到了RSA加解密算法,特此记录下;

  1. 公私钥成对出现,相互解密; 公钥加密,私钥解密。 私钥数字签名,公钥验证。

  2. 生成RSA秘钥对,主要利用linux OPENSSL

    2.1 生成RSA私钥
    genrsa -out rsa_private_key.pem 1024

    2.2 把RSA私钥转换成PKCS8格式
    pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt

    2.3 生成RSA公钥
    rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

    打开rsa_public_key.pem可以看到-----BEGIN PUBLIC KEY-----开头, -----END PUBLIC KEY-----结尾的没有换行的字符串,这个就是公钥

  3. PHP中实现加解密类

class RSA {

    private $pubKey = null;
    private $priKey = null;

    /**
    * 构造函数
    * @param string 公钥文件(验签和加密时传入)
    * @param string 私钥文件(签名和解密时传入)
    */
    public function __construct($pubKeyFile = '', $priKeyFile = '')
    {
        if ($pubKeyFile) {
            $this->pubKey = $this->getFileContent($pubKeyFile);
        }
        if ($priKeyFile) {
            $this->priKey = $this->getFileContent($priKeyFile);
        }
    }

    /**
     * RSA签名
     * @param $data 待签名数据
     * @param $priKey 商户私钥文件路径 或 密钥内容本身
     * return $sign 签名结果
     */
    public function sign($data, $code = 'base64') 
    {
        $result = openssl_get_privatekey($this->priKey);
        if (openssl_sign($data, $sign, $result)) {
            openssl_free_key($result);
            $sign = $this->encode($sign, $code);
        }
        return $sign;
    }

    /**
    * 验证签名
    * @param $data 待签名数据
    * @param $sign 要校对的的签名结果
    * @param string 签名编码(base64/hex/bin)
    * @return bool
    */
    public function verify($data, $sign, $code = 'base64')
    {
        $ret = false;
        $result = openssl_get_publickey($this->pubKey);
        $sign = $this->decode($sign, $code);
        if ($sign !== false) {
            $ret = (bool) openssl_verify($data, $sign, $result); 
            openssl_free_key($result);
        }
        return $ret;
    }

    /**
     * RSA加密
     * @param $content 需要加密的内容
     * @param $this->pubKey 商户公钥文件路径 或 密钥内容本身
     * return 加密后内容,明文
     */
    public function encrypt($content, $code = 'base64')
    {
        $result = openssl_get_publickey($this->pubKey);
        $rslt = '';
        for ($i = 0; $i < ((strlen($content)-strlen($content)%117) / 117 + 1); $i++) {
            $data = mb_strcut($content, $i * 117, 117, 'utf-8');
            openssl_public_encrypt($data, $encrypted, $result);
            $rslt .= $encrypted;
        }
        openssl_free_key($result);
        $rslt = $this->encode($rslt, $code);
        return $rslt;
    }

    /**
     * RSA解密
     * @param $content 需要解密的内容,密文
     * @param $this->priKey 商户私钥文件路径 或 密钥内容本身
     * return 解密后内容,明文
     */
    public function decrypt($content, $code = 'base64')
    {
        $result = openssl_get_privatekey($this->priKey);
        $content = $this->decode($content, $code);
        $rslt = '';
        for ($i = 0; $i < strlen($content)/128; $i++) {
            $data = substr($content, $i * 128, 128);
            openssl_private_decrypt($data, $decrypt, $result);
            $rslt .= $decrypt;
        }
        openssl_free_key($result);
        return $rslt;
    }

    private function encode($data, $code)  
    {
        switch (strtolower($code)) {
            case 'base64':
                $data = base64_encode(''.$data);
            break;
        }
        return $data;
    }

    private function decode($data, $code)
    {
        switch (strtolower($code)) {
            case 'base64':
                $data = base64_decode($data);
            break;
        }
        return $data;
    }

    private function getFileContent($file)   
    {
        if (file_exists($file)) {
            return file_get_contents($file);
        }
        return $file;
    }
}

4.代码中具体调用:

   /**
     * 同步债权信息
     * @param $orderNo 订单ID
     * @param $memberId 玖富理财用户ID
     * @return boolen
     */
    function syncRepayPlan($sDate, $eDate)
    {
        if (!$sDate || !$eDate) {
            return false;
        }
        $data = array('inputCharset' => jiufuStatus::JIUFU_INPUTCHARSET,
            'version' => jiufuStatus::JIUFU_VERSION,
            'channelId' => jiufuStatus::JIUFU_CHANNELID,
            'startTime' => $sDate,
            'endTime' => $eDate,
        );
        $requestNo = sprintf("%d:'%s':'%s'",
            jiufuStatus::REQTYPE_JIUFU_RSYNC_REPAY_PLAN, 'jiufuSyncRepayPlan', uniqid());
        $dataString = json_encode($data);
        $info = array(
            'requestno' => $requestNo,
            'type' => jiufuStatus::REQTYPE_JIUFU_RSYNC_REPAY_PLAN,
            'reqtime' => CMI_DATE_NOW,
            'data' => $dataString
        );
        $r = $this->xdReqst->createRequest($requestNo,
            jiufuStatus::REQTYPE_JIUFU_RSYNC_REPAY_PLAN, $info);
        if ($r) {
            $rslt = $this->excuteReq(__FUNCTION__, $data);
            return $this->handleRepayPlan($requestNo, $rslt);
        }
    }

    /**
     * 处理还款计划
     * @param $requestNo 请求ID
     * @param $rslt
     * @return boolen
     */
    function handleRepayPlan($requestNo, $rslt)
    {
        if ($rslt['returnCode'] != '0000' || empty($rslt['responseData'])) {
            printf('获取还款计划失败%s', $rslt['returnMessage']);
            return false;
        }
        $dataList = $rslt['responseData'];
        foreach ($dataList as $key => &$value) {
            $value['channelId'] = $rslt['channelId'];
        }
        $classFun = __FUNCTION__;
        $handleObj = new handleData();
        $handleObj->valList = $dataList;
        $r = $handleObj->$classFun();
        if ($r['Status'] == 'Fail') {
            $error = $r['Message'];
            $this->xdReqst->updateRequest($requestNo,
                REQSTATUS_FAILED, array('data' => $data, 'errorMsg' => $error));
            $this->errorMsg(__LINE__. $error);
            return false;
        }
        $this->xdReqst->updateRequest($requestNo, REQSTATUS_SUCCESS, $r);
        return true;
    }

    /**
    * 发送请求
    * @param $reqName 请求名称
    * @param $data 请求数据
    * @return array
    */
    public function excuteReq($reqName, $data = null)
    {
        $rsaObj = new rsa(PUBLIC_KEY_PATH, PRIVATE_KEY_PATH);

        //加密加签
        $secretData = $rsaObj->encrypt(json_encode($data));
        $secretSign = $rsaObj->sign(base64_decode($secretData));

        //请求接口
        $rslt = $this->proxy->$reqName(array('data' => $secretData, 'sign' => $secretSign));
        if (!$rsaObj->verify(base64_decode($rslt['data']), $rslt['sign'])) {
            $error = sprintf("验签失败");
            $this->errorMsg(__LINE__. $error);
        }

        //解密接口返回数据
        $rsltData = $rsaObj->decrypt($rslt['data']);
        return json_decode($rsltData, true);
    }