JAVA中的提供的第三方jar包(比如BouncyCastle), 对于SM2签名的大都是RS拼接格式。在Java体系内互相认证,问题不大,但如果与其他语言对接时, 难免遇到转换成asn1格式的问题。

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

/**
 *asn1格式转化成直接拼接r||s
 *
 * @param rsDer rs in asn1 format
 * @return sign result in plain byte array
 */
public static byte[] rsAsn1ToPlainByteArray(byte[] rsDer) {
    ASN1Sequence seq = ASN1Sequence.getInstance(rsDer);
    byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
    byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
    byte[] result = new byte[RS_LEN * 2];
    System.arraycopy(r, 0, result, 0, r.length);
    System.arraycopy(s, 0, result, RS_LEN, s.length);
    return result;
}

/**
 * r||s的字节数组转化成asn1格式
 *
 * @param sign in plain byte array
 * @return rs result in asn1 format
 */
public static byte[] rsPlainByteArrayToAsn1(byte[] sign) {
    if (sign.length != RS_LEN * 2) {
        throw new RuntimeException("err rs. ");
    }
    BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
    BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
    ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new ASN1Integer(r));
    v.add(new ASN1Integer(s));
    try {
        return new DERSequence(v).getEncoded("DER");
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

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

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

ASN.1 = 48,length,2,length_r,[0],r,2,length_s,[0],s
说明:
48:SEQUENCE(0x10) | CONSTRUCTED(0x20) 说明是有预定格式的序列
length:是数据内容的长度,就是后面字节长度
length_r(或length_s):是r(或s)内容的长度,包括[0]
 [0]:如果r(或s)的第一个字节大于127(或小于0),则需补0

具体代码如下: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;
    }
}

SignAsn1.java

/**
     * ASN1
     * 格式: 48,len-rs_asn1,2,len_r,[0],byte_r,2,len_s,[0],byte_s
     */
    public static class SignAsn1 {
        private static final byte PREFIX = SEQUENCE | CONSTRUCTED; // 48
        private Asn1Point point;

        public static SignAsn1 of(byte[] r, byte[] s) {
            SignAsn1 asn1 = new SignAsn1();
            asn1.point = Asn1Point.of(r, s);
            return asn1;
        }

        public static SignAsn1 ofRS(byte[] rs) {
            byte[] r = new byte[RS_LEN];
            byte[] s = new byte[RS_LEN];
            //获取r和s
            System.arraycopy(rs, 0, r, 0, RS_LEN);
            System.arraycopy(rs, RS_LEN, s, 0, RS_LEN);

            return of(r, s);
        }

        public static SignAsn1 ofAsn1(byte[] asn1Bts) {
            SignAsn1 asn1 = new SignAsn1();
            asn1.point = Asn1Point.of(Arrays.copyOfRange(asn1Bts, 2, asn1Bts.length));
            return asn1;
        }

        public byte[] getR() {
            return point.getX();
        }

        public byte[] getS() {
            return point.getY();
        }

        public byte[] toRS() {
            return point.toXY();
        }

        public byte[] toAsn1() {
            // 计算长度
            byte[] asn1XY = point.toAsn1();
            int lenXY = asn1XY.length;

            byte[] asn1 = new byte[lenXY + 2];
            // 前缀
            asn1[0] = PREFIX;
            // 长度
            asn1[1] = (byte) lenXY;
            // asn1XY
            System.arraycopy(asn1XY, 0, asn1, 2, lenXY);

            return asn1;
        }
    }

调用:

// rs -> asn1
byte[] rs;
byte[] asn1 = SignAsn1.ofRS(rs).toAsn1();
// rs -> asn1
byte[] asn1;
byte[] rs = SignAsn1.ofAsn1(rs).toRS();

发表评论

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