lpilp / phpsm2sm3sm4

php版本,支持国密SM2的签名算法,非对称加解密,SM3的hash, SM4的对称加解密
329 stars 77 forks source link

密钥 base64 转 hex 问题 #79

Closed nirvana72 closed 4 months ago

nirvana72 commented 4 months ago

java开发中, 一般用到hutool工具中自带一个sm2整合工具, 使用的是base64格式的密钥

我在php中使用这个库时, 代码中要求的是 hex格式的密钥

所以需要把base64 转 hex

代码中提示使用 bin2hex(base64_decode($publicKey)) 可以转

但是我转出来的hex密钥却报 key format error

MFKWEWYHKOZIZJOCAQYIKOECZ1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxVAWPHV6HXWEgTZre60709FTO+ 6Y5NCIOEY8p4a0+P3qaoghByOVLYC/RDPOIRQ==

bin2hex(base64_decode($publicKey)) 后为

3059301306072a8643ce3d020106082xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxd5f103d57a1dts5f1c04813ceb7asea8e8f454e8ha6393428641829e1a43e31739a20856072d152d80bf43a4e2145

源码 ../RtSm2.php 第348行 _getKeyXY 方法中, 判断密钥格式 长度128, 或130以04开头, 否则都报format error, 明显我转出来的不能用

然后我看库说明 接收的是 base64 短串, 我不太懂 这个 “短串” 是什么意思, 难道我用的是长串

lpilp commented 4 months ago

我指的是 就是明文密钥的base64, 你这个是asn1过的base64, 就是文件密钥里的那种,可以解开,或者用文件密钥的方式处理

nirvana72 commented 4 months ago

我用另一个issues 中您提到的方法, 用外部文件方式base64转hex

-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEUwulPOFFcaAevSmMgWHV/wPdV6HfXxwEgTzrel6o7o9FTo+6Y5NCi0EY8p4aQ+P3qaoghrBy0VLYC/RDpOIRRQ== -----END PUBLIC KEY-----

`$publickeyFile = "sm2pri.pem";

  $adapter = RtEccFactory::getAdapter();
  $sigSerializer = new DerSignatureSerializer();
  $keyData = file_get_contents( $publickeyFile );

  $derSerializer = new DerPublicKeySerializer( $adapter );
  $pemSerializer = new PemPublicKeySerializer( $derSerializer );
  $key = $pemSerializer->parse( $keyData );
  $pubKeyX = $this->decHex( $key->getPoint()->getX() );
  $pubKeyY = $this->decHex( $key->getPoint()->getY() );

  $publickey = $pubKeyX.$pubKeyY;`

转出的公钥可以使用并加密成功

但我以同样的方式转私钥的时候, 却不成功

-----BEGIN EC PRIVATE KEY----- MIGTAgEAMBMGByqGSM49AgEGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXpjk0KLQRjynhpD4/epqiCGsHLRUtgL9EOk4hFF== -----END EC PRIVATE KEY-----

/vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php - line:51 报错 Invalid data.

lpilp commented 4 months ago

私钥与公钥的格式是不一样的, 不能用一样的代码解,可用 asn1组件单独解开

nirvana72 commented 4 months ago

代码库中, /test/data/sm2.pem 私钥长度是 164 位, 我用

\Rtgm\util\MyAsn1::decode_file($privatekeyFile);

    $adapter = RtEccFactory::getAdapter();
    $privatekeyFile = "sm2pri.pem";
    $pemSerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($adapter));
    $keyData = file_get_contents($privatekeyFile);
    $key = $pemSerializer->parse($keyData);

都可以解出结果, 虽然目前还不知道哪个是16位私钥

但是我的java对接方给我提供的base64位私钥是

MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgFtsitF4zKnGftxjR6U9HqVVEPk24N+o3rLD+jhYFnr2gCgYIxxxBgi2hRANCAARTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSBPOt6Xqjuj0VOj7pjk0KLQRjynhpD4/epqiCGsHLRUtgL9EOk4hFF==

用这个就解不出来

比对了一下, 我的对接方给的私钥是 202位的, 好像记得base64编码长度都是3的倍数,

是对方给错字符串了, 还是我的方法不对

nirvana72 commented 4 months ago

MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgFtsitF4zKnGftxjR6U9HqVVEPk24N+o3rLD+jhYFnr2gCgYIKoEcz1UBgi2hxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyBYdX/A91Xod9fHASBPOt6Xqjuj0VOj7pjk0KLQRjynhpD4/epqiCGsHLRUtgL9EOk4hFF==

上面这个 202位长度的私钥, 用

\Rtgm\util\MyAsn1::decode_file($privatekeyFile);

解出来是

array(3) { [0] => string(2) "00" [1] => array(2) { [0] => string(11) "ecPublicKey" [1] => string(3) "sm2" } [2] => string(242) "3077020101042016DB22B45E332A719FB718D1E94F47A955443E4DB837EA37ACB0FE8E16059EBDA00A06082A811CCFxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxA01EBD298C8161D5FF03DD57A1DF5F1C04813CEB7A5EA8EE8F454E8FBA6393428B4118F29E1A43E3F7A9AA2086B072D152D80BF443A4E21145" }

这个字符串明显太长了

lpilp commented 4 months ago

[2] => string(242) "3077020101042016DB22B45E332A719FB718D1E94F47A955443E4DB837EA37ACB0FE8E16059EBDA00A06082A811CCF550182xxxxxxxxxxxxxxxxxC8161D5FF03DD57A1DF5F1C04813CEB7A5EA8EE8F454E8FBA6393428B4118F29E1A43E3F7A9AA2086B072D152D80BF443A4E21145"
}

再asn1 一次就行了,我那测试的只解一层,私钥这个要再解一次,这串里有公钥 + 私钥 还有一个简单的方法就是用新版本的 openssl1.1.1w可能现在版本更高,支持国密了, 用openssl命令行直接就可以解出来了

还有: 不知道你这个是不是测试用的,如果是正式用的就不要全文发出来,不安全,我已把一些段给你隐了

nirvana72 commented 4 months ago

感谢大佬, 公私钥都顺利转成16位了, 并与java 方的对上了

private function convertBase64PubKey2Hex(string $base64PubKey) {
        $key = \Rtgm\util\MyAsn1::decode($base64PubKey, 'base64');
        $key = $key[1];
        if (substr($key, 0, 2) === '04') {
            $key = substr($key, 2);
        }
        return $key;
    }
private function convertBase64PriKey2Hex(string $base6PriKey) {
    $key = \Rtgm\util\MyAsn1::decode($base6PriKey, 'base64');
    $key = \Rtgm\util\MyAsn1::decode($key[2], 'hex');
    $key = $key[1];
    return $key;
}
lpilp commented 4 months ago

^-^,好使就好