lpilp / phpsm2sm3sm4

php版本,支持国密SM2的签名算法,非对称加解密,SM3的hash, SM4的对称加解密
329 stars 76 forks source link

java代码生成的sm2解密与php不一致, #78

Closed Guo6 closed 4 months ago

Guo6 commented 4 months ago

sm2加解密部分,某行给的java包,感觉和正常的sm2不太一样,求大佬帮忙看看

//公钥加密 String publicKey = "0403F4F6DB2E86444DB14E5E90A658F3B461D7Axxxxxxxxxxxxxxxxxxx33A92E3A118ACBB3A6F1A9377B4D0A03740D8E0A56AAAA4E38238182"; //私钥解密 String privateKey = "74369C6E48FC2726352A148219Axxxxxxxxxxxxxxxxxxx4F5423DBE4B2CD88E73CF2FF9377D3657FF08";

String crypto2 = "BPrAczmdEwTP1aQbvZ5j4Ssyunm5j9lUb+8lCkKIOEbt8bnEKVzJcbu3/jMMmIXNy9Wzixb+QQOKkh4AHxkDBi3e5iSYI7iWPo98NnsO9/I1p2ZhDgEiuNqazIYw4+yjhOr/jts="; 解密出来的秘钥:E6C87B23B4BAC6C4036B91FE8ED210DD

/**

public byte[] decrypt(byte[] encryptData, byte[] privatekey) { // public void decrypt(byte[] encryptData, BigInteger privateKey) {

    BigInteger privateKey = new BigInteger(privatekey);
    byte[] C1Byte = new byte[65];
    System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);
    ECPoint C1 = curve.decodePoint(C1Byte).normalize();

    /* 计算[dB]C1 = (x2, y2) */
    ECPoint dBC1 = C1.multiply(privateKey).normalize();

    /* 计算t = KDF(x2 || y2, klen) */
    byte[] dBC1Bytes = dBC1.getEncoded(false);
    DerivationFunction kdf = new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20));

    int klen = encryptData.length - 65 - 20;

    byte[] t = new byte[klen];
    kdf.init(new ISO18033KDFParameters(dBC1Bytes));
    kdf.generateBytes(t, 0, t.length);

    if (allZero(t)) {
    }

    /* 5 计算M'=C2^t */
    byte[] M = new byte[klen];
    for (int i = 0; i < M.length; i++) {
        M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
    }

    /* 6 计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
    byte[] C3 = new byte[20];
    System.arraycopy(encryptData, encryptData.length - 20, C3, 0, 20);
    byte[] u = calculateHash(dBC1.getXCoord().toBigInteger(), M, dBC1.getYCoord().toBigInteger());
    if (Arrays.equals(u, C3)) {
        return M;
    } else {
        printHexString(u);
        printHexString(C3);
        return null;
    }
}
lpilp commented 4 months ago

这个看着应该不是某行的原始sm2算法,可能是中间商自已改的算法吧,这根本就不是 sm2加密算法,不符合国标规定。如果真是银行官网提供的算法,可以直接去网信办及银监会举报他们,让他们整改 ^_^,使用国国标的国密算法。 该算法有两个问题 1 c3 段是一个sm3的hash , 给的代码中是 calculateHash这个函数,不知道是否是sm3 , 重要的问题是正常是 32字节的, 代码中截取了20个字节,这与正规的就不一样了, 2 c2计算中的kdf算法中 标准使用的是sm3的hash, 代码中使用的是 sha256 总结: java不是国标的的sm2 非对称加密算法

Guo6 commented 4 months ago

内部对接用的,没有对外公开哈哈。试了很多种方式都解不开 我在网上搜了下,JAVA代码和这个作者的一模一样 https://blog.csdn.net/fenglongmiao/article/details/79501757

感谢大佬,这种方式是不是目前没法用PHP实现

lpilp commented 4 months ago

这个不应该的,目前git里的java的国密都是标准算法的,基本使用的是 BC库, 比如招行的对外的 java版本的sdk用的就是BC库,是标准的国密算法, 如果是内部使用的自己做了修改的算法,用其他语言也得进行相应的修改了,粗看了下就是那两个地方,自行更新兼容就可,主体的算法应该是一样。 看了下你给的链接,用的BC库,你可以看下里面的原码是否与你发出来的一样,

new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20));
// 正确的是用sm3Digerst, 还有这个看着又是做了截20位的操作
System.arraycopy(encryptData, encryptData.length - 20, C3, 0, 20);
// 正确的是 不要截20个字符,就用原始的32字符

当然也有可能你给的链接里用的1.5版本的 BC库kdf用的sh256,但也不可能把hash值截个20位这种操作,现在用的是1.6的BC是标准的国密,