最近做钱包项目的时候,为了安全(??)前后端交互的数据,sign和返回的数据统计进行的rsa处理,这就会有一些问题,比如当返回数据过长,导致加解密失败,这时候就要分段处理,这里分享一下我处理的方式
首先肯定是生成公钥私钥,这个没什么好说的,全都是一样的处理

//生成公钥私钥配置信息
    CONST CONFIG = [
        "DIGEST_ALG"        => "sha512",
        "PRIVATE_KEY_BITS"  => 4096,
        "PRIVATE_KEY_TYPE"  => OPENSSL_KEYTYPE_RSA,
    ];

    public static function CreateKey() {
        $res = openssl_pkey_new(self::CONFIG);
        openssl_pkey_export($res, $private_key);
        $public_key = openssl_pkey_get_details($res);
        $public_key = $public_key["key"];
        var_dump($private_key);    //私钥
        var_dump($public_key);     //公钥
    }

生成时候的private_key_bits是很有讲究的,一般肯定是越长(1024或2048)越安全,但是相应的分段加解密时候,分段的字节数也有所不同。不过我推荐用代码计算字节数,而不是写死,这样复用性更好一些。

/**
* @param $string
* @param int $opt
* @return bool|string
* 公钥私钥加密解密
*/
public static function Crypt($string,$opt=0){
self::$public_key = CUtil::getConfig('openssl_public_key','common',\Yii::$app->id);
$pub_id = openssl_pkey_get_public(self::$public_key);
self::$public_key_len = openssl_pkey_get_details($pub_id)['bits'];

self::$private_key = CUtil::getConfig('openssl_private_key','common',\Yii::$app->id);
$pri_id = openssl_pkey_get_private(self::$private_key);
self::$private_key_len = openssl_pkey_get_details($pri_id)['bits'];

switch ($opt) {
case self::TYPE['PUB_KEY_ENCODE'] : // 客户端公钥加密
//分段加密
$crypted = self::pubEnc($string);
break;
case self::TYPE['PRI_KEY_DECODE'] : // 服务器私钥解密
$crypted = self::priDec($string);
break;
case self::TYPE['PRI_KEY_ENCODE'] : // 服务器私钥加密
//分段
$crypted = self::priEnc($string);
break;
case self::TYPE['PUB_KEY_DECODE'] : // 客户端公钥解密
//分段
$crypted = self::pubDec($string);
break;
default :
$crypted = ""; break;
}

return $crypted;
}

/**
* 公钥加密
*/
private static function pubEnc($string)
{
$part_len = self::$public_key_len / 8 - 11;
$split = str_split($string, $part_len);
$crypto = '';
foreach ($split as $chunk) {
openssl_public_encrypt($chunk, $crypted, self::$public_key);
$crypto .= $crypted;
}
$crypted = base64_encode($crypto);
return $crypted;
/*openssl_public_encrypt($string, $crypted, $public_key);
$crypted = base64_encode($crypted);*/
}

/**
* 公钥解密
*/
private static function pubDec($string)
{
$string = base64_decode($string);
$part_len = self::$public_key_len / 8 ;
$split = str_split($string, $part_len);
$crypto = '';
foreach ($split as $chunk) {
$crypted = '';
openssl_public_decrypt($chunk,$crypted, self::$public_key);
$crypto .= $crypted;
}
$crypted = $crypto;
return $crypted;
/*$string = base64_decode($string);
openssl_public_decrypt($string,$crypted,$public_key);*/
}

/**
* 私钥加密
*/
private static function priEnc($string)
{
$part_len = self::$private_key_len / 8 - 11;
$split = str_split($string, $part_len);
$crypto = '';
foreach ($split as $chunk) {
$crypted = '';
openssl_private_encrypt($chunk, $crypted, self::$private_key);
$crypto .= $crypted;
}
//var_dump($crypto);
$crypted = base64_encode($crypto);
return $crypted;

/*openssl_private_encrypt($string, $crypted, $private_key);
$crypted = base64_encode($crypted);*/
}

/**
* @param $string
* 私钥解密
*/
private static function priDec($string)
{
$string = base64_decode($string);
$part_len = self::$private_key_len / 8 ;
$split = str_split($string, $part_len);
$crypto = '';
foreach ($split as $chunk) {
$crypted = '';
openssl_private_decrypt($chunk,$crypted, self::$private_key);
$crypto .= $crypted;
}
$crypted = $crypto;
return $crypted;
/*$string = base64_decode($string);
openssl_private_decrypt($string,$crypted,$private_key);*/
}

注:因为用到了base64编码,所以最好使用post进行交互,用get的话会把编码中的+全部转换成空格,可以用

str_replace('% ' ,'+',$encrypted);

进行转换,不过还是推荐post。
这里有人也许会注意到分段加密时,需要减去11个字节,这是因为加密方式用的默认的OPENSSL_PKCS1_PADDING,这个东西需要占用11个字节,所以需要减去
最后分享一下rsa加签方式

/*
     * 数据加签
     */
    public function sign($data)
    {
        openssl_sign($data, $sign, $this->private_key, self::RSA_ALGORITHM_SIGN);
 
        return base64_encode($sign);
    }
 
    /*
     * 数据签名验证
     */
    public function verify($data, $sign)
    {
        $pub_id = openssl_get_publickey($this->public_key);
        $res = openssl_verify($data, base64_decode($sign), $pub_id, self::RSA_ALGORITHM_SIGN);
 
        return $res;
    }


0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据