org.bouncycastle.crypto.engines.SM2Engine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-jdk15on Show documentation
Show all versions of bcprov-jdk15on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to JDK 1.8.
The newest version!
package org.bouncycastle.crypto.engines;
import java.math.BigInteger;
import java.security.SecureRandom;
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.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
/**
* SM2 public key encryption engine - based on https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02.
*/
public class SM2Engine
{
private final Digest digest;
private boolean forEncryption;
private ECKeyParameters ecKey;
private ECDomainParameters ecParams;
private int curveLength;
private SecureRandom random;
public SM2Engine()
{
this(new SM3Digest());
}
public SM2Engine(Digest digest)
{
this.digest = digest;
}
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;
}
public byte[] processBlock(
byte[] in,
int inOff,
int inLen)
throws InvalidCipherTextException
{
if (forEncryption)
{
return encrypt(in, inOff, inLen);
}
else
{
return decrypt(in, inOff, inLen);
}
}
private byte[] encrypt(byte[] in, int inOff, int inLen)
throws InvalidCipherTextException
{
byte[] c2 = new byte[inLen];
System.arraycopy(in, inOff, c2, 0, c2.length);
byte[] c1;
ECPoint kPB;
do
{
BigInteger k = nextK();
ECPoint c1P = ecParams.getG().multiply(k).normalize();
c1 = c1P.getEncoded(false);
kPB = ((ECPublicKeyParameters)ecKey).getQ().multiply(k).normalize();
kdf(digest, kPB, c2);
}
while (notEncrypted(c2, in, inOff));
byte[] c3 = new byte[digest.getDigestSize()];
addFieldElement(digest, kPB.getAffineXCoord());
digest.update(in, inOff, inLen);
addFieldElement(digest, kPB.getAffineYCoord());
digest.doFinal(c3, 0);
return Arrays.concatenate(c1, c2, c3);
}
private byte[] decrypt(byte[] in, int inOff, int inLen)
throws InvalidCipherTextException
{
byte[] c1 = new byte[curveLength * 2 + 1];
System.arraycopy(in, inOff, c1, 0, c1.length);
ECPoint c1P = ecParams.getCurve().decodePoint(c1);
ECPoint s = c1P.multiply(ecParams.getH());
if (s.isInfinity())
{
throw new InvalidCipherTextException("[h]C1 at infinity");
}
c1P = c1P.multiply(((ECPrivateKeyParameters)ecKey).getD()).normalize();
byte[] c2 = new byte[inLen - c1.length - digest.getDigestSize()];
System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);
kdf(digest, c1P, c2);
byte[] c3 = new byte[digest.getDigestSize()];
addFieldElement(digest, c1P.getAffineXCoord());
digest.update(c2, 0, c2.length);
addFieldElement(digest, c1P.getAffineYCoord());
digest.doFinal(c3, 0);
int check = 0;
for (int i = 0; i != c3.length; i++)
{
check |= c3[i] ^ in[c1.length + c2.length + i];
}
clearBlock(c1);
clearBlock(c3);
if (check != 0)
{
clearBlock(c2);
throw new InvalidCipherTextException("invalid cipher text");
}
return c2;
}
private boolean notEncrypted(byte[] encData, byte[] in, int inOff)
{
for (int i = 0; i != encData.length; i++)
{
if (encData[i] != in[inOff])
{
return false;
}
}
return true;
}
private void kdf(Digest digest, ECPoint c1, byte[] encData)
{
int ct = 1;
int v = digest.getDigestSize();
byte[] buf = new byte[digest.getDigestSize()];
int off = 0;
for (int i = 1; i <= ((encData.length + v - 1) / v); i++)
{
addFieldElement(digest, c1.getAffineXCoord());
addFieldElement(digest, c1.getAffineYCoord());
digest.update((byte)(ct >> 24));
digest.update((byte)(ct >> 16));
digest.update((byte)(ct >> 8));
digest.update((byte)ct);
digest.doFinal(buf, 0);
if (off + buf.length < encData.length)
{
xor(encData, buf, off, buf.length);
}
else
{
xor(encData, buf, off, encData.length - off);
}
off += buf.length;
ct++;
}
}
private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining)
{
for (int i = 0; i != dRemaining; i++)
{
data[dOff + i] ^= kdfOut[i];
}
}
private BigInteger nextK()
{
int qBitLength = ecParams.getN().bitLength();
BigInteger k;
do
{
k = new BigInteger(qBitLength, random);
}
while (k.equals(ECConstants.ZERO) || k.compareTo(ecParams.getN()) >= 0);
return k;
}
private void addFieldElement(Digest digest, ECFieldElement v)
{
byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());
digest.update(p, 0, p.length);
}
/**
* clear possible sensitive data
*/
private void clearBlock(
byte[] block)
{
for (int i = 0; i != block.length; i++)
{
block[i] = 0;
}
}
}