国密SM2算法的java和nodejs实现

转至 http://mp.weixin.qq.com/s/nboZAvg1qYiJIEun6yF2aQ

国密即国家密码局认定的国产密码算法,即商用密码。包括对称加密(SM1) ,非对称加密(SM2) , 信息摘要(SM3). 本文介绍SM2

SM2 基于 ECC实现。.国家密码局推荐ECC曲线是256位,相当于比特币的secp256k1.
SM2 数学计算过程可以参考国家密码局官网的公布文档
本文介绍SM2 签名验签的java和nodejs的实现

ECC 算法依赖两个重要的数学运算 1,大数运算,2,椭圆乘法运算。
Java 中天生有BigInteger类,椭圆乘法运算可以借助于开源的java加密库:bouncycastle ,gradle 工程添加依赖:
compile ‘org.bouncycastle:bcprov-jdk15on:1.55’
下面就贴代码吧

public class SM2 
{
   //测试参数
// public static final String[] ecc_param = {
//     "8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 
//     "787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 
//     "63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 
//     "8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 
//     "421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 
//     "0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2" 
// };
   
   //正式参数
   public static String[] ecc_param = { 
      "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
      "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
      "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
      "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
      "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
      "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
   };

   public static SM2 Instance() 
   {
      return new SM2();
   }

   public final BigInteger ecc_p;
   public final BigInteger ecc_a;
   public final BigInteger ecc_b;
   public final BigInteger ecc_n;
   public final BigInteger ecc_gx;
   public final BigInteger ecc_gy;
   public final ECCurve ecc_curve;
   public final ECPoint ecc_point_g;
   public final ECDomainParameters ecc_bc_spec;
   public final ECKeyPairGenerator ecc_key_pair_generator;
   public final ECFieldElement ecc_gx_fieldelement;
   public final ECFieldElement ecc_gy_fieldelement;

   public SM2() 
   {
      this.ecc_p = new BigInteger(ecc_param[0], 16);
      this.ecc_a = new BigInteger(ecc_param[1], 16);
      this.ecc_b = new BigInteger(ecc_param[2], 16);
      this.ecc_n = new BigInteger(ecc_param[3], 16);
      this.ecc_gx = new BigInteger(ecc_param[4], 16);
      this.ecc_gy = new BigInteger(ecc_param[5], 16);

      this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
      this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);

      this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
      this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);

      this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);

      ECKeyGenerationParameters ecc_ecgenparam;
      ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());

      this.ecc_key_pair_generator = new ECKeyPairGenerator();
      this.ecc_key_pair_generator.init(ecc_ecgenparam);
   }
}
签名代码片段:
    public static BigInteger[] Sm2Sign(byte[] md, AsymmetricCipherKeyPair keypair) {
        SM3Digest sm3 = new SM3Digest();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();
        byte[] hashData = new byte[32];
        sm3.update(md, 0, md.length);
        sm3.doFinal(hashData, 0);
        // e
        BigInteger e = new BigInteger(1, hashData);
        // k
        BigInteger k = null;
        ECPoint kp = null;
        BigInteger r = null;
        BigInteger s = null;
        BigInteger userD = null;
        BigInteger ecc_n = null;
        do {
            do {

                ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();
                k = ecpriv.getD();
                kp = ecpub.getQ();
                ecc_n = ecpriv.getParameters().getN();
                userD = ecpriv.getD();

                // r = (e+x) mod n
                r = e.add(kp.getX().toBigInteger());

                r = r.mod(ecc_n);
            }
            while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));

            //s=  (((1 + dA)~-1)  *  (k - r*da )) mod n
            BigInteger da_1 = userD.add(BigInteger.ONE);
            da_1 = da_1.modInverse(ecc_n);
            // s
            s = r.multiply(userD);
            s = k.subtract(s).mod(ecc_n);
            s = da_1.multiply(s).mod(ecc_n);
        }
        while (s.equals(BigInteger.ZERO));

        return new BigInteger[]{r, s};
    }

签验代码片段:
    public static boolean Sm2verify(byte[] md, BigInteger r, BigInteger s, AsymmetricCipherKeyPair keypair) {

        SM3Digest sm3 = new SM3Digest();

        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();

        byte[] hashData = new byte[32];
        sm3.update(md, 0, md.length);
        sm3.doFinal(hashData, 0);
        // e
        BigInteger e = new BigInteger(1, hashData);
        // k
        ECPoint k;
        ECPoint G = null;
        ECPoint Pa = null;
        BigInteger t = null;
        BigInteger R = null;
        BigInteger ecc_n = null;
        Pa = ecpub.getQ();
        G = ecpub.getParameters().getG();

        ecc_n = ecpub.getParameters().getN();

        if(r.equals(BigInteger.ONE)  || r.equals(ecc_n)) {
            return false;
        }
        if(s.equals(BigInteger.ONE)  || s.equals(ecc_n)) {
            return false;
        }

        t = r.add(s).mod(ecc_n);
        if (t.equals(BigInteger.ZERO)) {
            return false;
        }

        //k(x,y) = s*G + t*Pa
        k = G.multiply(s).add(Pa.multiply(t));
        //R = (e+k.x) mod n
        R = e.add(k.getX().toBigInteger()).mod(ecc_n);
        //R == r  true
        if (R.equals(r)) return true;


        return false;
    }

nodejs的实现方式
nodejs本身没有BigInteger的,但有牛人写了个BigInteger ,https://www.npmjs.com/package/biginteger
椭圆运算:https://github.com/cryptocoinjs/ecurve
有这两基本运算,就可以按国密局文档编写我们的SM2算法了
下面直接贴代码吧

var ecurve = require('ecurve');
var Point = ecurve.Point;
var BigInteger = require('bigi');
var Curve = ecurve.Curve;
var assert = require('assert')

var SM2=getCurveSM2();
function getCurveSM2() {
    var curve = {
        p: "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
        a: "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
        b: "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
        n: "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
        h: "01",
        Gx: "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
        Gy: "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
    }



    if (!curve) return null

    var p = new BigInteger(curve.p, 16)
    var a = new BigInteger(curve.a, 16)
    var b = new BigInteger(curve.b, 16)
    var n = new BigInteger(curve.n, 16)
    var h = new BigInteger(curve.h, 16)
    var Gx = new BigInteger(curve.Gx, 16)
    var Gy = new BigInteger(curve.Gy, 16)

    return new Curve(p, a, b, Gx, Gy, n, h)
}

function getQ(d) {
    var D =  BigInteger.fromBuffer(d);
    var G = SM2.G
    var Q = G.multiply(D);
    return Q;
}
function decodeFrom(buffer) {
   return Point.decodeFrom(SM2,buffer);
}


function decodeFromXY(buffer) {
    var byteLength = Math.floor((SM2.p.bitLength() + 7) / 8)
    assert.equal(buffer.length, byteLength * 2, 'Invalid sequence length')
    var x = BigInteger.fromBuffer(buffer.slice(0, byteLength))
    var y = BigInteger.fromBuffer(buffer.slice(byteLength))
    var Q = Point.fromAffine(SM2, x, y)
    return Q;
}


function sign(hash, d) {
    //var x = d.toBuffer(32)
    var e = BigInteger.fromBuffer(hash)
    var D =  BigInteger.fromBuffer(d);
    var n = SM2.n
    var G = SM2.G
    var Q = G.multiply(D);
    var r, s;
    do {
        do {
            r = e.add(Q.affineX);
            r = r.mod(n);
        }
        while (r.equals(BigInteger.ZERO) || r.add(D).equals(n));

        //s=  (((1 + dA)~-1)  *  (k - r*da )) mod n
        var da_1 = D.add(BigInteger.ONE);
        da_1 = da_1.modInverse(n);
        // s
        s = r.multiply(D);
        s = D.subtract(s).mod(n);
        s = da_1.multiply(s).mod(n);
    }
    while (s.equals(BigInteger.ZERO));
    return {r:r.toBuffer(),s:s.toBuffer()};
}
function verify (hash, signature, Q) {
    var n = SM2.n
    var G = SM2.G

    var r = BigInteger.fromBuffer(signature.r);
    var s =  BigInteger.fromBuffer(signature.s);

    // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1]
    if (r.signum() <= 0 || r.compareTo(n) >= 0) return false
    if (s.signum() <= 0 || s.compareTo(n) >= 0) return false


    // 1.4.2 H = Hash(M), already done by the user
    // 1.4.3 e = H
    var e = BigInteger.fromBuffer(hash)



    var t;

    t = r.add(s).mod(n);
    if (t.equals(BigInteger.ZERO)) {
        return false;
    }

    //k(x,y) = s*G + t*Pa
    var k = G.multiply(s).add(Q.multiply(t));
    //R = (e+k.x) mod n
    R = e.add(k.affineX).mod(n);
    //R == r  true
    if (R.equals(r)) return true;


    return false;

}
module.exports = {
    Curve: SM2,
    Point:Point,
    decodeFrom:decodeFrom,
    decodeFromXY:decodeFromXY,
    getQ:getQ,
    sign: sign,
    verify: verify
}

以上代码只是代码片段,需要完整工程的同学可以关注订阅号索取源代码

如果你喜欢这篇文章,请动动手指点击二维码关注本公众号

微信扫一扫
关注该公众号
这里写图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 35
    评论
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值