本节讲的SM2的实现,这个其实是可以参照RSA来写,都是非对称加密。
我们通过KeyPairGenerator.getInstance("SM2").generateKeyPair();来获取密钥对,最后调用的就是KeyPairGenSpi的generateKeyPair(),仿照RSA中的KeyPairGeneratorSpi来写,
package org.gk.gm.SM2.spi;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.gk.gm.SM2.util.ECUtil;
import org.gk.gm.provider.SMProvider;
public class KeyPairGenSpi extends EC {
public KeyPairGenSpi() {
try {
X9ECParameters x9 = ECUtil.getNamedCurveByName("SM2");
ECParameterSpec parameterSpec = new ECParameterSpec(x9.getCurve(), x9.getG(), x9.getN());
super.initialize(parameterSpec, (SecureRandom)null);
} catch (InvalidAlgorithmParameterException var2) {
var2.printStackTrace();
}
}
public KeyPair generateKeyPair() {
KeyPair keyPair = super.generateKeyPair();
try {
SM2PrivateKey e = new SM2PrivateKey((ECPrivateKey)keyPair.getPrivate(), SMProvider.CONFIGURATION);
SM2PublicKey publicKey = new SM2PublicKey((ECPublicKey)keyPair.getPublic(), SMProvider.CONFIGURATION);
return new KeyPair(publicKey, e);
} catch (InvalidKeyException var4) {
return null;
}
}
public void initialize(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
super.initialize((AlgorithmParameterSpec)null, (SecureRandom)null);
}
public void initialize(int keySize) {
try {
super.initialize((AlgorithmParameterSpec)null, (SecureRandom)null);
} catch (InvalidAlgorithmParameterException var3) {
;
}
}
public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException {
super.initialize((AlgorithmParameterSpec)null, random);
}
public void initialize(int keySize, SecureRandom random) {
try {
super.initialize((AlgorithmParameterSpec)null, random);
} catch (InvalidAlgorithmParameterException var4) {
;
}
}
}
我们这里继承EC(EC也是继承KeyPairGenerator的),其中构造方法就是对SM2进行初始化,SM2PublicKey,SM2PrivateKey这个都是自定义的,这个可以随个人的爱好,只是加解密的时候,对应的变一下就可以了,这里公钥genEncode返回的是512bit(128byte),私钥256bit(64byte)。
好了,再看加密,加密CipherSpi这个类跟RSA的差不多。
public class CipherSpi extends BaseCipherSpi {
private int opMode;
private AsymmetricKeyParameter keyParam = null;
private SM2Cipher sm2Cipher = null;
private ECPoint c1 = null;
private ByteArrayOutputStream bout = null;
public CipherSpi() {
}
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
if(opmode != 1 && opmode != 2) {
throw new InvalidKeyException("Only supports ENCRYPT_MODE|DECRYPT_MODE.");
} else if(key == null || !(key instanceof ECPublicKey) && !(key instanceof ECPrivateKey)) {
throw new InvalidKeyException("No useful Key.");
} else if(opmode == 2 && key instanceof ECPublicKey) {
throw new InvalidKeyException("DECRYPT_MODE needs a Private Key.");
} else {
if(this.sm2Cipher == null) {
this.sm2Cipher = new SM2Cipher();
}
this.keyParam = key instanceof ECPublicKey?ECUtil.generatePublicKeyParameter((ECPublicKey)key):ECUtil.generatePrivateKeyParameter((ECPrivateKey)key);
if(opmode == 1) {
ECPoint q = null;
if(this.keyParam.isPrivate()) {
ECDomainParameters edp = ((ECKeyParameters)this.keyParam).getParameters();
if(key instanceof SM2PrivateKey) {
DERBitString dps = ((SM2PrivateKey)key).getPubKey();
if(dps != null) {
try {
q = edp.getCurve().decodePoint(dps.getBytes());
} catch (RuntimeException var8) {
;
}
}
}
if(q == null) {
q = edp.getG().multiply(((ECPrivateKeyParameters)this.keyParam).getD());
}
} else {
q = ((ECPublicKeyParameters)this.keyParam).getQ();
}
this.c1 = this.sm2Cipher.Init_enc(q, random);
}
this.opMode = opmode;
if(this.bout == null) {
this.bout = new ByteArrayOutputStream();
} else {
this.bout.reset();
}
}
}
protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException {
this.engineInit(opmode, key, random);
}
protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException {
this.engineInit(opmode, key, random);
}
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
if(input != null && inputOffset >= 0 && inputOffset + inputLen <= input.length) {
this.bout.write(input, inputOffset, inputLen);
}
return null;
}
protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException {
if(input != null && inputOffset >= 0 && inputOffset + inputLen <= input.length) {
this.bout.write(input, inputOffset, inputLen);
}
return this.bout.size() + 1 + 64 + 32;
}
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException {
if(input != null && inputOffset >= 0 && inputOffset + inputLen <= input.length) {
this.bout.write(input, inputOffset, inputLen);
}
if(this.bout.size() == 0) {
return null;
} else {
byte[] c3 = new byte[32];
byte[] tmp = this.bout.toByteArray();
byte[] c2;
byte[] c1bytes;
if(this.opMode == 1) {
c2 = tmp;
this.sm2Cipher.Encrypt(tmp);
this.sm2Cipher.Dofinal(c3);
byte[] bins1 = Pack.BNto32Bytes(this.c1.getX().toBigInteger());
c1bytes = Pack.BNto32Bytes(this.c1.getY().toBigInteger());
ByteArrayOutputStream bu1 = new ByteArrayOutputStream(65 + tmp.length + 32);
byte[] res = null;
bu1.write(4);
try {
bu1.write(bins1);
bu1.write(c1bytes);
bu1.write(c2);
bu1.write(c3);
res = bu1.toByteArray();
bu1.close();
} catch (IOException var12) {
;
}
return res;
} else if(tmp[0] != 4 && tmp.length < 98) {
throw new IllegalBlockSizeException("Invalid data.");
} else {
ByteArrayInputStream bins = new ByteArrayInputStream(tmp);
c1bytes = new byte[65];
c2 = new byte[tmp.length - 32 - 65];
try {
bins.read(c1bytes);
bins.read(c2);
bins.read(c3);
bins.close();
this.c1 = ((ECKeyParameters)this.keyParam).getParameters().getCurve().decodePoint(c1bytes);
if(this.c1.isInfinity()) {
throw new IllegalBlockSizeException("ECPoint is Infinity.");
}
} catch (IOException var13) {
;
} catch (RuntimeException var14) {
throw new BadPaddingException("Invalid data.");
}
this.sm2Cipher.Init_dec(((ECPrivateKeyParameters)this.keyParam).getD(), this.c1);
this.sm2Cipher.Decrypt(c2);
byte[] bu = new byte[32];
this.sm2Cipher.Dofinal(bu);
if(Arrays.equals(bu, c3)) {
return c2;
} else {
throw new IllegalBlockSizeException("Decrypt Error.");
}
}
}
}
protected int engineGetOutputSize(int inputLen) {
return 97 + inputLen;
}
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
byte[] res = this.engineDoFinal(input, inputOffset, inputLen);
if(res == null) {
return 0;
} else if(output.length - outputOffset < res.length) {
throw new ShortBufferException("Output Length:" + res.length);
} else {
System.arraycopy(res, 0, output, outputOffset, res.length);
return res.length;
}
}
protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
throw new InvalidKeyException("Unsupported.");
}
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException {
throw new InvalidKeyException("Unsupported.");
}
public static class SM2 extends CipherSpi {
public SM2() {
}
}
}
Clipher 的doFinal方法,最后调用的就是engineDoFinal,真正实现加解密算法的是在
public class SM2Cipher {
private int ct = 1;
private ECPoint p2;
private SM3Digest sm3keybase;
private SM3Digest sm3c3;
private byte[] key = new byte[32];
private byte keyOff = 0;
public SM2Cipher() {
}
private void Reset() {
this.sm3keybase = new SM3Digest();
this.sm3c3 = new SM3Digest();
byte[] p = Pack.BNto32Bytes(this.p2.getX().toBigInteger());
this.sm3keybase.update(p, 0, p.length);
this.sm3c3.update(p, 0, p.length);
p = Pack.BNto32Bytes(this.p2.getY().toBigInteger());
this.sm3keybase.update(p, 0, p.length);
this.ct = 1;
this.NextKey();
}
private void NextKey() {
SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
sm3keycur.update((byte)(this.ct >> 24 & 255));
sm3keycur.update((byte)(this.ct >> 16 & 255));
sm3keycur.update((byte)(this.ct >> 8 & 255));
sm3keycur.update((byte)(this.ct & 255));
sm3keycur.doFinal(this.key, 0);
this.keyOff = 0;
++this.ct;
}
public ECPoint Init_enc(ECPoint userKey, SecureRandom random) {
SM2CipherKey cpkey = SM2CipherKeyGen.getInstance(random).generateKey();
BigInteger k = cpkey.K;
ECPoint c1 = cpkey.Q;
this.p2 = userKey.multiply(k);
this.Reset();
return c1;
}
public void Encrypt(byte[] data) {
this.sm3c3.update(data, 0, data.length);
for(int i = 0; i < data.length; ++i) {
if(this.keyOff == this.key.length) {
this.NextKey();
}
data[i] ^= this.key[this.keyOff++];
}
}
public void Init_dec(BigInteger userD, ECPoint c1) {
this.p2 = c1.multiply(userD);
this.Reset();
}
public void Decrypt(byte[] data) {
for(int i = 0; i < data.length; ++i) {
if(this.keyOff == this.key.length) {
this.NextKey();
}
data[i] ^= this.key[this.keyOff++];
}
this.sm3c3.update(data, 0, data.length);
}
public void Dofinal(byte[] c3) {
byte[] p = Pack.BNto32Bytes(this.p2.getY().toBigInteger());
this.sm3c3.update(p, 0, p.length);
this.sm3c3.doFinal(c3, 0);
this.Reset();
}
}
看到这里,这个和之前网络上单独加解密的一样了吧。
签名部分SignatureSpi
public class SignatureSpi extends DSABase {
SignatureSpi(SignatureSpi.SM2B digest, DSA signer, DSAEncoder encoder) {
super(digest, signer, encoder);
}
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
ECPublicKeyParameters param = (ECPublicKeyParameters)ECUtil.generatePublicKeyParameter(publicKey);
((SignatureSpi.SM2B)this.digest).setPublicKey(param.getQ());
this.signer.init(false, param);
}
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
ECPoint pubKey = null;
ECPrivateKeyParameters param = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter(privateKey);
ECDomainParameters edp = param.getParameters();
DERBitString dps = null;
if(privateKey instanceof SM2PrivateKey) {
dps = ((SM2PrivateKey)privateKey).getPubKey();
}
if(dps != null) {
try {
pubKey = edp.getCurve().decodePoint(dps.getBytes());
} catch (RuntimeException var7) {
;
}
}
if(pubKey == null) {
pubKey = edp.getG().multiply(param.getD());
}
((SignatureSpi.SM2B)this.digest).setPublicKey(pubKey);
if(this.appRandom != null) {
this.signer.init(true, new ParametersWithRandom(param, this.appRandom));
} else {
this.signer.init(true, param);
}
}
protected void engineSetParameter(AlgorithmParameterSpec params) {
if(params instanceof SigParamSpec) {
DEROctetString dos = ((SigParamSpec)params).string;
if(dos != null) {
((SignatureSpi.SM2B)this.digest).setUserID(dos.getOctets());
}
SecureRandom random = ((SigParamSpec)params).random;
if(random != null) {
this.appRandom = random;
}
}
}
/** @deprecated */
@Deprecated
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
if(param.equalsIgnoreCase("USER_ID")) {
if(value instanceof String) {
String uid = (String)value;
if(uid.length() > 0) {
((SignatureSpi.SM2B)this.digest).setUserID(uid.getBytes());
}
} else {
if(!(value instanceof byte[])) {
throw new InvalidParameterException("Bad User ID.");
}
((SignatureSpi.SM2B)this.digest).setUserID((byte[])((byte[])value));
}
} else {
if(!param.equalsIgnoreCase("RANDOM") || !(value instanceof SecureRandom)) {
throw new InvalidParameterException("Unknown parameter.");
}
this.appRandom = (SecureRandom)value;
}
}
protected void engineUpdate(byte b) throws SignatureException {
((SignatureSpi.SM2B)this.digest).prepareZ();
this.digest.update(b);
}
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
((SignatureSpi.SM2B)this.digest).prepareZ();
this.digest.update(b, off, len);
}
private static class SM2B extends SM3Digest {
private static final byte[] def_id = "SM2 = \'Saabee Meet 2b\'.".getBytes();
private final byte[][] zConst = new byte[4][32];
private final X9ECParameters x9cp = ECUtil.getNamedCurveByName("SM2");
private byte[] user_ID;
private ECPoint pubKey;
public SM2B() {
Pack.BNto32Bytes(this.x9cp.getCurve().getA().toBigInteger(), this.zConst[0]);
Pack.BNto32Bytes(this.x9cp.getCurve().getB().toBigInteger(), this.zConst[1]);
Pack.BNto32Bytes(this.x9cp.getG().getX().toBigInteger(), this.zConst[2]);
Pack.BNto32Bytes(this.x9cp.getG().getY().toBigInteger(), this.zConst[3]);
this.user_ID = null;
this.pubKey = null;
}
public void setUserID(byte[] userID) {
this.user_ID = userID;
}
public void setPublicKey(ECPoint pubKey) {
this.pubKey = pubKey;
}
public void prepareZ() {
if(this.pubKey != null) {
this.reset();
byte[] p = this.user_ID;
if(p == null || p.length == 0) {
p = EC5Util.getSM2_UID();
if(p == null) {
p = def_id;
}
}
int len = p.length * 8;
this.update((byte)(len >> 8 & 255));
this.update((byte)(len & 255));
this.update(p, 0, p.length);
p = this.zConst[0];
this.update(p, 0, p.length);
p = this.zConst[1];
this.update(p, 0, p.length);
p = this.zConst[2];
this.update(p, 0, p.length);
p = this.zConst[3];
this.update(p, 0, p.length);
p = Pack.BNto32Bytes(this.pubKey.getX().toBigInteger());
this.update(p, 0, p.length);
p = Pack.BNto32Bytes(this.pubKey.getY().toBigInteger());
this.update(p, 0, p.length);
this.pubKey = null;
byte[] zVal = new byte[super.getDigestSize()];
this.doFinal(zVal, 0);
this.reset();
this.update(zVal, 0, zVal.length);
}
}
}
private static class StdDSAEncoder implements DSAEncoder {
private StdDSAEncoder() {
}
public byte[] encode(BigInteger r, BigInteger s) throws IOException {
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));
return (new DERSequence(v)).getEncoded("DER");
}
public BigInteger[] decode(byte[] encoding) throws IOException {
ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding);
BigInteger[] sig = new BigInteger[]{ASN1Integer.getInstance(s.getObjectAt(0)).getValue(), ASN1Integer.getInstance(s.getObjectAt(1)).getValue()};
return sig;
}
}
public static class SM3SM2 extends SignatureSpi {
public SM3SM2() {
super(new SignatureSpi.SM2B(), new SM2Signer(), new SignatureSpi.StdDSAEncoder());
}
}
}
代码比较多,但是基本逻辑照着RSA的来,稍后我会整理一下,把全部代码发出来的。