libgcrypt-1.10.2编程-计算摘要-SM3算法带公钥


前言

SM2国密算法计算签名值之前,需要进行SM3摘要计算,而SM3摘要计算有2个预处理步骤,本文主要描述SM3带公钥摘要如何计算


一、SM3摘要说明

与国际摘要算法,如SHA1、SHA256、SHA512不同,在进行sm2签名时,计算sm3摘要,要进行预处理,其中用户的公钥要参与计算,这一点与国际签名算法不同,比较另类。而且网上很少有对该过程进行详细描述,想查找相关的资料或者代码都挺麻烦的,因此在这篇博文中进行详细说明

SM2签名时摘要计算过程可以参考国家标准《GBT 35276-2017 信息安全技术 SM2密码算法使用规范》第8章节,截图如下


在这里插入图片描述


如上图所示,预处理分为2个步骤

1.计算Z值。计算Z值需要用户公钥参与,输入参数1为用户身份标识ID,在标准规范后面有说明,无特殊约定情况下,固定为"1234567812345678"; 输入参数2为用户公钥,如上图中的Xa、Ya。至于图中其他参数,a、b、Xg、Yg是ECC椭圆曲线固定参数,后文代码中进行说明。最终计算结果以Z表示,为固定32字节的摘要值

2.计算最终的杂凑值H。使用Z值和原文数据M计算SM3摘要

二、使用C/C++计算SM3摘要

1.代码

#include <gcrypt.h>
#include <iostream>
#include "cppcodec/base64_rfc4648.hpp"
#include "cppcodec/hex_upper.hpp"
#include <algorithm>

using base64 = cppcodec::base64_rfc4648;
using hex = cppcodec::hex_upper;

int main(int argc, char** argv){
    gcry_md_algos md_algos = GCRY_MD_SM3;
    int sm3Len = gcry_md_get_algo_dlen(md_algos);
    assert(sm3Len == 32);

    gcry_md_hd_t hd = nullptr;
    gcry_error_t error = gcry_md_open(&hd, md_algos, 0);
    if (error != GPG_ERR_NO_ERROR) {
        fprintf(stderr, "gcry_md_open error\n");
        exit(1);
    }

    const char* userId = "1234567812345678";
    const char* a = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC";
    const char* b = "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93";
    const char* Gx = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
    const char* Gy = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
    const char* userX = "E2A0EAE784EE407D6F56A0BD3267D373EAC550E7ACE7588D1E95DDB28FA64D0E";
    const char* userY = "F78F7721589D64B53D4DFAA66C3C97851C3C1F2A11AEEBE491D10102FC081DEF";

    // 计算预处理1中entl的值: 由2字节表示的 userId 的比特长度
    int bitLen = strlen(userId) * 8;
    int intBytes = 4, cutIndex = 2;
    uint8_t* t = (uint8_t*) &bitLen;
    std::vector<uint8_t> entl_;
    for (int j = 0; j < intBytes; ++j) {
        entl_.push_back(t[j]);
    }
    // 字节数组元素逆序,因为产生的字节数组顺序是反的
    std::reverse(entl_.begin(), entl_.end());
    std::vector<uint8_t> entl(entl_.begin() + cutIndex, entl_.end());

    const std::vector<uint8_t>& aVector = hex::decode(a, strlen(a));
    const std::vector<uint8_t>& bVector = hex::decode(b, strlen(b));
    const std::vector<uint8_t>& GxVector = hex::decode(Gx, strlen(Gx));
    const std::vector<uint8_t>& GyVector = hex::decode(Gy, strlen(Gy));
    const std::vector<uint8_t>& userXVector = hex::decode(userX, strlen(userX));
    const std::vector<uint8_t>& userYVector = hex::decode(userY, strlen(userY));
    gcry_md_write(hd, entl.data(), entl.size());
    gcry_md_write(hd, userId, strlen(userId));
    gcry_md_write(hd, aVector.data(), aVector.size());
    gcry_md_write(hd, bVector.data(), bVector.size());
    gcry_md_write(hd, GxVector.data(), GxVector.size());
    gcry_md_write(hd, GyVector.data(), GyVector.size());
    gcry_md_write(hd, userXVector.data(), userXVector.size());
    gcry_md_write(hd, userYVector.data(), userYVector.size());
    uint8_t* z = gcry_md_read(hd, md_algos);
    // 定义字节数组,使用内存拷贝复制z的值。不能在计算SM3(Z||M)时直接使用z,而要使用zBytes
    uint8_t zBytes[sm3Len];
    memcpy(zBytes, z, sm3Len);
    std::cout << "Z: " << hex::encode(z, sm3Len) << std::endl;

    gcry_md_reset(hd);
    const char* buffer = "1234567890";
    size_t length = strlen(buffer);
    gcry_md_write(hd, zBytes, sm3Len);
    gcry_md_write(hd, buffer, length);
    uint8_t* hash = gcry_md_read(hd, md_algos);
    std::cout << "十六进制输出: " << hex::encode(hash, sm3Len) << std::endl;

    gcry_md_close(hd);
    return 0;
}

2.说明

SM2算法椭圆曲线方程: y 2 = x 3 + a x + b y^2= x^3 + ax + b y2=x3+ax+b

  1. 22行参数为用户身份标识,即规范中的ID参数,固定值 1234567812345678
  2. 23行a为方程中的系数a,以十六进制字符串形式赋值给a变量,固定值 FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
  3. 24行b为方程中的系数b,以十六进制字符串形式赋值给b变量,固定值 28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
  4. 25行Gx为椭圆曲线的基点x坐标,以十六进制字符串形式赋值给Gx变量,固定值 32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
  5. 26行Gy为椭圆曲线的基点y坐标,以十六进制字符串形式赋值给Gx变量,固定值 BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
  6. 27、28两行是用户的公钥x、y坐标,也以十六进制字符串形式赋值给相应变量。这两个参数应该通过参数外传进来

上述代码经过测试,计算结果与java的bouncycastle计算结果一致。当然bouncycastle中并没有sm3带公钥摘要计算的实现,也是本人借鉴公司的代码,以bouncycastle为基础实现的

通过修改用户身份标识ID参数,与bouncycastle计算结果仍保持一致


总结

以上就是今天要讲的内容,本文详细描述了国家标准中SM2签名时的摘要计算流程,以及实例代码,仅供参考

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值