问题描述
使用java开发的SM2加解密,由java层生成SM2公私钥,iOS、Android(因和java层一致,暂忽略)层使用公钥做加密,java层做解密。
- java生成的公私钥在iOS和java层单独做加解密均可用。
- iOS层加密结果java层无法解密
问题判定
SM2非对称加密的结果由C1,C2,C3三部分组成。其中C1是生成随机数的计算出的椭圆曲线点,C2是密文数据,C3是SM3的摘要值。最开始的国密标准的结果是按C1C2C3顺序的,新标准的是按C1C3C2顺序存放的。java层SM2结果是C1C2C3,iOS(C)的结果是C1C3C2。
以下调整java层SM2代码为C1C3C2。
代码
使用方法:使用方法参考下方SM2Util中main方法。
使用jar包为:bcprov-jdk15on-1.65.jar
maven引入:
<sm2.version>1.65</sm2.version>
<!--SM2加密-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${sm2.version}</version>
</dependency>
SM2公私钥实体类SM2KeyPair :
/**
* SM2公私钥实体类
*/
public class SM2KeyPair {
/** 公钥 */
private String publicKeyStr;
/** 私钥 */
private String privateKeyStr;
SM2KeyPair(String publicKeyStr, String privateKeyStr) {
this.publicKeyStr = publicKeyStr;
this.privateKeyStr = privateKeyStr;
}
public String getPublicKeyStr() {
return publicKeyStr;
}
public String getPrivateKeyStr() {
return privateKeyStr;
}
}
SM2加减密实现类ZSM2Engine:
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;
/**
* SM2加解密实现类
*/
public class ZSM2Engine {
public enum Mode {
C1C2C3, C1C3C2;
}
private final Digest digest;
private final Mode mode;
private boolean forEncryption;
private ECKeyParameters ecKey;
private ECDomainParameters ecParams;
private int curveLength;
private SecureRandom random;
/***
* 默认采用国标:C1||C3||C2
*/
public ZSM2Engine() {
this(new SM3Digest(), Mode.C1C3C2);
}
public ZSM2Engine(Mode mode) {
this(new SM3Digest(), mode);
}
public ZSM2Engine(Digest digest) {
this(digest, Mode.C1C2C3);
}
public ZSM2Engine(Digest digest, Mode mode) {
if (mode == null) {
throw new IllegalArgumentException("mode cannot be NULL");
}
this.digest = digest;
this.mode = mode;
}
/**
* 初始化
* @param forEncryption true-公钥加密, false-私钥解密
* @param param 密码参数,从中获取公或私钥、及椭圆曲线相关参数
*/
public void init(boolean forEncryption, CipherParameters param) {
this.forEncryption = forEncryption;
if (forEncryption) {
ParametersWithRandom rParam = (ParametersWithRandom) param;
ecKey = (ECKeyParameters) rParam.getParameters();
ecParams = ecKey.getParameters();
ECPoint s = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());
if (s.isInfinity()) {
throw new IllegalArgumentException("invalid key: [h]Q at infinity");
}
random = rParam.getRandom();
} else {
ecKey = (ECKeyParameters) param;
ecParams = ecKey.getParameters();
}
curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;
}
/**
* 进行加密、或解密
* @param in
* @param inOff
* @param inLen
* @return
* @throws InvalidCipherTextException
*/
public byte[] processBlock(
byte[] in,
int inOff,
int inLen)
throws IOException, InvalidCipherTextException {
if (forEncryption) {
return encrypt(in, inOff, inLen);
} else {
return decrypt(in, inOff, inLen);
}
}
public int getOutputSize(int inputLen) {
return (1 + 2 * curveLength) + inputLen + digest.getDigestSize();
}
protected ECMultiplier createBasePointMultiplier() {
return new FixedPointCombMultiplier();
}
private byte[] encrypt(byte[] in, int inOff, int inLen)
throws IOException {
byte[] c2 = new byte[inLen];
System.arraycopy