PHP 转换 SM2 加密数据 ASN1 编码格式为 C1C3C2 格式数据

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 协议》,转载必须注明作者和本文链接
我们走了很远的路,终于发现,路已经不止一条
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3

C1C3C2 转成 ASN1如何转啊,大佬你知道吗

2个月前 评论
deatil (楼主) 2个月前
deatil (楼主) 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!