RSA 签名/验签 (PHP为例),以及各个秘钥格式解析

in web技术 with 0 comment 访问: 306 次

函数明细

只能打开是PEM格式的秘钥,成功返回资源类型

只能打开是PEM格式的秘钥,成功返回资源类型

加密后的数据可以通过openssl_public_decrypt()函数来解密
该函数用来签名数据(或者哈希)让别人相信数据并不是其他人写的

你可以用该函数来校验消息是否是私钥拥有者写的。

该函数可以用来加密数据,供该公钥匹配的私钥拥有者读取。 它也可以用来在数据库中存储安全数据。

openssl_private_decrypt() 解密先前通过 openssl_public_encrypt() 函数加密的 data
你可以用该函数来解密只对你可用的数据。

用例解析

/**
 * 通用rsa认证与加密类
 *
 * @author litblc
 * User: z00455118
 * Date: 2019/7/15
 * Time: 19:20
 */

class RSA
{
    /**
     * 签名算法, 默认为 OPENSSL_ALGO_SHA1
     */
    const RSA_ALGORITHM_SIGN = OPENSSL_ALGO_SHA256;

    /**
     * 公钥
     * @var string
     */
    private static $publicKey = 'file:///D:/cert/public-key.pem';

    /**
     * 私钥
     * @var string
     */
    private static $privateKey = 'file:///D:/cert/private-key.pem';

    /**
     * 第三方公钥 交互验签使用
     * @var string
     */
    private static $thirdPublicKey = 'file:///D:/cert/third-public-key.pem';

    /**
     * 初始化秘钥信息
     * @var string
     */
    public function __construct($config)
    {
        self::$publicKey = CERT_DIR . $config['publicKey'];
        self::$privateKey = CERT_DIR . $config['privateKey'];
        self::$thirdPublicKey = CERT_DIR . $config['thirdPublicKey'];
    }
    /**
     * 是否使用安全base64需要参考第三方验签的解析方案,如果也是php推荐使用安全方式
     * @param $data
     * @return string
     */
    private static function url_safe_base64_encode($data)
    {
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
    }

    /**
     * @param $data
     * @return string
     */
    private static function url_safe_base64_decode($data)
    {
        $base_64 = str_replace(['-', '_'], ['+', '/'], $data);

        return base64_decode($base_64);
    }

    /**
     * 获取rsa密钥加密位数
     * @param $source
     * @return mixed
     */
    private static function getKeyBitDetail($source)
    {
        return openssl_pkey_get_details($source)['bits'];
    }

    /**
     * 获取文本格式私钥 并重新格式化 为保证任何key都可以识别
     * 由于各个语言以及环境使用的证书格式不同。参考下一节: ### 秘钥格式解析
     * @return bool|resource
     */
    private static function getPrivateKey()
    {
        if (file_exists(self::$publicKey)) {
            $source =  file_get_contents(self::$privateKey);

            $search = [
                "-----BEGIN RSA PRIVATE KEY-----",
                "-----END RSA PRIVATE KEY-----",
                "\n",
                "\r",
                "\r\n"
            ];

            $private_key = str_replace($search,"",$source);
            return openssl_pkey_get_private($search[0] . PHP_EOL . wordwrap($private_key, 64, "\n", true) . PHP_EOL . $search[1]);
        }
    }

    /**
     * 获取公钥 并重新格式化
     * @return resource
     */
    private static function getPublicKey()
    {
        if (file_exists(self::$publicKey)) {
            $source = file_get_contents(self::$publicKey);

            $search = [
                "-----BEGIN PUBLIC KEY-----",
                "-----END PUBLIC KEY-----",
                "\n",
                "\r",
                "\r\n"
            ];
            $public_key = str_replace($search,"",$source);

            return openssl_pkey_get_public($search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1]);
        }
    }

    /**
     * 获取第三方公钥,并格式化
     * @return resource
     */
    private static function getPublicKeyThird()
    {
        if (file_exists(self::$thirdPublicKey)) {
            $source = file_get_contents(self::$thirdPublicKey);

            $search = [
                "-----BEGIN PUBLIC KEY-----",
                "-----END PUBLIC KEY-----",
                "\n",
                "\r",
                "\r\n"
            ];
            $public_key = str_replace($search, "", $source);

            return openssl_pkey_get_public($search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1]);
        }
    }

    /**
     * 排序数据并生成待验签字符串(类似微信支付,使用此方法,而非例子中json_encode方法)
     * @return string
     */
    private static function createLinkString($data = [])
    {
        unset($data['sign']);

        foreach ($data as $key => $val) {
            if (!$val) {
                unset($data[$key]);
            }
        }
        ksort($data);

        return urldecode(http_build_query($data));
    }

    /**
     * 排序数据并生成待验签字符串(类似微信支付,使用此方法,而非例子中json_encode方法)
     * @return string
     */
    public static function createLinkStringNew($data = [])
    {
        $mac = '';
        ksort($data);
        unset($data['sign'], $data['sign_type']);

        foreach ($data as $key => $val) {
            if ($val) {
                $mac .= "&{$key}={$val}";
            }
        }

        return ltrim($mac, '&');
    }

    /**
     * 私钥加密
     * @param $data
     * @return bool|null
     */
    public static function privEncrypt($data = '')
    {
        $privKey = self::getPrivateKey();

        $partLen = self::getKeyBitDetail($privKey) / 8 - 11;

        $parts = str_split($data, $partLen);

        $encrypted = '';

        foreach ($parts as $part) {
            openssl_private_encrypt($part, $partEncrypt, $privKey);
            $encrypted .= $partEncrypt;
        }
        openssl_free_key($privKey);

        return $encrypted ? self::url_safe_base64_encode($encrypted) : null;
    }

    /**
     * 公钥解密
     * @param string $encrypted
     * @return bool|null
     */
    public static function publicDecrypt($encrypted = '')
    {
        $pubKey = self::getPublicKey();

        $partLen = self::getKeyBitDetail($pubKey) / 8;

        $parts = str_split(self::url_safe_base64_decode($encrypted), $partLen);

        $decrypted = '';

        foreach ($parts as $part) {
            openssl_public_decrypt($part, $partDecrypt, $pubKey);
            $decrypted .= $partDecrypt;
        }

        openssl_free_key($pubKey);

        return $decrypted ?: null;
    }

    /**
     * 公钥加密
     * @param string $data
     * @return bool|null
     */
    public static function publicEncrypt($data = '')
    {
        $pubKey = self::getPublicKey();

        $partLen = self::getKeyBitDetail($pubKey) / 8 - 11;

        $parts = str_split($data, $partLen);

        $encrypted = '';

        foreach ($parts as $part) {
            openssl_public_encrypt($part, $partEncrypt, $pubKey);
            $encrypted .= $partEncrypt;
        }

        openssl_free_key($pubKey);

        return $encrypted ? self::url_safe_base64_encode($encrypted) : null;
    }

    /**

     * 私钥解密
     * @param string $encrypted
     * @return bool|null
     */
    public static function privDecrypt($encrypted = '')
    {
        $privKey = self::getPrivateKey();

        $partLen = self::getKeyBitDetail($privKey) / 8;

        $parts = str_split(self::url_safe_base64_decode($encrypted), $partLen);

        $decrypted = '';

        foreach ($parts as $part) {
            openssl_private_decrypt($part, $partDecrypt, $privKey);
            $decrypted .= $partDecrypt;
        }

        openssl_free_key($privKey);

        return $decrypted ?: null;
    }

    /**
     * 私钥签名
     * @param array $data
     * @return null|string
     */
    public static function privSign($data = [])
    {
        $privKey = self::getPrivateKey();

        openssl_sign(self::createLinkStringNew($data), $sign, $privKey, self::RSA_ALGORITHM_SIGN);

        openssl_free_key($privKey);

        return $sign ? self::url_safe_base64_encode($sign) : null;
    }

    /**
     * 公钥验签
     * @param array $data
     * @param string $sign
     * @return int
     */
    public static function publicVerifySign($data = [], $sign = '')
    {
        $pubKey = self::getPublicKey();

        $res = openssl_verify(self::createLinkStringNew($data), self::url_safe_base64_decode($sign), $pubKey, self::RSA_ALGORITHM_SIGN);

        openssl_free_key($pubKey);

        return $res;
    }

    /**
     * 公钥验签(第三方)
     * @param array $data
     * @param string $sign
     * @return int
     */
    public static function publicVerifySignThird($data = [], $sign = '')
    {
        $pubKey = self::getPublicKeyThird();

        $res = openssl_verify(self::createLinkStringNew($data), self::url_safe_base64_decode($sign), $pubKey, self::RSA_ALGORITHM_SIGN);

        openssl_free_key($pubKey);

        return $res;
    }
}

测试输出

$data['name'] = '网红';
$data['age'] = '26';
$data['title'] = '中华人民共和国公民';
$data['english'] = 'litblc.com';


// 私钥加密 公钥解密
$privSec = RSA::privEncrypt(json_encode($data));
echo '私钥加密:' . $privSec . '<br/>';
echo '公钥解密:' . RSA::publicDecrypt($privSec) . '<br/>';
echo '<hr>';

// 公钥加密 私钥解密
$pubSec = RSA::publicEncrypt(json_encode($data));
echo '公钥加密:' . $pubSec . '<br/>';
echo '私钥解密:' . RSA::privDecrypt($pubSec) . '<br/>';
echo '<hr>';

// 验签
$sign = RSA::privSign($data);
echo '私钥签名后:' . $sign . '<br/>';
echo '公钥验签后:' . RSA::publicVerifySign($data, $sign) . '<br/>';

秘钥格式解析

赞赏支持
Responses