国密SM2算法的java和nodejs实现

来源:互联网 发布:淘宝招牌图片素材尺寸 编辑:程序博客网 时间:2024/05/29 14:00

转至 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 nullvar 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 falseif (s.signum() <= 0 || s.compareTo(n) >= 0) return false// 1.4.2 H = Hash(M), already done by the user// 1.4.3 e = Hvar 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*Pavar k = G.multiply(s).add(Q.multiply(t));//R = (e+k.x) mod nR = e.add(k.affineX).mod(n);//R == r  trueif (R.equals(r)) return true;return false;

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

以上代码只是代码片段,需要完整工程的同学可以留下邮箱索取源代码

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

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