From 36bf16353eb4d628ae2f8141b3f20db22d9e3475 Mon Sep 17 00:00:00 2001 From: liulu Date: Thu, 12 Dec 2024 15:11:21 +0800 Subject: [PATCH] sdf api --- .../com/sunyard/chsm/sdf/SdfApiService.java | 46 +++ .../chsm/sdf/adapter/BcSdfApiAdaptor.java | 64 +++- .../chsm/sdf/adapter/JnaSdfAdaptor.java | 38 ++- .../chsm/sdf/adapter/SdfApiAdapter.java | 31 +- .../com/sunyard/chsm/sdf/model/EccPriKey.java | 10 +- .../sunyard/chsm/sdf/model/EccSignature.java | 25 ++ .../chsm/utils/gm/SM2PreprocessSigner.java | 294 ++++++++++++++++++ .../sunyard/chsm/sdf/SingleSdfApiService.java | 58 ++++ .../chsm/pool/LoadBalancedSdfApiService.java | 61 ++++ 9 files changed, 609 insertions(+), 18 deletions(-) create mode 100644 chsm-common/src/main/java/com/sunyard/chsm/utils/gm/SM2PreprocessSigner.java diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java index f61ca0b..964be67 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java @@ -2,7 +2,9 @@ package com.sunyard.chsm.sdf; import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccSignature; /** @@ -70,6 +72,50 @@ public interface SdfApiService { */ EccKey genKeyPairEcc(); + /** + * 外部密钥ECC签名 + * + * @param privateKey ECC私钥 + * @param pucData 缓冲区指针,用于存放外部输入的数据 + * @param userId 签名者id + * @return pucSignature 返回签名值数据 + */ + EccSignature externalSignWithId(byte[] privateKey, byte[] pucData, byte[] userId); + + EccSignature externalSign(byte[] privateKey, byte[] pucData); + + /** + * 外部密钥ECC验证 + * + * @param publicKey ECC公钥 + * @param pubData 原文 + * @param userId 签名者id + * @param signData 外部签名数据 + * @return 0 成功; 非0 失败,返回错误代码 + */ + boolean externalVerifyWithId(byte[] publicKey, byte[] pubData, byte[] signData, byte[] userId); + + boolean externalVerify(byte[] publicKey, byte[] pubData, byte[] signData); + + + /** + * 外部密钥ECC公钥加密 + * + * @param publicKey 外部ECC公钥结构 + * @param pucData 缓冲区指针,用于存放外部输入的数据 + * @return pucEncData 返回数据密文 + */ + EccCipher externalEncrypt(byte[] publicKey, byte[] pucData); + + /** + * 外部密钥ECC私钥解密 + * + * @param privateKey 外部ECC私钥结构 + * @param encData 缓冲区指针,用于存放输入的数据密文 + * @return pucData 返回数据明文 + */ + byte[] externalDecrypt(byte[] privateKey, byte[] encData); + /** * 计算MAC * diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/BcSdfApiAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/BcSdfApiAdaptor.java index 4a2b29c..24c3c23 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/BcSdfApiAdaptor.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/BcSdfApiAdaptor.java @@ -4,16 +4,21 @@ import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.DeviceInfo; import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; import com.sunyard.chsm.sdf.util.LangUtils; import com.sunyard.chsm.utils.gm.BCECUtils; import com.sunyard.chsm.utils.gm.BCSM2Utils; import com.sunyard.chsm.utils.gm.BCSM4Utils; import lombok.SneakyThrows; +import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.digests.NullDigest; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.math.ec.ECPoint; @@ -130,6 +135,52 @@ public class BcSdfApiAdaptor implements SdfApiAdapter { } } + @Override + public EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData) { + ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD()); + try { + SM2Signer signer = new SM2Signer(new NullDigest()); + signer.init(true, pri); + signer.update(pucData, 0, pucData.length); + byte[] signature = signer.generateSignature(); + return EccSignature.fromBytes(signature); + } catch (CryptoException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature) { + ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(publicKey.getX(), publicKey.getY()); + SM2Signer verifier = new SM2Signer(new NullDigest()); + verifier.init(false, pub); + // 验证数据 + verifier.update(pucData, 0, pucData.length); + return verifier.verifySignature(pucSignature.getRawSignBytes()); + } + + @Override + public EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData) { + ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(pubKey.getX(), pubKey.getY()); + try { + byte[] encrypt = BCSM2Utils.encrypt(pub, pucData); + return EccCipher.fromBytes(encrypt); + } catch (InvalidCipherTextException e) { + throw new IllegalArgumentException(e); + } + } + + + @Override + public byte[] externalDecryptECC(String sessionHandle, EccPriKey pucPrivateKeyEcc, EccCipher pucEncData) { + ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD()); + try { + return BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, pucEncData.getC1C3C2Bytes())); + } catch (InvalidCipherTextException e) { + throw new IllegalArgumentException(e); + } + } + @Override public String importKeyWithISKECC(String sessionHandle, int uiIskIndex, EccCipher eccCipher) { ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD()); @@ -159,19 +210,6 @@ public class BcSdfApiAdaptor implements SdfApiAdapter { return 0; } - - @Override - public EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData) { - ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(pubKey.getX(), pubKey.getY()); - try { - byte[] encrypt = BCSM2Utils.encrypt(pub, pucData); - return EccCipher.fromBytes(encrypt); - } catch (InvalidCipherTextException e) { - throw new IllegalArgumentException(e); - } - } - - @Override @SneakyThrows public byte[] symEncrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucData) { diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java index 652320b..0bfda4f 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java @@ -4,12 +4,14 @@ import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.sdf.context.SdrCode; import com.sunyard.chsm.sdf.lib.SdfLibrary; import com.sunyard.chsm.sdf.model.DeviceInfo; import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; import com.sunyard.chsm.sdf.model.EccPriKey; import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; import com.sunyard.chsm.sdf.model.SDF_DeviceInfo; import com.sunyard.chsm.sdf.util.LangUtils; import lombok.RequiredArgsConstructor; @@ -17,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -169,10 +172,11 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter { @Override public int destroyKey(String sessionHandle, String hKeyHandle) { - return 0; + Pointer hSessionHandle = getSessionHandle(sessionHandle); + Pointer keyHandle = KEY_HANDLE_CONTEXT.remove(hKeyHandle); + return sdfLibrary.SDF_DestroyKey(hSessionHandle, keyHandle); } - @Override public EccCipher exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, EccPubKey pubKey, EccCipher pucEncDateIn) { Pointer hSessionHandle = getSessionHandle(sessionHandle); @@ -183,6 +187,25 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter { return EccCipher.fromBytes(pucEncDateOut); } + @Override + public EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData) { + byte[] eccSignature = new byte[128]; + int uiDataLength = pucData.length; + Pointer hSessionHandle = getSessionHandle(sessionHandle); + sdfLibrary.SDF_ExternalSign_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_1), privateKey.toSdfData(), + pucData, uiDataLength, eccSignature); + return EccSignature.fromBytes(eccSignature); + } + + @Override + public boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature) { + int uiDataLength = pucData.length; + Pointer hSessionHandle = getSessionHandle(sessionHandle); + int result = sdfLibrary.SDF_ExternalVerify_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_1), publicKey.toSdfData(), + pucData, uiDataLength, pucSignature.toSdfData()); + return Objects.equals(result, SdrCode.SDR_OK.getCode()); + } + @Override public EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData) { byte[] encData = new byte[128 + 32 + 4 + pucData.length]; @@ -191,6 +214,15 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter { return EccCipher.fromBytes(encData); } + @Override + public byte[] externalDecryptECC(String sessionHandle, EccPriKey priKey, EccCipher pucEncData) { + Pointer hSessionHandle = getSessionHandle(sessionHandle); + byte[] pucData = new byte[pucEncData.getL()]; + IntByReference puiLength = new IntByReference(); + sdfLibrary.SDF_ExternalDecrypt_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_3), priKey.toSdfData(), pucEncData.toSdfData(), pucData, puiLength); + return pucData; + } + @Override public byte[] symEncrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucData) { @@ -225,7 +257,7 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter { Pointer hKeyHandle = getKeyHandle(keyHandle); int uiMacLength = pucData.length; - byte[] pucMac = new byte[uiMacLength]; + byte[] pucMac = new byte[16]; IntByReference puiLength = new IntByReference(); sdfLibrary.SDF_CalculateMAC(hSessionHandle, hKeyHandle, getAlgId(uiAlg), pucIv, pucData, uiMacLength, pucMac, puiLength); diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java index 6bef870..6875435 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java @@ -4,7 +4,9 @@ import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.DeviceInfo; import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; /** * @author liulu @@ -96,9 +98,27 @@ public interface SdfApiAdapter { */ int destroyKey(String sessionHandle, String hKeyHandle); - EccCipher exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, EccPubKey pubKey, EccCipher pucEncDateIn); + /** + * 外部密钥ECC签名 + * + * @param privateKey ECC私钥 + * @param pucData 缓冲区指针,用于存放外部输入的数据 + * @return pucSignature 返回签名值数据 + */ + EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData); + + /** + * 外部密钥ECC验证 + * + * @param publicKey ECC公钥 + * @param pucData 缓冲区指针,用于存放外部输入的数据 + * @param pucSignature 缓冲区指针,用于存放输入的签名值数据 + * @return 0 成功; 非0 失败,返回错误代码 + */ + boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature); + /** * 外部密钥ECC公钥加密 * @@ -108,6 +128,15 @@ public interface SdfApiAdapter { */ EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData); + /** + * 外部密钥ECC私钥解密 + * + * @param pucPrivateKeyEcc 外部ECC私钥结构 + * @param pucEncData 缓冲区指针,用于存放输入的数据密文 + * @return pucData 返回数据明文 + */ + byte[] externalDecryptECC(String sessionHandle, EccPriKey pucPrivateKeyEcc, EccCipher pucEncData); + /** * 对称加密 * diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccPriKey.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccPriKey.java index 8614e4e..07034c8 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccPriKey.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccPriKey.java @@ -3,6 +3,7 @@ package com.sunyard.chsm.sdf.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.bouncycastle.util.encoders.Hex; import org.springframework.util.Assert; import java.util.Arrays; @@ -22,8 +23,15 @@ public class EccPriKey { // 私 钥 private byte[] D; + public byte[] toSdfData() { + byte[] sdf = new byte[68]; + System.arraycopy(Hex.decode("00010000"), 0, sdf, 0, 4); + System.arraycopy(D, 0, sdf, 36, 32); + return sdf; + } + public static EccPriKey fromBytes(byte[] priKey) { - Assert.notNull(priKey, "公钥数据不能为null"); + Assert.notNull(priKey, "私钥数据不能为null"); if (Objects.equals(priKey.length, 68)) { // 00010000 priKey = Arrays.copyOfRange(priKey, 4, priKey.length); diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccSignature.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccSignature.java index 77307d7..b809193 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccSignature.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/model/EccSignature.java @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Arrays; + /** * @author liulu * @version V1.0 @@ -27,5 +29,28 @@ public class EccSignature { return signData; } + public byte[] toSdfData() { + byte[] res = new byte[128]; + System.arraycopy(r, 0, res, 32, 32); + System.arraycopy(s, 0, res, 96, 32); + return res; + } + + public static EccSignature fromBytes(byte[] sign) { + if (sign == null || sign.length == 0) { + return null; + } + if (sign.length == 64) { + byte[] r = Arrays.copyOfRange(sign, 0, 32); + byte[] s = Arrays.copyOfRange(sign, 32, 64); + return new EccSignature(r, s); + } else if (sign.length == 128) { + byte[] r = Arrays.copyOfRange(sign, 32, 64); + byte[] s = Arrays.copyOfRange(sign, 96, 128); + return new EccSignature(r, s); + } else { + throw new IllegalArgumentException("ECC签名数据格式错误"); + } + } } diff --git a/chsm-common/src/main/java/com/sunyard/chsm/utils/gm/SM2PreprocessSigner.java b/chsm-common/src/main/java/com/sunyard/chsm/utils/gm/SM2PreprocessSigner.java new file mode 100644 index 0000000..e479f63 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/utils/gm/SM2PreprocessSigner.java @@ -0,0 +1,294 @@ +package com.sunyard.chsm.utils.gm; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.Digest; +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.ParametersWithID; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.DSAKCalculator; +import org.bouncycastle.crypto.signers.RandomDSAKCalculator; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECConstants; +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.encoders.Hex; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * 有的国密需求是用户可以自己做预处理,签名验签只是对预处理的结果进行签名和验签 + */ +public class SM2PreprocessSigner implements ECConstants { + private static final int DIGEST_LENGTH = 32; // bytes + + private final DSAKCalculator kCalculator = new RandomDSAKCalculator(); + private Digest digest = null; + + private ECDomainParameters ecParams; + private ECPoint pubPoint; + private ECKeyParameters ecKey; + private byte[] userID; + + /** + * 初始化 + * + * @param forSigning true表示用于签名,false表示用于验签 + * @param param + */ + public void init(boolean forSigning, CipherParameters param) { + init(forSigning, new SM3Digest(), param); + } + + /** + * 初始化 + * + * @param forSigning true表示用于签名,false表示用于验签 + * @param digest SM2算法的话,一般是采用SM3摘要算法 + * @param param + * @throws RuntimeException + */ + public void init(boolean forSigning, Digest digest, CipherParameters param) throws RuntimeException { + CipherParameters baseParam; + + if (digest.getDigestSize() != DIGEST_LENGTH) { + throw new RuntimeException("Digest size must be " + DIGEST_LENGTH); + } + this.digest = digest; + + if (param instanceof ParametersWithID) { + baseParam = ((ParametersWithID) param).getParameters(); + userID = ((ParametersWithID) param).getID(); + } else { + baseParam = param; + userID = Hex.decode("31323334353637383132333435363738"); // the default value + } + + if (forSigning) { + if (baseParam instanceof ParametersWithRandom) { + ParametersWithRandom rParam = (ParametersWithRandom) baseParam; + + ecKey = (ECKeyParameters) rParam.getParameters(); + ecParams = ecKey.getParameters(); + kCalculator.init(ecParams.getN(), rParam.getRandom()); + } else { + ecKey = (ECKeyParameters) baseParam; + ecParams = ecKey.getParameters(); + kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom()); + } + pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), ((ECPrivateKeyParameters) ecKey).getD()).normalize(); + } else { + ecKey = (ECKeyParameters) baseParam; + ecParams = ecKey.getParameters(); + pubPoint = ((ECPublicKeyParameters) ecKey).getQ(); + } + } + + /** + * 预处理,辅助方法 + * ZA=H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。 + * M=ZA ∥ M; + * e = Hv(M) + * + * @return + */ + public byte[] preprocess(byte[] m) { + return preprocess(m, 0, m.length); + } + + + public byte[] preprocess(byte[] m, int off, int len) { + byte[] z = getZ(userID); + digest.update(z, 0, z.length); + digest.update(m, off, len); + byte[] eHash = new byte[DIGEST_LENGTH]; + digest.doFinal(eHash, 0); + return eHash; + } + + public boolean verifySignature(byte[] eHash, byte[] signature) { + try { + BigInteger[] rs = derDecode(signature); + if (rs != null) { + return verifySignature(eHash, rs[0], rs[1]); + } + } catch (IOException e) { + } + + return false; + } + + public void reset() { + digest.reset(); + } + + public byte[] generateSignature(byte[] eHash) throws CryptoException { + BigInteger n = ecParams.getN(); + BigInteger e = calculateE(eHash); + BigInteger d = ((ECPrivateKeyParameters) ecKey).getD(); + + BigInteger r, s; + + ECMultiplier basePointMultiplier = createBasePointMultiplier(); + + // 5.2.1 Draft RFC: SM2 Public Key Algorithms + do // generate s + { + BigInteger k; + do // generate r + { + // A3 + k = kCalculator.nextK(); + + // A4 + ECPoint p = basePointMultiplier.multiply(ecParams.getG(), k).normalize(); + + // A5 + r = e.add(p.getAffineXCoord().toBigInteger()).mod(n); + } + while (r.equals(ZERO) || r.add(k).equals(n)); + + // A6 + BigInteger dPlus1ModN = d.add(ONE).modInverse(n); + + s = k.subtract(r.multiply(d)).mod(n); + s = dPlus1ModN.multiply(s).mod(n); + } + while (s.equals(ZERO)); + + // A7 + try { + return derEncode(r, s); + } catch (IOException ex) { + throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex); + } + } + + private boolean verifySignature(byte[] eHash, BigInteger r, BigInteger s) { + BigInteger n = ecParams.getN(); + + // 5.3.1 Draft RFC: SM2 Public Key Algorithms + // B1 + if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) { + return false; + } + + // B2 + if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) { + return false; + } + + // B3 eHash + + // B4 + BigInteger e = calculateE(eHash); + + // B5 + BigInteger t = r.add(s).mod(n); + if (t.equals(ZERO)) { + return false; + } + + // B6 + ECPoint q = ((ECPublicKeyParameters) ecKey).getQ(); + ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize(); + if (x1y1.isInfinity()) { + return false; + } + + // B7 + BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n); + + return expectedR.equals(r); + } + + private byte[] digestDoFinal() { + byte[] result = new byte[digest.getDigestSize()]; + digest.doFinal(result, 0); + + reset(); + + return result; + } + + private byte[] getZ(byte[] userID) { + digest.reset(); + + addUserID(digest, userID); + + addFieldElement(digest, ecParams.getCurve().getA()); + addFieldElement(digest, ecParams.getCurve().getB()); + addFieldElement(digest, ecParams.getG().getAffineXCoord()); + addFieldElement(digest, ecParams.getG().getAffineYCoord()); + addFieldElement(digest, pubPoint.getAffineXCoord()); + addFieldElement(digest, pubPoint.getAffineYCoord()); + + byte[] result = new byte[digest.getDigestSize()]; + + digest.doFinal(result, 0); + + return result; + } + + private void addUserID(Digest digest, byte[] userID) { + int len = userID.length * 8; + digest.update((byte) (len >> 8 & 0xFF)); + digest.update((byte) (len & 0xFF)); + digest.update(userID, 0, userID.length); + } + + private void addFieldElement(Digest digest, ECFieldElement v) { + byte[] p = v.getEncoded(); + digest.update(p, 0, p.length); + } + + protected ECMultiplier createBasePointMultiplier() { + return new FixedPointCombMultiplier(); + } + + protected BigInteger calculateE(byte[] message) { + return new BigInteger(1, message); + } + + protected BigInteger[] derDecode(byte[] encoding) + throws IOException { + ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(encoding)); + if (seq.size() != 2) { + return null; + } + + BigInteger r = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue(); + BigInteger s = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue(); + + byte[] expectedEncoding = derEncode(r, s); + if (!Arrays.constantTimeAreEqual(expectedEncoding, encoding)) { + return null; + } + + return new BigInteger[]{r, s}; + } + + protected byte[] derEncode(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(ASN1Encoding.DER); + } +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java index 7fdfb0b..2afde32 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java @@ -11,11 +11,18 @@ import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.DeviceInfo; import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; +import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; import com.sunyard.chsm.sdf.util.PaddingUtil; import com.sunyard.chsm.service.TmkService; +import com.sunyard.chsm.utils.gm.BCECUtils; import com.sunyard.chsm.utils.gm.BCSM3Utils; +import com.sunyard.chsm.utils.gm.SM2PreprocessSigner; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ParametersWithID; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import org.springframework.util.Assert; @@ -80,6 +87,57 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean { return sdfApiAdapter.generateKeyPairECC(sessionHandle, AlgId.SGD_SM2_1); } + @Override + public EccSignature externalSign(byte[] privateKey, byte[] pucData) { + return externalSignWithId(privateKey, pucData, new byte[0]); + } + + @Override + public EccSignature externalSignWithId(byte[] privateKey, byte[] pucData, byte[] userId) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + CipherParameters keyParameters = BCECUtils.createECPrivateKeyParameters(sdfEccPrivateKey.getD()); + if (userId != null && userId.length > 0) { + keyParameters = new ParametersWithID(keyParameters, userId); + } + signer.init(true, keyParameters); + byte[] preHash = signer.preprocess(pucData); + return sdfApiAdapter.externalSignECC(sessionHandle, sdfEccPrivateKey, preHash); + } + + @Override + public boolean externalVerifyWithId(byte[] publicKey, byte[] pubData, byte[] signData, byte[] userId) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + CipherParameters parameters = BCECUtils.createECPublicKeyParameters(eccPublicKey.getX(), eccPublicKey.getY()); + if (userId != null && userId.length > 0) { + parameters = new ParametersWithID(parameters, userId); + } + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + signer.init(false, parameters); + byte[] preHash = signer.preprocess(pubData); + EccSignature eccSignature = EccSignature.fromBytes(signData); + + return sdfApiAdapter.externalVerifyECC(sessionHandle, eccPublicKey, preHash, eccSignature); + } + + @Override + public boolean externalVerify(byte[] publicKey, byte[] pubData, byte[] signData) { + return externalVerifyWithId(publicKey, pubData, signData, new byte[0]); + } + + @Override + public EccCipher externalEncrypt(byte[] publicKey, byte[] pucData) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + return sdfApiAdapter.externalEncryptECC(sessionHandle, eccPublicKey, pucData); + } + + @Override + public byte[] externalDecrypt(byte[] privateKey, byte[] encData) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + EccCipher eccCipher = EccCipher.fromBytes(encData); + return sdfApiAdapter.externalDecryptECC(sessionHandle, sdfEccPrivateKey, eccCipher); + } + @Override public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) { checkStatus(); diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java index 18fc70a..319b2f5 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java @@ -2,10 +2,18 @@ package com.sunyard.chsm.pool; import com.sunyard.chsm.sdf.SdfApiService; import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; +import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; import com.sunyard.chsm.sdf.util.PaddingUtil; +import com.sunyard.chsm.utils.gm.BCECUtils; import com.sunyard.chsm.utils.gm.BCSM3Utils; +import com.sunyard.chsm.utils.gm.SM2PreprocessSigner; import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ParametersWithID; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -63,6 +71,59 @@ public class LoadBalancedSdfApiService implements SdfApiService { return apply(s -> s.getSdfApiAdapter().generateKeyPairECC(s.getSessionHandle(), AlgId.SGD_SM2_1)); } + @Override + public EccSignature externalSign(byte[] privateKey, byte[] pucData) { + return externalSignWithId(privateKey, pucData, new byte[0]); + } + + @Override + public EccSignature externalSignWithId(byte[] privateKey, byte[] pucData, byte[] userId) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + CipherParameters keyParameters = BCECUtils.createECPrivateKeyParameters(sdfEccPrivateKey.getD()); + if (userId != null && userId.length > 0) { + keyParameters = new ParametersWithID(keyParameters, userId); + } + signer.init(true, keyParameters); + byte[] preHash = signer.preprocess(pucData); + + return apply(s -> s.getSdfApiAdapter().externalSignECC(s.getSessionHandle(), sdfEccPrivateKey, preHash)); + } + + @Override + public boolean externalVerifyWithId(byte[] publicKey, byte[] pubData, byte[] signData, byte[] userId) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + CipherParameters parameters = BCECUtils.createECPublicKeyParameters(eccPublicKey.getX(), eccPublicKey.getY()); + if (userId != null && userId.length > 0) { + parameters = new ParametersWithID(parameters, userId); + } + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + signer.init(false, parameters); + byte[] preHash = signer.preprocess(pubData); + EccSignature eccSignature = EccSignature.fromBytes(signData); + + return apply(s -> s.getSdfApiAdapter().externalVerifyECC(s.getSessionHandle(), + eccPublicKey, preHash, eccSignature)); + } + + @Override + public boolean externalVerify(byte[] publicKey, byte[] pubData, byte[] signData) { + return externalVerifyWithId(publicKey, pubData, signData, new byte[0]); + } + + @Override + public EccCipher externalEncrypt(byte[] publicKey, byte[] pucData) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + return apply(s -> s.getSdfApiAdapter().externalEncryptECC(s.getSessionHandle(), eccPublicKey, pucData)); + } + + @Override + public byte[] externalDecrypt(byte[] privateKey, byte[] encData) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + EccCipher eccCipher = EccCipher.fromBytes(encData); + return apply(s -> s.getSdfApiAdapter().externalDecryptECC(s.getSessionHandle(), sdfEccPrivateKey, eccCipher)); + } + @Override public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) { return apply(s -> {