php 的 SM2 加密生成資料和解析資料格式基本為 C1C3C2 格式,JAVA 預設生成的加密資料為 ASN1 格式,PHP 解析可能會有問題,需要轉換下才能使用,所以就寫了一個轉換函式。
下面是轉換函式:
use phpseclib3\File\ASN1;
use phpseclib3\Math\BigInteger;
// sm2 加密資料 ASN1 格式轉換為 c1c3c2 格式
// 需要 composer 安裝 "phpseclib/phpseclib": "^3.0"
function sm2ASN1ToC1C3C2($data)
{
$der = ASN1::decodeBER(base64_decode($data));
$x = "";
$y = "";
if (isset($der[0]["content"][0]["content"])) {
$x = $der[0]["content"][0]["content"]->toHex();
}
if (isset($der[0]["content"][1]["content"])) {
$y = $der[0]["content"][1]["content"]->toHex();
}
$hash = bin2hex($der[0]["content"][2]["content"] ?? "");
$ct = bin2hex($der[0]["content"][3]["content"] ?? "");
$x = str_pad($x, 64, "0", STR_PAD_LEFT);
$y = str_pad($y, 64, "0", STR_PAD_LEFT);
$endata = $x . $y . $hash . $ct;
return $endata;
}
// sm2 加密資料 c1c3c2 格式轉換為 ASN1 格式
function sm2C1C3C2ToASN1($data)
{
$data = hex2bin($data);
$x = substr($data, 0, 32);
$y = substr($data, 32, 32);
$hash = substr($data, 64, 32);
$ct = substr($data, 96);
$map = [
'type' => ASN1::TYPE_SEQUENCE,
'children' => [
'x' => ['type' => ASN1::TYPE_INTEGER],
'y' => ['type' => ASN1::TYPE_INTEGER],
'hash' => ['type' => ASN1::TYPE_OCTET_STRING],
'ct' => ['type' => ASN1::TYPE_OCTET_STRING]
]
];
$derData = [
'x' => new BigInteger($x, 256),
'y' => new BigInteger($y, 256),
'hash' => $hash,
'ct' => $ct
];
$der = ASN1::encodeDER($derData, $map);
return base64_encode($der);
}
測試:
use Rtgm\sm\RtSm2;
// 測試資料
$prikey = "6b4bb2cfe6d4e3499f74cfd05b048b380230d6d7837ebbe1a865850054fbafdb";
$data = "MGwCIQDafQBon8ZrC5fRya4oC6yAgONN6PIWN/I4fk/8wwhGIAIgJgJ/vmW0UmEGmzTp4sgPvigyafQXSU5gsfwLJvE1WYwEIM8nvAb2K7xoK/Q/yi7z/7jzq5XwO3/TtDyvluEiZD0yBAP1Ed4=";
$endata = sm2ASN1ToC1C3C2($data);
// 使用包: "lpilp/guomi": "1.0.*",
$sm2 = new RtSm2();
$res = $sm2->doDecrypt($endata, $prikey);
var_dump($res);
// 輸出: 123
// 轉為 ASN1 格式
$asn1data = sm2C1C3C2ToASN1($endata);
var_dump($asn1data);
解析 ASN1 編碼格式和編碼 ASN1 編碼格式用到了第三方包 phpseclib/phpseclib
, SM2 解密測試用到的包 lpilp/guomi
。
其他 PHP 包解密 SM2 加密資料時請注意測試。
go 的 sm2解密可以檢視 sm2 doc
本作品採用《CC 協議》,轉載必須註明作者和本文連結