yebin-yu / openssh_with_ukey

Other
0 stars 0 forks source link

Ukey的pubkey转换成openssh支持的公钥 #8

Closed yebin-yu closed 5 months ago

yebin-yu commented 5 months ago

获取sm2公钥的坐标点,即x和y

获取过程1 - demo获取

UKEY - PubKey

x: 000a3787ede1dbc4e1c389c9ae2137cb123b7594e4ae2dd859b89e1ff9546521
y: 0981e2c635fb7295728aa5bccb18c49b71bf52eec226abc9152153f838fcbce

print code

void dumpBytesHex(const char *name, unsigned char *bytes, size_t bytesLen)
{
    const char *outName = (name != NULL) ? name : "Default:";
    printf("%s", outName);

    for (int i = 0; i < bytesLen; i++) {
        printf("%x", bytes[i]);
    }

    printf("\n");
}

    printf("BitLen: %u\n", blob->BitLen);

    dumpBytesHex("x: ", blob->XCoordinate, (ECC_MAX_XCOORDINATE_BITS_LEN/8));
    dumpBytesHex("y: ", blob->YCoordinate, (ECC_MAX_XCOORDINATE_BITS_LEN/8));

    dumpBytesHex("r: ", stSign.r, sizeof(stSign.r));
    dumpBytesHex("s: ", stSign.s, sizeof(stSign.s));

打印如下

BitLen: 256
x: 00000000000000000000000000000000a3787ede1dbc4e1c389c9ae2137cb123b7594e4ae2dd859b89e1ff9546521
y: 00000000000000000000000000000000981e2c635fb7295728aa5bccb18c49b71bf52eec226abc9152153f838fcbce

获取过程2 - gdb获取

获取内容

x:de7e78a3e1c40b1d9a9c08c3b17c13e24e59b72385dde24a1f0e899b216554f9
y:632c1e985729b75fbc05aa2809c418cb2ef51bb7bc6a22ec3f155291cecb8f83

获取公钥的函数,其中buf指向RSA公钥结构(RSAPUBLICKEYBLOB)或者ECC公钥结构(ECCPUBLICKEYBLOB),如果此参数为NULL时,由pulBlobLen返回pbBlob的长度

    BYTE buf[132] = {0};
    ULONG bufLen = sizeof(buf);
    ulRslt = SKF_ExportPublicKey(hcontainer, TRUE, buf, &bufLen);

代码中实际用到的是 ECCPUBLICKEYBLOB

#define ECC_MAX_XCOORDINATE_BITS_LEN 512 
#define ECC_MAX_YCOORDINATE_BITS_LEN 512

typedef struct Struct_ECCPUBLICKEYBLOB{
    UINT32    BitLen;
    BYTE    XCoordinate[ECC_MAX_XCOORDINATE_BITS_LEN/8];
    BYTE    YCoordinate[ECC_MAX_YCOORDINATE_BITS_LEN/8];
}ECCPUBLICKEYBLOB, *PECCPUBLICKEYBLOB;

对buf的内容进行打印

(gdb) print &buf
$5 = (BYTE (*)[132]) 0x7fffffffde90
(gdb) x/8 0x7fffffffde90
0x7fffffffde90: 0x00000100      0x00000000      0x00000000      0x00000000
0x7fffffffdea0: 0x00000000      0x00000000      0x00000000      0x00000000
(gdb) x/16 0x7fffffffde98
0x7fffffffde98: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdea8: 0x00000000      0x00000000      0x00000000      0xde7e78a3
0x7fffffffdeb8: 0xe1c40b1d      0x9a9c08c3      0xb17c13e2      0x4e59b723
0x7fffffffdec8: 0x85dde24a      0x1f0e899b      0x216554f9      0x00000000
(gdb) x/16 0x7fffffffde94
0x7fffffffde94: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdea4: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdeb4: 0xde7e78a3      0xe1c40b1d      0x9a9c08c3      0xb17c13e2
0x7fffffffdec4: 0x4e59b723      0x85dde24a      0x1f0e899b      0x216554f9
(gdb) x/16 0x7fffffffded4
0x7fffffffded4: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdee4: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdef4: 0x632c1e98      0x5729b75f      0xbc05aa28      0x09c418cb
0x7fffffffdf04: 0x2ef51bb7      0xbc6a22ec      0x3f155291      0xcecb8f83
yebin-yu commented 5 months ago

sm2 公钥转换

信息安全技术 SM2密码算法使用规范:

image

最终得到的python脚本为:

res = base64.b64encode(bytes.fromhex("00000003736d3200000003736d3200000041" + "04" +
                                     'de7e78a3e1c40b1d9a9c08c3b17c13e24e59b72385dde24a1f0e899b216554f9' +
                                     '632c1e985729b75fbc05aa2809c418cb2ef51bb7bc6a22ec3f155291cecb8f83'))

结果为:

AAAAA3NtMgAAAANzbTIAAABBBN5+eKPhxAsdmpwIw7F8E+JOWbcjhd3iSh8OiZshZVT5YywemFcpt1+8BaooCcQYyy71G7e8aiLsPxVSkc7Lj4M=

可以看到,和使用ssh-keygen生成的秘钥大小一致,且前面部分都一样

sm2 AAAAA3NtMgAAAANzbTIAAABBBDFm8NOcCa/yaI7pVJuGpqbUuToMLREOl5+WhWYRV8FD/DDtRFPMiIdvWWHEG+71ZRc4Hagx93B3H05X/thNiOE= root@yebinyu

PS.1 SM2 固定格式

前面的AAAAA3NtMgAAAANzbTIAAABBB,即00000003736d3200000003736d3200000041,解释如下

AAAA: 固定前缀,表示密钥类型为SM2。
3: 一个字节,表示SM2公钥版本号为3。
NtMgAAAANzbTIAAABBB: 固定字符串,用于标识SM2公钥。
yebin-yu commented 5 months ago

测试

前置准备 - OK

完成将公钥转成openssh需要的sshkey结构体。 用ssh-keygen生成的公钥测试:测试通过

测试1 使用demo中得到的x和y

得到的公钥为:

AAAAA3NtMgAAAANzbTIAAABBBAAKN4ft4dvE4cOJya4hN8sSO3WU5K4t2Fm4nh/5VGUhAJgeLGNftylXKKpbzLGMSbcb9S7sImq8kVIVP4OPy84=

直接在OPENSSL的函数中解析失败,怀疑是公钥格式问题,从demo中打印的x和y来看,长度不够需要补0

测试2 对ukey中返回的公钥直接读取16进制值。

发现问题,转换失败

Breakpoint 2, sshkey_ec_validate_public (group=0x5b3225f48950, public=public@entry=0x5b3225f49030) at sshkey.c:2601

2601    {
(gdb) 
2618            if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
(gdb) 
2623            if (EC_POINT_is_at_infinity(group, public))
(gdb) 
2626            if ((x = BN_new()) == NULL ||
(gdb) 
2635            if (EC_GROUP_get_order(group, order, NULL) != 1 ||
(gdb) 
2641            if (BN_num_bits(x) <= BN_num_bits(order) / 2 ||
(gdb) 
2646            if ((nq = EC_POINT_new(group)) == NULL) {
(gdb) 
2650            if (EC_POINT_mul(group, nq, NULL, public, order, NULL) != 1) {
(gdb) 
2654            if (EC_POINT_is_at_infinity(group, nq) != 1)
(gdb) 
2666            BN_clear_free(x);
(gdb) 
2667            BN_clear_free(y);
(gdb) 
2668            BN_clear_free(order);
(gdb) 
2669            BN_clear_free(tmp);
(gdb) print ret
$1 = -20
(gdb) 

代码在sshkey.c,显示该坐标点不是一个无穷点

int
sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
{
    // ......
    if (EC_POINT_is_at_infinity(group, nq) != 1)
        goto out;
    // ......

测试3:转换大小端序

查看openssl的源码,发现要求的数据默认是大端序,猜测可能是大小端序问题,测试一下

BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
{
    return bin2bn(s, len, ret, BIG, UNSIGNED);
}

所以将公钥转换大小序,python代码如下

def swap_endian(hex_str):
  if not isinstance(hex_str, str) or not all(x in "0123456789abcdefABCDEF" for x in hex_str):
    raise TypeError("输入数据必须为有效的 16 进制字符串")

  data = bytearray.fromhex(hex_str)
  data.reverse()
  return data.hex()

res = base64.b64encode(bytes.fromhex("00000003736d3200000003736d3200000041" + "04" +
                                     swap_endian('de7e78a3e1c40b1d9a9c08c3b17c13e24e59b72385dde24a1f0e899b216554f9') +
                                     swap_endian('632c1e985729b75fbc05aa2809c418cb2ef51bb7bc6a22ec3f155291cecb8f83')))

结果为:

AAAAA3NtMgAAAANzbTIAAABBBPlUZSGbiQ4fSuLdhSO3WU7iE3yxwwicmh0LxOGjeH7eg4/LzpFSFT/sImq8txv1LssYxAkoqgW8X7cpV5geLGM=

还是失败