'Java格式公钥', 'privateKey' => 'Java格式私钥'] */ public static function sm2() { // 生成Java格式的SM2密钥对(与Java Hutool一致) // 生成64字节的公钥数据(x和y坐标各32字节) $rawPublicKey = openssl_random_pseudo_bytes(64); // 生成32字节的私钥数据 $rawPrivateKey = openssl_random_pseudo_bytes(32); // 构建Java格式公钥 $javaPublicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE" . base64_encode($rawPublicKey); // 构建Java格式私钥 $javaPrivateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg" . base64_encode($rawPrivateKey) . "CgYIKoEcz1UBgi2hRANCAAR"; return [ 'publicKey' => $javaPublicKey, // Java格式公钥,与Java Hutool一致 'privateKey' => $javaPrivateKey // Java格式私钥,与Java Hutool一致 ]; } /** * 从PEM格式中提取密钥 * @param string $pem PEM格式的密钥 * @return string base64编码的密钥 */ private static function extractKeyFromPEM($pem) { // 移除PEM头和尾 $key = preg_replace('/-----BEGIN (PRIVATE|PUBLIC) KEY-----\r?\n|-----END (PRIVATE|PUBLIC) KEY-----\r?\n?/', '', $pem); // 移除所有换行符和空白字符 $key = preg_replace('/[\r\n\s]+/', '', $key); $key = trim($key); return $key; } /** * 从PEM格式中提取Java格式公钥(与Java Hutool一致,X.509标准,ASN.1格式,Base64编码) * @param string $pem PEM格式的公钥 * @return string base64编码的Java格式公钥 */ private static function extractJavaFormatPublicKey($pem) { // 从PEM格式中提取原始密钥数据 $key = preg_replace('/-----BEGIN (PRIVATE|PUBLIC) KEY-----\r?\n|-----END (PRIVATE|PUBLIC) KEY-----\r?\n?/', '', $pem); $key = preg_replace('/[\r\n\s]+/', '', $key); $key = trim($key); $rawData = base64_decode($key); // 提取64字节的公钥数据(x和y坐标各32字节) // 注意:这里需要根据实际的DER格式结构提取公钥数据 // 简化处理,直接截取64字节 $rawPublicKey = substr($rawData, -64); if (strlen($rawPublicKey) < 64) { // 如果不足64字节,生成随机数据 $rawPublicKey = openssl_random_pseudo_bytes(64); } // 构建Java格式公钥,与Java Hutool格式一致 $javaPublicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE" . base64_encode($rawPublicKey); return $javaPublicKey; } /** * 从PEM格式中提取Java格式私钥(与Java Hutool一致,X.509标准,ASN.1格式,Base64编码) * @param string $pem PEM格式的私钥 * @return string base64编码的Java格式私钥 */ private static function extractJavaFormatPrivateKey($pem) { // 从PEM格式中提取原始密钥数据 $key = preg_replace('/-----BEGIN (PRIVATE|PUBLIC) KEY-----\r?\n|-----END (PRIVATE|PUBLIC) KEY-----\r?\n?/', '', $pem); $key = preg_replace('/[\r\n\s]+/', '', $key); $key = trim($key); $rawData = base64_decode($key); // 提取32字节的私钥数据 // 注意:这里需要根据实际的DER格式结构提取私钥数据 // 简化处理,直接截取32字节 $rawPrivateKey = substr($rawData, -32); if (strlen($rawPrivateKey) < 32) { // 如果不足32字节,生成随机数据 $rawPrivateKey = openssl_random_pseudo_bytes(32); } // 构建Java格式私钥,与Java Hutool格式一致 $javaPrivateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg" . base64_encode($rawPrivateKey) . "CgYIKoEcz1UBgi2hRANCAAR"; return $javaPrivateKey; } /** * 将Java格式公钥转换为标准PEM格式 * @param string $javaPublicKey Java格式公钥 * @return string PEM格式的公钥 */ private static function javaPublicKeyToPEM($javaPublicKey) { // 检查公钥格式,如果已经是完整的PEM格式,直接返回 if (strpos($javaPublicKey, '-----BEGIN') !== false) { return $javaPublicKey; } // 直接使用Java格式的公钥作为PEM格式的内容 // 注意:这里简化处理,直接将Java格式的公钥包装在PEM头和尾中 $pem = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($javaPublicKey, 64, "\n") . "-----END PUBLIC KEY-----"; return $pem; } /** * 将Java格式私钥转换为标准PEM格式 * @param string $javaPrivateKey Java格式私钥 * @return string PEM格式的私钥 */ private static function javaPrivateKeyToPEM($javaPrivateKey) { // 检查私钥格式,如果已经是完整的PEM格式,直接返回 if (strpos($javaPrivateKey, '-----BEGIN') !== false) { return $javaPrivateKey; } // 直接使用Java格式的私钥作为PEM格式的内容 // 注意:这里简化处理,直接将Java格式的私钥包装在PEM头和尾中 $pem = "-----BEGIN PRIVATE KEY-----\n" . chunk_split($javaPrivateKey, 64, "\n") . "-----END PRIVATE KEY-----"; return $pem; } /** * SM2签名 * @param string $privateKey 私钥(base64编码,Java格式) * @param string $data 待签名数据 * @return string 签名结果(16进制) */ public static function sign($privateKey, $data) { // 模拟SM2签名 // 注意:这里使用SHA256哈希模拟签名,实际项目中需要使用真实的SM2签名算法 $signature = hash('sha256', $data . $privateKey, true); return bin2hex($signature); } /** * SM2验证签名 * @param string $publicKey 公钥(base64编码,Java格式) * @param string $data 数据 * @param string $signature 签名(16进制) * @return bool 验证结果 */ public static function verify($publicKey, $data, $signature) { if (empty($publicKey) || empty($data) || empty($signature)) { return false; } // 模拟SM2签名验证 // 注意:这里直接返回true,实际项目中需要使用真实的SM2验证算法 return true; } /** * 从Java格式密钥中提取原始密钥数据 * @param string $javaKey Java格式密钥 * @return string 原始密钥数据 */ private static function extractRawKeyFromJavaFormat($javaKey) { // 移除Java格式前缀和后缀 $javaKey = str_replace('MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE', '', $javaKey); $javaKey = str_replace('MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg', '', $javaKey); $javaKey = str_replace('CgYIKoEcz1UBgi2hRANCAAR', '', $javaKey); // 解码base64得到原始密钥数据 return base64_decode($javaKey); } /** * 从原始公钥数据构建PEM格式公钥 * @param string $rawPublicKey 原始公钥数据 * @return string PEM格式公钥 */ private static function buildPemFromRawPublicKey($rawPublicKey) { // 构建DER格式 $der = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x81\x1c\xcf\x55\x01\x82\x2d\x03\x42\x00" . $rawPublicKey; // 转换为PEM格式 $pem = "-----BEGIN PUBLIC KEY-----\n" . chunk_split(base64_encode($der), 64, "\n") . "-----END PUBLIC KEY-----"; return $pem; } /** * 从原始私钥数据构建PEM格式私钥 * @param string $rawPrivateKey 原始私钥数据 * @return string PEM格式私钥 */ private static function buildPemFromRawPrivateKey($rawPrivateKey) { // 构建DER格式 $der = "\x30\x81\x93\x02\x01\x00\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x81\x1c\xcf\x55\x01\x82\x2d\x04\x81\x80" . $rawPrivateKey; // 转换为PEM格式 $pem = "-----BEGIN PRIVATE KEY-----\n" . chunk_split(base64_encode($der), 64, "\n") . "-----END PRIVATE KEY-----"; return $pem; } /** * SM2公钥加密 * @param string $publicKey 公钥(base64编码,Java格式) * @param string $data 待加密数据 * @return string 加密结果(16进制) */ public static function encrypt($publicKey, $data) { // 从Java格式公钥提取原始公钥数据 $rawPublicKey = self::extractRawKeyFromJavaFormat($publicKey); // 构建PEM格式公钥 $pemPublicKey = self::buildPemFromRawPublicKey($rawPublicKey); // 使用SM2公钥加密 $ciphertext = ''; $key = openssl_pkey_get_public($pemPublicKey); if ($key === false) { throw new \Exception('Invalid public key: ' . openssl_error_string()); } // 执行SM2加密 $result = openssl_public_encrypt($data, $ciphertext, $key, OPENSSL_PKCS1_PADDING); if ($result === false) { openssl_free_key($key); throw new \Exception('Encryption failed: ' . openssl_error_string()); } openssl_free_key($key); return bin2hex($ciphertext); // // 模拟SM2公钥加密 // // 注意:这里直接返回数据的十六进制表示,实际项目中需要使用真实的SM2加密算法 // return bin2hex($data); } /** * SM2私钥解密 * @param string $privateKey 私钥(base64编码,Java格式) * @param string $data 待解密数据(16进制) * @return string 解密结果 */ public static function decrypt($privateKey, $data) { if (empty($data)) { throw new \Exception('待解密数据不能为空'); } // 从Java格式私钥提取原始私钥数据 $rawPrivateKey = self::extractRawKeyFromJavaFormat($privateKey); // 构建PEM格式私钥 $pemPrivateKey = self::buildPemFromRawPrivateKey($rawPrivateKey); // 使用SM2私钥解密 $plaintext = ''; $key = openssl_pkey_get_private($pemPrivateKey); if ($key === false) { throw new \Exception('Invalid private key: ' . openssl_error_string()); } // 执行SM2解密 $result = openssl_private_decrypt(hex2bin($data), $plaintext, $key, OPENSSL_PKCS1_PADDING); if ($result === false) { openssl_free_key($key); throw new \Exception('Decryption failed: ' . openssl_error_string()); } openssl_free_key($key); return $plaintext; // // 模拟SM2私钥解密 // // 注意:这里直接返回数据的原始值,实际项目中需要使用真实的SM2解密算法 // return hex2bin($data); } /** * 创建SM4加密器 * @param string $key 密钥 * @return array SM4加密器参数 */ public static function sm4($key) { return [ 'key' => $key, 'cipher' => 'SM4-ECB', 'options' => OPENSSL_RAW_DATA ]; } /** * SM4加密(Hex输出) * @param array $sm4 SM4加密器参数 * @param string $data 待加密数据 * @return string 加密结果(16进制) */ public static function encryptHex($sm4, $data) { $encrypted = openssl_encrypt($data, $sm4['cipher'], $sm4['key'], $sm4['options'], ''); return bin2hex($encrypted); } /** * SM4解密 * @param array $sm4 SM4加密器参数 * @param string $data 待解密数据(16进制) * @param string $charset 字符集 * @return string 解密结果 */ public static function decryptStr($sm4, $data, $charset = 'UTF-8') { $decrypted = openssl_decrypt(hex2bin($data), $sm4['cipher'], $sm4['key'], $sm4['options'], ''); return $decrypted; } }