lpilp / phpsm2sm3sm4

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

phpsm2加密java无法解密,java加密,php也无法解密。 #88

Closed ran1990 closed 1 month ago

ran1990 commented 1 month ago

问题: 1、php sm2加密的内容 和其他java公司可以正常加密解密,和这个xx公司加密彼此都不通,怀疑对方sm2 修改过。 2、对方加密的内容的base64,我们解密是否只需要base decode 然后再bin2hex就行呢? @lpilp 对方jiava代码如下: ` import com.alibaba.fastjson.JSON; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint;

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Arrays;

public class SM2 {

public static String sm2Encrypt(String plainText, String pubKey) {
    byte[] data = SM2.encrypt(plainText, pubKey);
    String enData = Base64.encode(data);
    return enData;
}

public static String sm2Decrypt(String plainText, String priKey) {
    byte[] encryptData = Base64.decode(plainText);
    String rawData = SM2.decrypt(encryptData, priKey);
    return rawData;
}

//国密办文件中推荐的椭圆曲线相关参数

private static BigInteger n = new BigInteger(
        "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
private static BigInteger p = new BigInteger(
        "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
private static BigInteger a = new BigInteger(
        "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
private static BigInteger b = new BigInteger(
        "28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
private static BigInteger gx = new BigInteger(
        "32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
private static BigInteger gy = new BigInteger(
        "BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
private static final int DIGEST_LENGTH = 32;
private static SecureRandom random = new SecureRandom();
private static ECCurve.Fp curve = new ECCurve.Fp(p, a, b);
private static ECPoint G = curve.createPoint(gx, gy);
private static ECDomainParameters ecc_bc_spec = new ECDomainParameters(curve, G, n);

/**
 * 以16进制打印字节数组
 *
 * @param b
 */
public static void printHexString(byte[] b) {
    for (int i = 0; i < b.length; i++) {
        String hex = Integer.toHexString(b[i] & 0xFF);
        if (hex.length() == 1) {
            hex = '0' + hex;
        }
        System.out.print(hex.toUpperCase());
    }
    System.out.println();
}

/**
 * 随机数生成器
 *
 * @param max
 * @return
 */
private static BigInteger random(BigInteger max) {

    BigInteger r = new BigInteger(256, random);
    while (r.compareTo(max) >= 0) {
        r = new BigInteger(128, random);

    }
    return r;
}

/**
 * 判断字节数组是否全0
 *
 * @param buffer
 * @return
 */
private static boolean allZero(byte[] buffer) {
    for (int i = 0; i < buffer.length; i++) {
        if (buffer[i] != 0) {
            return false;
        }
    }
    return true;
}

/**
 * 公钥加密
 *
 * @param input     加密原文
 * @param pubKeyStr 公钥
 * @return
 */
public static byte[] encrypt(String input, String pubKeyStr) {
    ECPoint publicKey = curve.decodePoint(hexStr2Bytes(pubKeyStr));
    byte[] inputBuffer = new byte[0];
    try {
        inputBuffer = input.getBytes("UTF8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    byte[] C1Buffer;
    ECPoint kpb;
    byte[] t;
    do {
        /* 1 产生随机数k,k属于[1, n-1] */
        BigInteger k = random(n);
        /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */
        ECPoint C1 = G.multiply(k);
        C1Buffer = C1.getEncoded(false);
        /*
         * 3 计算椭圆曲线点 S = [h]Pb
         */
        BigInteger h = ecc_bc_spec.getH();
        if (h != null) {
            ECPoint S = publicKey.multiply(h);
            if (S.isInfinity()) {
                throw new IllegalStateException();
            }
        }
        /* 4 计算 [k]PB = (x2, y2) */
        kpb = publicKey.multiply(k).normalize();
        /* 5 计算 t = KDF(x2||y2, klen) */
        byte[] kpbBytes = kpb.getEncoded(false);
        t = KDF(kpbBytes, inputBuffer.length);
    } while (allZero(t));
    /* 6 计算C2=M^t */
    byte[] C2 = new byte[inputBuffer.length];
    for (int i = 0; i < inputBuffer.length; i++) {
        C2[i] = (byte) (inputBuffer[i] ^ t[i]);
    }
    /* 7 计算C3 = Hash(x2 || M || y2) */
    byte[] C3 = sm3hash(kpb.getXCoord().toBigInteger().toByteArray(), inputBuffer,
            kpb.getYCoord().toBigInteger().toByteArray());
    /* 8 输出密文 C=C1 || C2 || C3 */
    byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];
    System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
    System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
    System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length);
    return encryptResult;
}

/**
 * 私钥解密
 *
 * @param encryptData 密文数据字节数组
 * @param priKeyStr   解密私钥
 * @return
 */
public static String decrypt(byte[] encryptData, String priKeyStr) {

    BigInteger privateKey = new BigInteger(priKeyStr, 16);

    byte[] C1Byte = new byte[65];
    System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);

    ECPoint C1 = curve.decodePoint(C1Byte).normalize();

    /*
     * 计算椭圆曲线点 S = [h]C1 是否为无穷点
     */
    BigInteger h = ecc_bc_spec.getH();
    if (h != null) {
        ECPoint S = C1.multiply(h);
        if (S.isInfinity()) {
            throw new IllegalStateException();
        }
    }
    /* 计算[dB]C1 = (x2, y2) */
    ECPoint dBC1 = C1.multiply(privateKey).normalize();

    /* 计算t = KDF(x2 || y2, klen) */
    byte[] dBC1Bytes = dBC1.getEncoded(false);
    int klen = encryptData.length - 65 - DIGEST_LENGTH;
    byte[] t = KDF(dBC1Bytes, klen);

    if (allZero(t)) {
        System.err.println("all zero");
        throw new IllegalStateException();
    }

    /* 计算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]);
    }

    /*  计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
    byte[] C3 = new byte[DIGEST_LENGTH];

    System.arraycopy(encryptData, encryptData.length - DIGEST_LENGTH, C3, 0, DIGEST_LENGTH);
    byte[] u = sm3hash(dBC1.getXCoord().toBigInteger().toByteArray(), M,
            dBC1.getYCoord().toBigInteger().toByteArray());

    if (Arrays.equals(u, C3)) {
        try {
            return new String(M, "UTF8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    } else {
        return null;
    }
}

/**
 * 判断是否在范围内
 *
 * @param param
 * @param min
 * @param max
 * @return
 */
private static boolean between(BigInteger param, BigInteger min, BigInteger max) {
    if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
        return true;
    } else {
        return false;
    }
}

/**
 * 判断生成的公钥是否合法
 *
 * @param publicKey
 * @return
 */
private static boolean checkPublicKey(ECPoint publicKey) {
    if (!publicKey.isInfinity()) {
        BigInteger x = publicKey.getXCoord().toBigInteger();
        BigInteger y = publicKey.getYCoord().toBigInteger();
        if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {
            BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);
            BigInteger yResult = y.pow(2).mod(p);
            if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
                return true;
            }
        }
    }
    return false;
}

/**
 * 字节数组拼接
 *
 * @param params
 * @return
 */
private static byte[] join(byte[]... params) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] res = null;
    try {
        for (int i = 0; i < params.length; i++) {
            baos.write(params[i]);
        }
        res = baos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return res;
}

/**
 * sm3摘要
 * @param params
 * @return
 */
private static byte[] sm3hash(byte[]... params) {
    byte[] res = null;
    try {
        res = SM3.hash(join(params));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return res;
}

/**
 * 密钥派生函数
 * @param Z
 * @param klen 生成klen字节数长度的密钥
 * @return
 */
private static byte[] KDF(byte[] Z, int klen) {
    int ct = 1;
    int end = (int) Math.ceil(klen * 1.0 / 32);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        for (int i = 1; i < end; i++) {
            baos.write(sm3hash(Z, SM3.toByteArray(ct)));
            ct++;
        }
        byte[] last = sm3hash(Z, SM3.toByteArray(ct));
        if (klen % 32 == 0) {
            baos.write(last);
        } else {
            baos.write(last, 0, klen % 32);
        }
        return baos.toByteArray();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public static byte[] hexStr2Bytes(String src) {
    int l = src.length() / 2;
    byte[] ret = new byte[l];
    for (int i = 0; i < l; ++i) {
        int m = i * 2 + 1;
        int n = m + 1;
        ret[i] = uniteBytes(src.substring(i * 2, m), src.substring(m, n));
    }
    return ret;
}

private static byte uniteBytes(String src0, String src1) {
    byte b0 = Byte.decode("0x" + src0);
    b0 = (byte) (b0 << 4);
    byte b1 = Byte.decode("0x" + src1);
    byte ret = (byte) (b0 | b1);
    return ret;
}

`

lpilp commented 1 month ago

对方的格式( 按hex来说)是 04 + c1+ c2 + c3, PHP这边的缺省格式是 c1+c3+c2 , 加解密时两个注意点就行,

ran1990 commented 1 month ago

@lpilp 对方返回的是bas64 : $m2EncryptData =BJdx3C2g+B/K2fgQxw6jYpLfPilj9TCu/vTWybALImwzGY82JTC94FeoUI0yJCrk1JehikW6GL+cMlcx7Jb+S+yuF9sBrzU7H4aXgGzY1rrP7fkWm9CpJgovquVnNEB3noFiMbtVhP9eTu/t3nbvj2NLAI+6JsKtybFg60rkHK2X/GOIXSB6bTE3yNOrQx3+6v7jfG390i1BEsyaHBvIXOsXks1Y4JqRjvmiQlu7fr3I93e1hAF6ruRvrUMuxIpuYWbYSmMy6wSVXgWBHzSVRc13F24ZtaX8krf7D64K2TPPBRjS7nS1hgYrytyOEqT+lbSAT3X4u8+MKIFHV66qmlrS2B7Z/wJ+SKWR2Q6KyloTMH4p8ZwMYKQeoyN046316kBVo2UxhLP4CQwp1/7NMzSGBkEc9IYcE85wNi8mheR920f82vGL+Lp/vbNJ5tua5yEK2DzmFbLT0jOhanaGOeo=

    $m2EncryptData = bin2hex(base64_decode($m2EncryptData));

    $m2DecryptData = $sm2->doDecrypt($m2EncryptData, $this->response_private_key, true, C1C2C3);

还是不对

lpilp commented 1 month ago

看代码应该是没有问题的,还有一个确认下 $this->response_private_key 这边使用的是 64个 hex字节的的明文密钥, 是否是使用的是这种密钥 如果都正确的话,只能一步步调试了,也可以你用一对测试密码对, 用XX公司的代码加密一段,然后把公私钥,加密后的都 打出来,我给你看下, 如果上面的就是用的测试的密码对,那把私钥给我下也行,就不用重新生成了

ran1990 commented 1 month ago

@lpilp 我不确定对方java是否定制化更改过,对方技术似乎说过这样的话语; 流程是,我使用一个平台公钥去签名,传给对方,对方在返回一个加密字符串,使用私钥解密,但是这个私钥和我加密的公钥似乎不是一堆,应该类似支付宝,把公钥转换成过平台公钥。

私钥:7d895253367e6exxxxxxxxxxxxxxxxxxxxxxxx0b4dafb38cbd1bd1e9b1bf1 明文参数: {"certNo":"341126197709218366","certType":"01","sourceIp":"127.000.000.001","personalMandate":"1","appName":"01测试","sceneId":"1","protocolNo":"11","protocolVersion":"11","cardNo":"6228480246125491166","ipType":"04"} 加密结果: BB4HlitUonlL2PhySO/Tq7dMk+goRT27g/blWU698LVabcGPJd/6kW45GuDoPbEEBAXfMX7NbTjmSyUZlNpvM568F9lhe+skvySIlPdUl6d4pDR7UrTYWNBJhZFZqhq93T57RN3tIXv0MsjcFPn7150k500M57v5Zd2or//4FfYv+gdpVKmS/9JbmCflMl/cVuxLcEnU+u5Y2Ck1QqGrS/lRJj7MO6Rglor2TDsrvxZ2rlXmBdyUULoIC/m75HyL2XytOOmMyepjXCGZHjfxBAnG3bBPzbXZi5iAGJD52yiYNp1yLKUGIivFxNKCbxhjKilApGSSPZoVo8/GCP++dWuTPrfLJiJIuRJyQwRjcDZuafMb58Do4/SKlb83IquxQ6uosOxtx9dCogTJaAllJxKDam2drojLqKoI5Q== 上送报文: {"data":"BB4HlitUonlL2PhySO/Tq7dMk+goRT27g/blWU698LVabcGPJd/6kW45GuDoPbEEBAXfMX7NbTjmSyUZlNpvM568F9lhe+skvySIlPdUl6d4pDR7UrTYWNBJhZFZqhq93T57RN3tIXv0MsjcFPn7150k500M57v5Zd2or//4FfYv+gdpVKmS/9JbmCflMl/cVuxLcEnU+u5Y2Ck1QqGrS/lRJj7MO6Rglor2TDsrvxZ2rlXmBdyUULoIC/m75HyL2XytOOmMyepjXCGZHjfxBAnG3bBPzbXZi5iAGJD52yiYNp1yLKUGIivFxNKCbxhjKilApGSSPZoVo8/GCP++dWuTPrfLJiJIuRJyQwRjcDZuafMb58Do4/SKlb83IquxQ6uosOxtx9dCogTJaAllJxKDam2drojLqKoI5Q=="} 响应报文: {"errInfo":"成功","data":"BPo8lvMHr5la+ESqlMwNEcLbT8ER8/a4HL7Kn642Ire0RU18H+e17kgFU1JSQphHcXFWI0yzLit9i9y2YNFEk6z1BusR9cN4EJtgrvqDwN9SezYjFKeaorOt0496LDK1oRsM8XeN3/UtSHvGKRndI6iajYcRXZek33Wv1iCxvFopmgBkv22GHGIxohLPpSpeShzcHKqRbcLrs+x9fMakwB747+AzjuYVe0aTqKHn09Bjj9ou/LVLX/+sV8V8LM/m1nM9WVSrRYA7JgyPnw0dnsITE8DbVJmm2FtMZ4QDruhZJj7MzRBPKjo8zxllc2QCfaPTc9o5Uvd3bxoxRWFnf6yNIYaTuAxny3KkdgDHq/g1hWC2A7Z3sSIXUGW9eFTgQROwjPhPPJsttVXfgB2M4tp51hkQSVbzKDN3V/eeeUu8k7YlzOM+zarJphf+RzNMPB+xwwXv3MdEskYMyDjwleM=","errCode":"20000000"}

lpilp commented 1 month ago

试了解不开,最大的可能是出在这里:SM3.toByteArray(ct),这个有个补齐操作,你看下代码有没有补齐到4字节 这个正常应该是 ( php代码) str_pad(chr(ct), 4, chr(0), STR_PAD_LEFT)) , 有个补全操作,比如当ct=1时,这个的16进制就是 00000001

private static byte[] KDF(byte[] Z, int klen) {
        int ct = 1;
        int end = (int) Math.ceil(klen * 1.0 / 32);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            for (int i = 1; i < end; i++) {
                baos.write(sm3hash(Z, SM3.toByteArray(ct)));
                ct++;
            }
。。。 。。。
ran1990 commented 1 month ago

是需要改php代码还是java代码呢。java代码对方肯定是不配合的。 @lpilp

lpilp commented 1 month ago

如果是对方的问题,那肯定得按对方的格式来修改我们的PHP,从GIT的开源的那个项目来看 SM3.toByteArray(ct) ,好像没有问题, 不知道你那边的有没有修改过,原始是:

static byte[] toByteArray(int i) {
        byte[] byteArray = new byte[4];
        byteArray[0] = (byte) (i >>> 24);
        byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
        byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
        byteArray[3] = (byte) (i & 0xFF);
        return byteArray;
    }
ran1990 commented 1 month ago

` public class SM3 {

private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
        '9', 'A', 'B', 'C', 'D', 'E', 'F'};
private static final String ivHexStr = "7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e";
private static final BigInteger IV = new BigInteger(ivHexStr.replaceAll(" ",
        ""), 16);
private static final Integer Tj15 = Integer.valueOf("79cc4519", 16);
private static final Integer Tj63 = Integer.valueOf("7a879d8a", 16);
private static final byte[] FirstPadding = {(byte) 0x80};
private static final byte[] ZeroPadding = {(byte) 0x00};

private static int T(int j) {
    if (j >= 0 && j <= 15) {
        return Tj15.intValue();
    } else if (j >= 16 && j <= 63) {
        return Tj63.intValue();
    } else {
        throw new RuntimeException("data invalid");
    }
}

private static Integer FF(Integer x, Integer y, Integer z, int j) {
    if (j >= 0 && j <= 15) {
        return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
    } else if (j >= 16 && j <= 63) {
        return Integer.valueOf((x.intValue() & y.intValue())
                | (x.intValue() & z.intValue())
                | (y.intValue() & z.intValue()));
    } else {
        throw new RuntimeException("data invalid");
    }
}

private static Integer GG(Integer x, Integer y, Integer z, int j) {
    if (j >= 0 && j <= 15) {
        return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
    } else if (j >= 16 && j <= 63) {
        return Integer.valueOf((x.intValue() & y.intValue())
                | (~x.intValue() & z.intValue()));
    } else {
        throw new RuntimeException("data invalid");
    }
}

private static Integer P0(Integer x) {
    return Integer.valueOf(x.intValue()
            ^ Integer.rotateLeft(x.intValue(), 9)
            ^ Integer.rotateLeft(x.intValue(), 17));
}

private static Integer P1(Integer x) {
    return Integer.valueOf(x.intValue()
            ^ Integer.rotateLeft(x.intValue(), 15)
            ^ Integer.rotateLeft(x.intValue(), 23));
}

private static byte[] padding(byte[] source) throws IOException {
    if (source.length >= 0x2000000000000000l) {
        throw new RuntimeException("src data invalid.");
    }
    long l = source.length * 8;
    long k = 448 - (l + 1) % 512;
    if (k < 0) {
        k = k + 512;
    }
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write(source);
    baos.write(FirstPadding);
    long i = k - 7;
    while (i > 0) {
        baos.write(ZeroPadding);
        i -= 8;
    }
    baos.write(long2bytes(l));
    return baos.toByteArray();
}

private static byte[] long2bytes(long l) {
    byte[] bytes = new byte[8];
    for (int i = 0; i < 8; i++) {
        bytes[i] = (byte) (l >>> ((7 - i) * 8));
    }
    return bytes;
}

public static byte[] hash(byte[] source) throws IOException {
    byte[] m1 = padding(source);
    int n = m1.length / (512 / 8);
    byte[] b;
    byte[] vi = IV.toByteArray();
    byte[] vi1 = null;
    for (int i = 0; i < n; i++) {
        b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64);
        vi1 = CF(vi, b);
        vi = vi1;
    }
    return vi1;
}

private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
    int a, b, c, d, e, f, g, h;
    a = toInteger(vi, 0);
    b = toInteger(vi, 1);
    c = toInteger(vi, 2);
    d = toInteger(vi, 3);
    e = toInteger(vi, 4);
    f = toInteger(vi, 5);
    g = toInteger(vi, 6);
    h = toInteger(vi, 7);

    int[] w = new int[68];
    int[] w1 = new int[64];
    for (int i = 0; i < 16; i++) {
        w[i] = toInteger(bi, i);
    }
    for (int j = 16; j < 68; j++) {
        w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15))
                ^ Integer.rotateLeft(w[j - 13], 7) ^ w[j - 6];
    }
    for (int j = 0; j < 64; j++) {
        w1[j] = w[j] ^ w[j + 4];
    }
    int ss1, ss2, tt1, tt2;
    for (int j = 0; j < 64; j++) {
        ss1 = Integer
                .rotateLeft(
                        Integer.rotateLeft(a, 12) + e
                                + Integer.rotateLeft(T(j), j), 7);
        ss2 = ss1 ^ Integer.rotateLeft(a, 12);
        tt1 = FF(a, b, c, j) + d + ss2 + w1[j];
        tt2 = GG(e, f, g, j) + h + ss1 + w[j];
        d = c;
        c = Integer.rotateLeft(b, 9);
        b = a;
        a = tt1;
        h = g;
        g = Integer.rotateLeft(f, 19);
        f = e;
        e = P0(tt2);
    }
    byte[] v = toByteArray(a, b, c, d, e, f, g, h);
    for (int i = 0; i < v.length; i++) {
        v[i] = (byte) (v[i] ^ vi[i]);
    }
    return v;
}

private static int toInteger(byte[] source, int index) {
    StringBuilder valueStr = new StringBuilder("");
    for (int i = 0; i < 4; i++) {
        valueStr.append(hexDigits[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
        valueStr.append(hexDigits[(byte) (source[index * 4 + i] & 0x0F)]);
    }
    return Long.valueOf(valueStr.toString(), 16).intValue();

}

private static byte[] toByteArray(int a, int b, int c, int d, int e, int f,
                                  int g, int h) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
    baos.write(toByteArray(a));
    baos.write(toByteArray(b));
    baos.write(toByteArray(c));
    baos.write(toByteArray(d));
    baos.write(toByteArray(e));
    baos.write(toByteArray(f));
    baos.write(toByteArray(g));
    baos.write(toByteArray(h));
    return baos.toByteArray();
}

public static byte[] toByteArray(int i) {
    byte[] byteArray = new byte[4];
    byteArray[0] = (byte) (i >>> 24);
    byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
    byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
    byteArray[3] = (byte) (i & 0xFF);
    return byteArray;
}

private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0) {
        n = 256 + n;
    }
    int d1 = n / 16;
    int d2 = n % 16;
    return "" + hexDigits[d1] + hexDigits[d2];
}

public static String byteArrayToHexString(byte[] b) {
    StringBuffer resultSb = new StringBuffer();
    for (int i = 0; i < b.length; i++) {
        resultSb.append(byteToHexString(b[i]));
    }
    return resultSb.toString();
}

public static void main(String[] args) throws IOException {
    System.out.println(SM3.byteArrayToHexString(SM3.hash("sm3算法测试".getBytes())));
}

}

`

lpilp commented 1 month ago

也没有修改,今天刚好有空,我装个java环境试试

ran1990 commented 1 month ago

感谢大佬

lpilp commented 1 month ago

修改 /src/smecc/sm2/chipher.php 的第26行的reset函数,添加了4行,该修改只是匹配了这个java项目,该java有bug, 与其他的项目不匹配,请fork项目后自行修改使用

private function  Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
    {
        $this->sm3keybase = new SM3Digest();
        $this->sm3c3 = new SM3Digest();

        $p = array();

        $gmp_x = $this->p2->GetX();
        $this->sm3keybase->BlockUpdate(array(4), 0, 1); // 添加: 这里添加个04
        $x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
        $this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
        if($x[0]>127){ // >7F
            $this->sm3c3->BlockUpdate(array(0), 0, 1);   // 这里做了个判定,补00
        }
        $this->sm3c3->BlockUpdate($x, 0, sizeof($x));
   .....
    }
ran1990 commented 1 month ago

@lpilp 试了一下 还是不行

image

image

lpilp commented 1 month ago

看着是好使的啊 图片

lpilp commented 1 month ago

改完后是这样,是不是哪里没改对

private function  Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
    {
        $this->sm3keybase = new SM3Digest();
        $this->sm3c3 = new SM3Digest();

        $p = array();

        $gmp_x = $this->p2->GetX();
        $this->sm3keybase->BlockUpdate(array(4), 0, 1); // 添加: 这里添加个04
        $x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
        $this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
        if($x[0]>127){ // >7F
            $this->sm3c3->BlockUpdate(array(0), 0, 1);   // 这里做了个判定,补00
        }
        $this->sm3c3->BlockUpdate($x, 0, sizeof($x));

        $gmp_y = $this->p2->GetY();
        $y = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
        $this->sm3keybase->BlockUpdate($y, 0, sizeof($y));

        $this->ct = 1;
        $this->NextKey();
    }
ran1990 commented 1 month ago

是这样子的,我用发您的这个加密的的确可以解密,但是我用java生成了一个新的,又不能解密了,不能百分百解密。

BPQaO3ISxSZ8A3ycmyMpETc8/G7AZhP/9dg92TmT4BXhH5/l9XovYH0gS8Btp95ofANXnN6MSG25G5VM5k8DLVnF9L1e+HnEqX8OO31bu17sQZsrUs/KQqZ6Ffe3qxU3dz+CfuYzCMCJ587BupNCcjBjeKjTmnXQl4Si+mXtaiG0wsIIP1ZaBdgdjuovN7eFKQByC3sD+YNOwWVHK1Z1ENkQPtq4CC6AptkemStRti3Y8iMq4/s90q1oLuRtHsMZcihAkRIZYoonUw/F/wwwNDkSA6GS2zs=

lpilp commented 1 month ago

当时没注意,还有一个y也得处理,/src/smecc/sm2/chipher.php 还是这个文件里,判定一下, java取x,y的值用错了函数,取成生成asn1的bigint的算法,得计算是否大于7f,

public function  Dofinal()
    {
        $c3 = array();
        $gmp_p = $this->p2->GetY();

        $p = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_p);
       ///========以下添加的======
        if($p[0]>127){ // >7F
            $this->sm3c3->BlockUpdate(array(0), 0, 1);   // 这里做了个判定,补00
        }
      ///========以上添加的======
        $this->sm3c3->BlockUpdate($p, 0, sizeof($p));
        $this->sm3c3->DoFinal($c3, 0);
        $this->Reset(); 
        return $c3;
    }
ran1990 commented 1 month ago

感谢大佬 @lpilp ,总算可以了,我都打算放弃只有使用java暴露一个api 来负责解密加密了,还好最终ok