JAVA中的提供的第三方jar包(比如BouncyCastle), 对于SM2加密的大都是c1c2c3格式。在Java体系内互相认证,问题不大,但如果与其他语言对接时, 就需要按照最新的国家标准, 将其转换成c1c3c2的asn1格式。

BouncyCastle 提供了一些类辅助,例如:

private static final int C1_LEN = 65;
private static final int C3_LEN = 32;
/**
     * 将c1c3c2转成标准的ASN1格式
     *
     * @param c1c3c2
     * @return
     * @throws IOException
     */
    public static byte[] changeC1C3C2ToAsn1(byte[] c1c3c2) throws IOException {
        byte[] c1 = Arrays.copyOfRange(c1c3c2, 0, C1_LEN);
        byte[] c3 = Arrays.copyOfRange(c1c3c2, C1_LEN, C1_LEN + C3_LEN);
        byte[] c2 = Arrays.copyOfRange(c1c3c2, C1_LEN + C3_LEN, c1c3c2.length);
        byte[] c1X = Arrays.copyOfRange(c1, 1, 33);
        byte[] c1Y = Arrays.copyOfRange(c1, 33, 65);
        ASN1Integer x = new ASN1Integer(c1X);
        ASN1Integer y = new ASN1Integer(c1Y);
        DEROctetString derDig = new DEROctetString(c3);
        DEROctetString derEnc = new DEROctetString(c2);
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(x);
        v.add(y);
        v.add(derDig);
        v.add(derEnc);
        DERSequence seq = new DERSequence(v);
        return seq.getEncoded();
    }

    /**
     * 将ASN1格式转成c1c3c2
     *
     * @param asn1
     * @return
     * @throws IOException
     */
    public static byte[] changeAsn1ToC1C3C2(byte[] asn1) throws IOException {
        ASN1InputStream aIn = new ASN1InputStream(asn1);
        ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
        BigInteger x = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();
        BigInteger y = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
        byte[] c3 = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
        byte[] c2 = ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets();
        // 不压缩
        ECPoint c1Point = x9ECParameters.getCurve().createPoint(x, y, false);
        byte[] c1 = c1Point.getEncoded();

        return ByteUtils.join(c1, c3, c2);
    }

但 BouncyCastle 各版本可能存在冲突,也不希望为了一个转换引入第三方包。这里我写了一个基于JDK的c1c3c2和asn1的转换。

其实,了解清楚asn1的格式就不难转换了:

ASN.1 = 48,len-bts,c1_asn1,c3_asn1(4,length_c3,c3),c2_asn1(4,len-bts,c2)
说明:
48:SEQUENCE(0x10) | CONSTRUCTED(0x20) 说明是有预定格式的序列
len-bts:数据内容的长度,就是后面字节长度,可能是多个字节(长度字节大小,长度字节)
c1_asn1:c1的Asn1格式(2,len_x,[0],byte_x,2,len_y,[0],byte_y)
c2_asn1len-bts是c2的数据内容的长度 ,可能是多个字节(长度字节大小,长度字节) 

具体代码如下:BigIntegers.java

public final class BigIntegers {
    /**
     * Return the passed in value as an unsigned byte array.
     *
     * @param value value to be converted.
     * @return a byte array without a leading zero byte if present in the signed encoding.
     */
    public static byte[] asUnsignedByteArray(BigInteger value) {
        byte[] bytes = value.toByteArray();

        if (bytes[0] == 0) {
            byte[] tmp = new byte[bytes.length - 1];

            System.arraycopy(bytes, 1, tmp, 0, tmp.length);

            return tmp;
        }

        return bytes;
    }

    /**
     * Return the passed in value as an unsigned byte array.
     *
     * @param value value to be converted.
     * @return a byte array without a leading zero byte if present in the signed encoding.
     */
    public static byte[] asUnsignedByteArray(int length, BigInteger value) {
        byte[] bytes = value.toByteArray();
        if (bytes.length == length) {
            return bytes;
        }

        int start = bytes[0] == 0 ? 1 : 0;
        int count = bytes.length - start;

        if (count > length) {
            throw new IllegalArgumentException("standard length exceeded for value");
        }

        byte[] tmp = new byte[length];
        System.arraycopy(bytes, start, tmp, tmp.length - count, count);
        return tmp;
    }

    public static BigInteger fromUnsignedByteArray(byte[] buf) {
        return new BigInteger(1, buf);
    }

    public static BigInteger fromUnsignedByteArray(byte[] buf, int off, int length) {
        byte[] mag = buf;
        if (off != 0 || length != buf.length) {
            mag = new byte[length];
            System.arraycopy(buf, off, mag, 0, length);
        }
        return new BigInteger(1, mag);
    }
}

Asn1Point.java

/**
 * ASN1
 * 格式: 2,len_x,[0],byte_x,2,len_y,[0],byte_y
 */
public static class Asn1Point {
    private static final byte PREFIX = 2;
    private byte[] x;
    private byte[] y;
    private boolean negativeR;
    private boolean negativeS;

    public static Asn1Point of(byte[] x, byte[] y) {
        Asn1Point asn1 = new Asn1Point();
        asn1.x = x;
        asn1.y = y;
        asn1.negativeR = x[0] < 0;
        asn1.negativeS = y[0] < 0;
        return asn1;
    }

    public static Asn1Point of(byte[] asn1) {
        int lenX = asn1[1];
        int lenY = asn1[lenX + 3];
        int idxX = 2;
        int idxY = asn1.length - lenY;
        byte[] r = new byte[lenX];
        byte[] s = new byte[lenY];
        //获取R
        System.arraycopy(asn1, idxX, r, 0, lenX);
        // 判断第一个是否是正负符号
        if (r[0] == 0 && r[1] < 0) {
            r = Arrays.copyOfRange(r, 1, lenX);
        }
        //获取S
        System.arraycopy(asn1, idxY, s, 0, lenY);
        if (s[0] == 0 && s[1] < 0) {
            s = Arrays.copyOfRange(s, 1, lenY);
        }
        //返回
        return of(r, s);
    }

    public byte[] getX() {
        return x;
    }

    public byte[] getY() {
        return y;
    }

    public byte[] toXY() {
        int lenR = x.length;
        int lenS = y.length;
        byte[] rs = new byte[lenR + lenS];
        //获取R
        System.arraycopy(x, 0, rs, 0, lenR);
        //获取S
        System.arraycopy(y, 0, rs, lenR, lenS);
        return rs;
    }

    public byte[] toAsn1() {
        // 计算长度
        int lenR = x.length;
        int lenS = y.length;
        if (negativeR) {
            lenR += 1;
        }
        if (negativeS) {
            lenS += 1;
        }
        int len = lenR + lenS + 4;

        byte[] asn1 = new byte[len];
        int idx = 0;
        // x
        asn1[idx++] = PREFIX;
        asn1[idx++] = (byte) lenR;
        if (negativeR) {
            asn1[idx++] = 0;
        }
        System.arraycopy(x, 0, asn1, idx, x.length);
        idx += x.length;
        // y
        asn1[idx++] = PREFIX;
        asn1[idx++] = (byte) lenS;
        if (negativeS) {
            asn1[idx++] = 0;
        }
        System.arraycopy(y, 0, asn1, idx, y.length);

        return asn1;
    }
}

EncryptAsn1.java

/**
     * ASN1
     * 格式: 48,len-c1c3c2_asn1,c1_asn1,c3_asn1(4,len,c3),c2_asn1(4,len-bts,c3)
     */
    public static class EncryptAsn1 {
        private static final byte PREFIX = SEQUENCE | CONSTRUCTED; // 48
        private static final byte PREFIX_C = 4;
        /**
         * sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码
         * (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1
         */
        private static final int C1_LEN = 65;
        /**
         * new SM3Digest().getDigestSize()
         */
        private static final int C3_LEN = 32;
        private byte[] c1;
        private byte[] c2;
        private byte[] c3;
        private Asn1Point c1Point;

        public static EncryptAsn1 ofC1C2C3(byte[] c1c2c3) {
            EncryptAsn1 asn1 = new EncryptAsn1();

            asn1.c1 = Arrays.copyOfRange(c1c2c3, 0, C1_LEN);
            asn1.c2 = Arrays.copyOfRange(c1c2c3, C1_LEN, c1c2c3.length - C3_LEN);
            asn1.c3 = Arrays.copyOfRange(c1c2c3, c1c2c3.length - C3_LEN, c1c2c3.length);
            asn1.c1Point = Asn1Point.of(getX(asn1.c1), getY(asn1.c1));

            return asn1;
        }

        public static EncryptAsn1 ofC1C3C2(byte[] c1c3c2) {
            EncryptAsn1 asn1 = new EncryptAsn1();

            asn1.c1 = Arrays.copyOfRange(c1c3c2, 0, C1_LEN);
            asn1.c3 = Arrays.copyOfRange(c1c3c2, C1_LEN, C1_LEN + C3_LEN);
            asn1.c2 = Arrays.copyOfRange(c1c3c2, C1_LEN + C3_LEN, c1c3c2.length);
            asn1.c1Point = Asn1Point.of(getX(asn1.c1), getY(asn1.c1));

            return asn1;

        }

        public static EncryptAsn1 ofAsn1(byte[] asn1Bts) {
            EncryptAsn1 asn1 = new EncryptAsn1();

            // 计算c1c3c2Len
            byte[] lenBts = getUnSignedLengthByte(asn1Bts, 1);
            int c1c3c2Len = BigIntegers.fromUnsignedByteArray(lenBts).intValue();

            // 取c1_asn1
            int c1Idx = 1 + lenBts.length;
            if (c1c3c2Len > 127) {
                c1Idx++;
            }
            int c1XLen = asn1Bts[c1Idx + 1];
            int c1YLen = asn1Bts[c1Idx + 2 + c1XLen + 1];
            int c1Len = c1XLen + c1YLen + 4;
            byte[] c1Asn1 = Arrays.copyOfRange(asn1Bts, c1Idx, c1Idx + c1Len);
            asn1.c1Point = Asn1Point.of(c1Asn1);
            asn1.c1 = new byte[C1_LEN];
            asn1.c1[0] = PREFIX_C;
            System.arraycopy(asn1.c1Point.toXY(), 0, asn1.c1, 1, C1_LEN - 1);
            // 取c3
            int c3Idx = c1Idx + c1Len + 2;
            asn1.c3 = Arrays.copyOfRange(asn1Bts, c3Idx, c3Idx + C3_LEN);
            // 取c2
            int c2Idx = c3Idx + C3_LEN + 1;
            byte[] c2LenBts = getUnSignedLengthByte(asn1Bts, c2Idx);
            int c2Len = BigIntegers.fromUnsignedByteArray(c2LenBts).intValue();
            c2Idx += c2LenBts.length;
            if (c2Len > 127) {
                c2Idx++;
            }
            asn1.c2 = Arrays.copyOfRange(asn1Bts, c2Idx, c2Idx + c2Len);

            return asn1;
        }

        public byte[] toC1C2C3() {
            return ByteUtils.join(c1, c2, c3);
        }

        public byte[] toC1C3C2() {
            return ByteUtils.join(c1, c3, c2);
        }

        public byte[] toAsn1() {
            byte[] c1Asn1 = c1Point.toAsn1();
            // 计算长度
            int c1Len = c1Asn1.length;
            int c3Len = c3.length;
            // C2长度字节
            int c2Len = c2.length;
            byte[] c2LenBts = getLengthByte(c2Len);
            // C1C3C2长度字节
            int c1c3c2Len = c1Len + 2 + c3Len + 1 + c2LenBts.length + c2Len;
            byte[] lenBts = getLengthByte(c1c3c2Len);
            //总长度
            int len = 1 + lenBts.length + c1c3c2Len;
            // 拼接
            byte[] asn1 = new byte[len];
            int idx = 0;
            // 前缀
            asn1[idx++] = PREFIX;
            // 长度
            System.arraycopy(lenBts, 0, asn1, idx, lenBts.length);
            idx += lenBts.length;
            // c1
            System.arraycopy(c1Asn1, 0, asn1, idx, c1Len);
            idx += c1Len;
            // c3
            asn1[idx++] = PREFIX_C;
            asn1[idx++] = (byte) c3Len;
            System.arraycopy(c3, 0, asn1, idx, c3Len);
            idx += c3Len;
            // c2
            asn1[idx++] = PREFIX_C;
            System.arraycopy(c2LenBts, 0, asn1, idx, c2LenBts.length);
            idx += c2LenBts.length;
            System.arraycopy(c2, 0, asn1, idx, c2Len);

            return asn1;
        }

        public byte[] getC1() {
            return c1;
        }

        public byte[] getC2() {
            return c2;
        }

        public byte[] getC3() {
            return c3;
        }

        public BigInteger getX() {
            return BigIntegers.fromUnsignedByteArray(c1Point.getX());
        }

        public BigInteger getY() {
            return BigIntegers.fromUnsignedByteArray(c1Point.getY());
        }

        private static byte[] getX(byte[] c1) {
            if (c1.length > 64) {
                return Arrays.copyOfRange(c1, 1, RS_LEN + 1);
            }
            return Arrays.copyOfRange(c1, 0, RS_LEN);

        }

        private static byte[] getY(byte[] c1) {
            if (c1.length > 64) {
                return Arrays.copyOfRange(c1, RS_LEN + 1, c1.length);
            }
            return Arrays.copyOfRange(c1, RS_LEN, c1.length);
        }

        /**
         * 将长度转成字节数组: 字节长度,[Int -> byte[]]
         *
         * @param len
         * @return
         */
        private static byte[] getLengthByte(int len) {
            // 小于127,则没有长度字节
            if (len <= 127) {
                return new byte[]{(byte) len};
            }
            // 大于127,增加长度字节
            byte[] lenC3Bts = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(len));
            byte[] rv = new byte[lenC3Bts.length + 1];
            rv[0] = (byte) (lenC3Bts.length - 128);
            System.arraycopy(lenC3Bts, 0, rv, 1, lenC3Bts.length);

            return rv;

        }
    }

    /**
     * 获取长度字节(不包含统计字节)
     *
     * @param lenBts
     * @return
     */
    private static byte[] getUnSignedLengthByte(byte[] lenBts, int idx) {
        int len = 1;
        if (lenBts[idx] < 0) {
            len = lenBts[idx] + 128;
            idx++;
        }
        return Arrays.copyOfRange(lenBts, idx, idx + len);
    }

调用:

// c1c3c2 -> asn1
byte[] c1c3c2;
byte[] asn1 = EncryptAsn1.ofC1C3C2(rs).toAsn1();
// c1c3c2 -> asn1
byte[] asn1;
byte[] c1c3c2 = EncryptAsn1.ofAsn1(rs).toC1C3C2();
// c1c2c3 -> c1c3c2
byte[] c1c2c3;
byte[] c1c3c2 = EncryptAsn1.ofC1C2C3(rs).toC1C3C2();

3 对 “SM2加解密:c1c3c2与Asn1格式”的想法;

  1. OpenSSL performs ASN1 encoding on the SM2 encryption result, and the ciphertext encoding format is also required to be ASN1 format when decrypting. Encryption and decryption on other platforms may require the original ciphertext spliced ??by C1C3C2, so encoding and decoding is required. Individual back-end encryption and decryption are spliced ??according to C1C2C3, or other orders. If encryption and decryption is not possible, confirm the splicing order with the background and splice by yourself.

  2. OpenSSL performs ASN1 encoding on the SM2 encryption result, and the ciphertext encoding format is also required to be ASN1 format when decrypting. Encryption and decryption on other platforms may require the original ciphertext spliced ??by C1C3C2, so encoding and decoding is required. Individual back-end encryption and decryption are spliced ??according to C1C2C3, or other orders. If encryption and decryption is not possible, confirm the splicing order with the background and splice by yourself.

  3. 用此密钥java加密后,python的gmssl无法解密
    public_key = ‘04764DB4E0088629529CCB2A5F311D5BCA6504AD017EF65AB06A8D2796F20D00E95E02803262CF6CC9623E4BCA63D967425803919D35E1DF3140BEE02166BD6925’
    private_key = ‘032E074CB48FC3BA4B5081C11BBC0AEDBADE2FCEE7289DD7B8D41481739C81C8’

发表评论

邮箱地址不会被公开。 必填项已用*标注