SM2签名:R||S格式与ASN1格式
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();