自学内容网 自学内容网

嵌入式 GmSSL的SM2,SM3具体使用及对接JAVA的BC库

前言

用嵌入式下的GmSSL库用公钥生成的SM2的密文发送给Java服务端。
GmSSL移植到嵌入式可以参考我上一篇博文

SM3使用

sm3类型于一种hash值密码算法
使用非常简单,下面为详细代码

// SM3 加密
bool GmSSL::SM3Hash(const char *jsonString, char *outString)
{
    unsigned char hash[SM3_DIGEST_LENGTH];
    sm3_digest((const unsigned char *)jsonString, strlen(jsonString), hash);

    // 将哈希值转换为十六进制字符串

    for (int i = 0; i < SM3_DIGEST_LENGTH; ++i)
    {
        snprintf(outString + i * 2, 3, "%02x", hash[i]); // 每个字节生成两个十六进制字符
    }
    return true;
}

SM2的公钥加密


// SM2 公钥加密
bool GmSSL::SM2Encrypt(const char *publicKey, const char *jsonString, char *outString)
{
    SM2_KEY sm2_key;

    uint8_t cbuf[SM2_MAX_CIPHERTEXT_SIZE + 100];

    size_t clen;

    if (sm2_public_key_info_from_string(&sm2_key, publicKey) != 1)
    {
        error_print();
        return 0;
    }

    if (sm2_encrypt(&sm2_key, (const uint8_t *)jsonString, strlen(jsonString), cbuf, &clen) != 1)
    {

        return 0;
    }

    for (int i = 0; i < clen; ++i)
    {
        snprintf(outString + i * 2, 3, "%02x", cbuf[i]); // 每个字节生成两个十六进制字符
    }

    return 1;
}

GmSSL库的sm2_public_key_info_from_string实现

GmSSL没有sm2_public_key_info_from_string函数,
只有sm2_public_key_info_from_pem只能从pem文件中去读取公钥。
但是嵌入式设备连个文件系统都没有,怎么读pem文件呢,可以根据sm2_public_key_info_from_pem函数重写sm2_public_key_info_from_string函数。
根据pem_read函数重写pem_read_from_string函数。


int sm2_public_key_info_from_string(SM2_KEY *a, const char *public_key_str)
{
uint8_t buf[512];
const uint8_t *cp = buf;
size_t len;

if (pem_read_from_string(public_key_str, "PUBLIC KEY", buf, &len, sizeof(buf)) != 1) {
error_print();
return -1;
}
if (sm2_public_key_info_from_der(a, &cp, &len) != 1
|| len > 0) {
return -1;
}
return 1;
}

int pem_read_from_string(const char *from_string, const char *name, uint8_t *data, size_t *datalen, size_t maxlen)
{
    char line[80] = {0};
    char begin_line[80];
    char end_line[80];
    int len;
    BASE64_CTX ctx;

    snprintf(begin_line, sizeof(begin_line), "-----BEGIN %s-----\n", name);
    snprintf(end_line, sizeof(end_line), "-----END %s-----\n", name);

    if (from_string == NULL)
    {
        return 0;
    }

    *datalen = 0;

    base64_decode_init(&ctx);

    strcpy(line, from_string);

    base64_decode_update(&ctx, (uint8_t *)line, strlen(line), data, &len);
 
    *datalen += len;

    base64_decode_finish(&ctx, data, &len);
    *datalen += len;
    return 1;
}

对接JAVA的BC库

用GMSSL V3.0.0 SM2 sm2_encrypt加密生成的密文,用第三方GM工具和JAVA的BC库解密不了,
只能用自己能解
测试网址
SM2在线解密工具
这是因为GMSSL 的SM2加密输入的格式和JAVA的不一样,加一层DER封装
默认格式为C1C3C2
C1,为point.x和point.y拼接而成,
C3为 hash值即SM3的值
C2为ciphertext
GMSSL 的SM2通过了DER的序列化

    SM2=: 30819402206DFE2BA0FF33E37B8C7D7DCFE76039DACF32C704D20B2198F8AEB5F857252604022100BB8749515C51FE1FFD72F8B895A7C572DFAAB0A71426FDA11CAF277FAA00C6C404204FF7CCB2BBC8C912C43F7845D35D8FA0F3EEAFAEAD63D513949C45624BAFBAAB042B95D2827D56F27C38A92BFE7F0EDB47F497BB85F9DE9E335174C7F30BC747C06ABC13FBCBFE56A36422570D
    SM2_INFO=
        XCoordinate: 6DFE2BA0FF33E37B8C7D7DCFE76039DACF32C704D20B2198F8AEB5F857252604
        YCoordinate: BB8749515C51FE1FFD72F8B895A7C572DFAAB0A71426FDA11CAF277FAA00C6C4
        HASH: 4FF7CCB2BBC8C912C43F7845D35D8FA0F3EEAFAEAD63D513949C45624BAFBAAB
        CipherText: 95D2827D56F27C38A92BFE7F0EDB47F497BB85F9DE9E335174C7F30BC747C06ABC13FBCBFE56A36422570D

GMSSL 的密文输出
30819402206DFE2BA0FF33E37B8C7D7DCFE76039DACF32C704D20B2198F8AEB5F857252604022100BB8749515C51FE1FFD72F8B895A7C572DFAAB0A71426FDA11CAF277FAA00C6C404204FF7CCB2BBC8C912C43F7845D35D8FA0F3EEAFAEAD63D513949C45624BAFBAAB042B95D2827D56F27C38A92BFE7F0EDB47F497BB85F9DE9E335174C7F30BC747C06ABC13FBCBFE56A36422570D
可以分解为

数据说明备注
308194序列头
02数据的类型int为02
20 x的长度32个整形为0x20
6DFE2BA0FF33E37B8C7D7DCFE76039DACF32C704D20B2198F8AEB5F857252604x
02数据的类型int为02
21y的长度
00BB8749515C51FE1FFD72F8B895A7C572DFAAB0A71426FDA11CAF277FAA00C6C4y的内容由于y的值不能为负值,而BB是一个负值,所以在前面加上00
04数据的类型字符串为04
20数据长度hash的长度为0x20
4FF7CCB2BBC8C912C43F7845D35D8FA0F3EEAFAEAD63D513949C45624BAFBAABSM3的值
04数据的类型字符串为04
2B数据长度CipherText的长度为0x2B
95D2827D56F27C38A92BFE7F0EDB47F497BB85F9DE9E335174C7F30BC747C06ABC13FBCBFE56A36422570DCipherText的内容

JAVA的BC库需要的数据不需要数据长度和数据类型的前缀。

所以需要手动拼接一下。
C1+C3+C2就可以了
6DFE2BA0FF33E37B8C7D7DCFE76039DACF32C704D20B2198F8AEB5F85725260400BB8749515C51FE1FFD72F8B895A7C572DFAAB0A71426FDA11CAF277FAA00C6C495D2827D56F27C38A92BFE7F0EDB47F497BB85F9DE9E335174C7F30BC747C06ABC13FBCBFE56A36422570D
就可以解析出来明文了
在这里插入图片描述

具体修改代码

修改src/sm2_lib.c


int sm2_ciphertext_to_der(const SM2_CIPHERTEXT *C, uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (!C) {
return 0;
}
memcpy(*out,C->point.x,32);
memcpy((*out)+32,C->point.y,32);
memcpy((*out)+64,C->hash,32);
memcpy((*out)+96,C->ciphertext,C->ciphertext_size);
*outlen = 96+C->ciphertext_size;
//if (asn1_integer_to_der(C->point.x, 32, NULL, &len) != 1
// || asn1_integer_to_der(C->point.y, 32, NULL, &len) != 1
// || asn1_octet_string_to_der(C->hash, 32, NULL, &len) != 1
// || asn1_octet_string_to_der(C->ciphertext, C->ciphertext_size, NULL, &len) != 1
// || asn1_sequence_header_to_der(len, out, outlen) != 1
// || asn1_integer_to_der(C->point.x, 32, out, outlen) != 1
// || asn1_integer_to_der(C->point.y, 32, out, outlen) != 1
// || asn1_octet_string_to_der(C->hash, 32, out, outlen) != 1
// || asn1_octet_string_to_der(C->ciphertext, C->ciphertext_size, out, outlen) != 1) {
// error_print();
// return -1;
// }
return 1;
}


原文地址:https://blog.csdn.net/q472599451/article/details/143023413

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!