SM2加解密:c1c3c2与Asn1格式
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_asn1:len-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();