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

2023-09-05 16:08:58 浏览数 (2)

函数明细

  • openssl_pkey_get_details返回包含密钥详情的数组,如类型type,加密位数bits等
    • openssl_pkey_get_private获取私钥

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

  • openssl_pkey_get_public获取公钥

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

  • openssl_private_encrypt使用私钥加密数据

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

  • openssl_public_decrypt解密先前由 openssl_private_encrypt() 加密的数据,并且将结果保存至第二个参数中

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

  • openssl_public_encrypt使用公钥加密数据

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

  • openssl_private_decrypt使用私钥解密数据

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

  • openssl_sign生成签名,通过使用与之关联的私钥生成加密数字签名来计算指定的签名
  • openssl_verify验证签名,通过使用关联的公钥验证指定数据的签名是否正确, 通过返回int 1
  • openssl_free_key从内存中释放密钥资源

用例解析

代码语言:javascript复制
/**
 * 通用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",
                "rn"
            ];

            $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",
                "rn"
            ];
            $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",
                "rn"
            ];
            $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;
    }
}

测试输出

代码语言:javascript复制
$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/>';

秘钥格式解析

  • X.509、PKCS文件格式介绍
  • What are the differences between .P7B (PKCS#7) .PFX/.P12 (PKCS#12) .PEM, .DER, .CRT, .CER Certificates?
  • 公钥密码学标准 - wiki
  • SSL 证书格式普及,PEM、CER、JKS、PKCS12
  • RSA公钥、私钥的生成详解,包含Java、PHP、Android、iOS端

0 人点赞