mirror of
https://gitee.com/BDWare/gmhelper
synced 2025-01-10 09:54:09 +00:00
initial commit
This commit is contained in:
parent
38735bc6bf
commit
9307363b4a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
|
build
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
|
||||||
|
36
build.gradle
Normal file
36
build.gradle
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java-library'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyLibs(type: Copy) {
|
||||||
|
from configurations.runtimeClasspath
|
||||||
|
into "./build/output/libs/"
|
||||||
|
}
|
||||||
|
|
||||||
|
//task copyJar(type: Exec, dependsOn: [":gmhelper:jar", ":gmhelper:copyLibs"]) {
|
||||||
|
// println("copyJar start")
|
||||||
|
// commandLine "cp", "./build/libs/$project.name-$version" + ".jar", "./build/output/gmhelper.jar"
|
||||||
|
//}
|
||||||
|
|
||||||
|
task copyJar(type: Copy, dependsOn: [":gmhelper:jar", ":gmhelper:copyLibs"]) {
|
||||||
|
from "./build/libs/$project.name-${project.version}.jar"
|
||||||
|
into "./build/output"
|
||||||
|
rename { String fileName -> "gmhelper.jar" }
|
||||||
|
doFirst {
|
||||||
|
println "copyJar start"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api 'com.google.code.gson:gson:2.8.8'
|
||||||
|
api 'org.bouncycastle:bcpkix-jdk15on:1.69'
|
||||||
|
api 'org.bouncycastle:bcprov-jdk15on:1.69'
|
||||||
|
|
||||||
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
}
|
523
src/main/java/org.zz/gmhelper/BCECUtil.java
Normal file
523
src/main/java/org.zz/gmhelper/BCECUtil.java
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.*;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x9.X962Parameters;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECPoint;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||||
|
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
|
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
|
import org.bouncycastle.crypto.params.*;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
||||||
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
||||||
|
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||||
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
|
import org.bouncycastle.util.io.pem.PemWriter;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.ECGenParameterSpec;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个工具类的方法,也适用于其他基于BC库的ECC算法
|
||||||
|
*/
|
||||||
|
public class BCECUtil {
|
||||||
|
public static final String ALGO_NAME_EC = "EC";
|
||||||
|
public static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
|
||||||
|
public static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成ECC密钥对
|
||||||
|
*
|
||||||
|
* @return ECC密钥对
|
||||||
|
*/
|
||||||
|
public static AsymmetricCipherKeyPair generateKeyPairParameter(
|
||||||
|
ECDomainParameters domainParameters, SecureRandom random) {
|
||||||
|
ECKeyGenerationParameters keyGenerationParams =
|
||||||
|
new ECKeyGenerationParameters(domainParameters, random);
|
||||||
|
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
|
||||||
|
keyGen.init(keyGenerationParams);
|
||||||
|
return keyGen.generateKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random)
|
||||||
|
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
|
KeyPairGenerator kpg =
|
||||||
|
KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
ECParameterSpec parameterSpec =
|
||||||
|
new ECParameterSpec(
|
||||||
|
domainParameters.getCurve(),
|
||||||
|
domainParameters.getG(),
|
||||||
|
domainParameters.getN(),
|
||||||
|
domainParameters.getH());
|
||||||
|
kpg.initialize(parameterSpec, (null == random ? new SecureRandom() : random));
|
||||||
|
return kpg.generateKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCurveLength(ECKeyParameters ecKey) {
|
||||||
|
return getCurveLength(ecKey.getParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCurveLength(ECDomainParameters domainParams) {
|
||||||
|
return (domainParams.getCurve().getFieldSize() + 7) / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) {
|
||||||
|
if (src.length == curveLength) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] result = new byte[curveLength];
|
||||||
|
if (src.length > curveLength) {
|
||||||
|
System.arraycopy(src, src.length - result.length, result, 0, result.length);
|
||||||
|
} else {
|
||||||
|
System.arraycopy(src, 0, result, result.length - src.length, src.length);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dHex 十六进制字符串形式的私钥d值,如果是SM2算法,Hex字符串长度应该是64(即32字节)
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters createECPrivateKeyParameters(
|
||||||
|
String dHex, ECDomainParameters domainParameters) {
|
||||||
|
return createECPrivateKeyParameters(ByteUtils.fromHexString(dHex), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dBytes 字节数组形式的私钥d值,如果是SM2算法,应该是32字节
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters createECPrivateKeyParameters(
|
||||||
|
byte[] dBytes, ECDomainParameters domainParameters) {
|
||||||
|
return createECPrivateKeyParameters(new BigInteger(1, dBytes), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param d 大数形式的私钥d值
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters createECPrivateKeyParameters(
|
||||||
|
BigInteger d, ECDomainParameters domainParameters) {
|
||||||
|
return new ECPrivateKeyParameters(d, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据EC私钥构造EC公钥
|
||||||
|
*
|
||||||
|
* @param priKey ECC私钥参数对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters buildECPublicKeyByPrivateKey(ECPrivateKeyParameters priKey) {
|
||||||
|
ECDomainParameters domainParameters = priKey.getParameters();
|
||||||
|
ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), priKey.getD());
|
||||||
|
return new ECPublicKeyParameters(q, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x 大数形式的公钥x分量
|
||||||
|
* @param y 大数形式的公钥y分量
|
||||||
|
* @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters createECPublicKeyParameters(
|
||||||
|
BigInteger x, BigInteger y, ECCurve curve, ECDomainParameters domainParameters) {
|
||||||
|
return createECPublicKeyParameters(x.toByteArray(), y.toByteArray(), curve, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param xHex 十六进制形式的公钥x分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
|
||||||
|
* @param yHex 十六进制形式的公钥y分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
|
||||||
|
* @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters createECPublicKeyParameters(
|
||||||
|
String xHex, String yHex, ECCurve curve, ECDomainParameters domainParameters) {
|
||||||
|
return createECPublicKeyParameters(
|
||||||
|
ByteUtils.fromHexString(xHex), ByteUtils.fromHexString(yHex), curve, domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param xBytes 十六进制形式的公钥x分量,如果是SM2算法,应该是32字节
|
||||||
|
* @param yBytes 十六进制形式的公钥y分量,如果是SM2算法,应该是32字节
|
||||||
|
* @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ECPublicKeyParameters createECPublicKeyParameters(
|
||||||
|
byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) {
|
||||||
|
final byte uncompressedFlag = 0x04;
|
||||||
|
int curveLength = getCurveLength(domainParameters);
|
||||||
|
xBytes = fixToCurveLengthBytes(curveLength, xBytes);
|
||||||
|
yBytes = fixToCurveLengthBytes(curveLength, yBytes);
|
||||||
|
byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length];
|
||||||
|
encodedPubKey[0] = uncompressedFlag;
|
||||||
|
System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length);
|
||||||
|
System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length);
|
||||||
|
return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param str 十六进制形式的公钥x分量,如果是SM2算法,应该是32字节
|
||||||
|
* @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
|
||||||
|
* @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static ECPublicKeyParameters createECPublicKeyFromStrParameters(
|
||||||
|
String str, ECCurve curve, ECDomainParameters domainParameters) {
|
||||||
|
return new ECPublicKeyParameters(
|
||||||
|
curve.decodePoint(ByteUtils.fromHexString(str)), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) {
|
||||||
|
ECParameterSpec parameterSpec = ecPriKey.getParameters();
|
||||||
|
ECDomainParameters domainParameters =
|
||||||
|
new ECDomainParameters(
|
||||||
|
parameterSpec.getCurve(),
|
||||||
|
parameterSpec.getG(),
|
||||||
|
parameterSpec.getN(),
|
||||||
|
parameterSpec.getH());
|
||||||
|
return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) {
|
||||||
|
ECParameterSpec parameterSpec = ecPubKey.getParameters();
|
||||||
|
ECDomainParameters domainParameters =
|
||||||
|
new ECDomainParameters(
|
||||||
|
parameterSpec.getCurve(),
|
||||||
|
parameterSpec.getG(),
|
||||||
|
parameterSpec.getN(),
|
||||||
|
parameterSpec.getH());
|
||||||
|
return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(
|
||||||
|
SubjectPublicKeyInfo subPubInfo)
|
||||||
|
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException,
|
||||||
|
IOException {
|
||||||
|
return BCECUtil.convertX509ToECPublicKey(
|
||||||
|
subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将ECC私钥转换为PKCS8标准的字节流
|
||||||
|
*
|
||||||
|
* @param priKey
|
||||||
|
* @param pubKey 可以为空,但是如果为空的话得到的结果OpenSSL可能解析不了
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPrivateKeyToPKCS8(
|
||||||
|
ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) {
|
||||||
|
ECDomainParameters domainParams = priKey.getParameters();
|
||||||
|
ECParameterSpec spec =
|
||||||
|
new ECParameterSpec(
|
||||||
|
domainParams.getCurve(), domainParams.getG(), domainParams.getN(), domainParams.getH());
|
||||||
|
BCECPublicKey publicKey = null;
|
||||||
|
if (pubKey != null) {
|
||||||
|
publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, BouncyCastleProvider.CONFIGURATION);
|
||||||
|
}
|
||||||
|
BCECPrivateKey privateKey =
|
||||||
|
new BCECPrivateKey(
|
||||||
|
ALGO_NAME_EC, priKey, publicKey, spec, BouncyCastleProvider.CONFIGURATION);
|
||||||
|
return privateKey.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将PKCS8标准的私钥字节流转换为私钥对象
|
||||||
|
*
|
||||||
|
* @param pkcs8Key
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws InvalidKeySpecException
|
||||||
|
*/
|
||||||
|
public static BCECPrivateKey convertPKCS8ToECPrivateKey(byte[] pkcs8Key)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
|
||||||
|
PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return (BCECPrivateKey) kf.generatePrivate(peks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将PKCS8标准的私钥字节流转换为PEM
|
||||||
|
*
|
||||||
|
* @param encodedKey
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static String convertECPrivateKeyPKCS8ToPEM(byte[] encodedKey) throws IOException {
|
||||||
|
return convertEncodedDataToPEM(PEM_STRING_ECPRIVATEKEY, encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将PEM格式的私钥转换为PKCS8标准字节流
|
||||||
|
*
|
||||||
|
* @param pemString
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPrivateKeyPEMToPKCS8(String pemString) throws IOException {
|
||||||
|
return convertPEMToEncodedData(pemString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将ECC私钥转换为SEC1标准的字节流 openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的,
|
||||||
|
* 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥. 相对RSA私钥的PKCS1标准,ECC私钥的标准为SEC1
|
||||||
|
*
|
||||||
|
* @param priKey
|
||||||
|
* @param pubKey
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPrivateKeyToSEC1(
|
||||||
|
ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) throws IOException {
|
||||||
|
byte[] pkcs8Bytes = convertECPrivateKeyToPKCS8(priKey, pubKey);
|
||||||
|
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes);
|
||||||
|
ASN1Encodable encodable = pki.parsePrivateKey();
|
||||||
|
ASN1Primitive primitive = encodable.toASN1Primitive();
|
||||||
|
byte[] sec1Bytes = primitive.getEncoded();
|
||||||
|
return sec1Bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将SEC1标准的私钥字节流恢复为PKCS8标准的字节流
|
||||||
|
*
|
||||||
|
* @param sec1Key
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPrivateKeySEC1ToPKCS8(byte[] sec1Key) throws IOException {
|
||||||
|
/**
|
||||||
|
* 参考org.bouncycastle.asn1.pkcs.PrivateKeyInfo和
|
||||||
|
* org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey,逆向拼装
|
||||||
|
*/
|
||||||
|
X962Parameters params = getDomainParametersFromName(SM2Util.JDK_EC_SPEC, false);
|
||||||
|
ASN1OctetString privKey = new DEROctetString(sec1Key);
|
||||||
|
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||||
|
v.add(new ASN1Integer(0)); // 版本号
|
||||||
|
v.add(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params)); // 算法标识
|
||||||
|
v.add(privKey);
|
||||||
|
DERSequence ds = new DERSequence(v);
|
||||||
|
return ds.getEncoded(ASN1Encoding.DER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将SEC1标准的私钥字节流转为BCECPrivateKey对象
|
||||||
|
*
|
||||||
|
* @param sec1Key
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws InvalidKeySpecException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static BCECPrivateKey convertSEC1ToBCECPrivateKey(byte[] sec1Key)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException,
|
||||||
|
IOException {
|
||||||
|
PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(convertECPrivateKeySEC1ToPKCS8(sec1Key));
|
||||||
|
KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return (BCECPrivateKey) kf.generatePrivate(peks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将SEC1标准的私钥字节流转为ECPrivateKeyParameters对象 openssl
|
||||||
|
* i2d_ECPrivateKey函数生成的DER编码的ecc私钥是:SEC1标准的、带有EC_GROUP、带有公钥的,
|
||||||
|
* 这个工具函数的主要目的就是为了使Java程序能够“识别”openssl生成的ECC私钥
|
||||||
|
*
|
||||||
|
* @param sec1Key
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws InvalidKeySpecException
|
||||||
|
*/
|
||||||
|
public static ECPrivateKeyParameters convertSEC1ToECPrivateKey(byte[] sec1Key)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException,
|
||||||
|
IOException {
|
||||||
|
BCECPrivateKey privateKey = convertSEC1ToBCECPrivateKey(sec1Key);
|
||||||
|
return convertPrivateKeyToParameters(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将ECC公钥对象转换为X509标准的字节流
|
||||||
|
*
|
||||||
|
* @param pubKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPublicKeyToX509(ECPublicKeyParameters pubKey) {
|
||||||
|
ECDomainParameters domainParams = pubKey.getParameters();
|
||||||
|
ECParameterSpec spec =
|
||||||
|
new ECParameterSpec(
|
||||||
|
domainParams.getCurve(), domainParams.getG(), domainParams.getN(), domainParams.getH());
|
||||||
|
BCECPublicKey publicKey =
|
||||||
|
new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, BouncyCastleProvider.CONFIGURATION);
|
||||||
|
return publicKey.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将X509标准的公钥字节流转为公钥对象
|
||||||
|
*
|
||||||
|
* @param x509Bytes
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws InvalidKeySpecException
|
||||||
|
*/
|
||||||
|
public static BCECPublicKey convertX509ToECPublicKey(byte[] x509Bytes)
|
||||||
|
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes);
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return (BCECPublicKey) kf.generatePublic(eks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将X509标准的公钥字节流转为PEM
|
||||||
|
*
|
||||||
|
* @param encodedKey
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static String convertECPublicKeyX509ToPEM(byte[] encodedKey) throws IOException {
|
||||||
|
return convertEncodedDataToPEM(PEM_STRING_PUBLIC, encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将PEM格式的公钥转为X509标准的字节流
|
||||||
|
*
|
||||||
|
* @param pemString
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] convertECPublicKeyPEMToX509(String pemString) throws IOException {
|
||||||
|
return convertPEMToEncodedData(pemString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy from BC
|
||||||
|
*
|
||||||
|
* @param genSpec
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) {
|
||||||
|
return getDomainParametersFromName(genSpec.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy from BC
|
||||||
|
*
|
||||||
|
* @param curveName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static X9ECParameters getDomainParametersFromName(String curveName) {
|
||||||
|
X9ECParameters domainParameters;
|
||||||
|
try {
|
||||||
|
if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') {
|
||||||
|
ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
|
||||||
|
domainParameters = ECUtil.getNamedCurveByOid(oidID);
|
||||||
|
} else {
|
||||||
|
if (curveName.indexOf(' ') > 0) {
|
||||||
|
curveName = curveName.substring(curveName.indexOf(' ') + 1);
|
||||||
|
domainParameters = ECUtil.getNamedCurveByName(curveName);
|
||||||
|
} else {
|
||||||
|
domainParameters = ECUtil.getNamedCurveByName(curveName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
domainParameters = ECUtil.getNamedCurveByName(curveName);
|
||||||
|
}
|
||||||
|
return domainParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy from BC
|
||||||
|
*
|
||||||
|
* @param ecSpec
|
||||||
|
* @param withCompression
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static X962Parameters getDomainParametersFromName(
|
||||||
|
java.security.spec.ECParameterSpec ecSpec, boolean withCompression) {
|
||||||
|
X962Parameters params;
|
||||||
|
|
||||||
|
if (ecSpec instanceof ECNamedCurveSpec) {
|
||||||
|
ASN1ObjectIdentifier curveOid =
|
||||||
|
ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName());
|
||||||
|
if (curveOid == null) {
|
||||||
|
curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName());
|
||||||
|
}
|
||||||
|
params = new X962Parameters(curveOid);
|
||||||
|
} else if (ecSpec == null) {
|
||||||
|
params = new X962Parameters(DERNull.INSTANCE);
|
||||||
|
} else {
|
||||||
|
ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
|
||||||
|
|
||||||
|
X9ECParameters ecP =
|
||||||
|
new X9ECParameters(
|
||||||
|
curve,
|
||||||
|
new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression),
|
||||||
|
ecSpec.getOrder(),
|
||||||
|
BigInteger.valueOf(ecSpec.getCofactor()),
|
||||||
|
ecSpec.getCurve().getSeed());
|
||||||
|
|
||||||
|
//// 如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码,因为高版本的EC5Util.convertPoint没有向下兼容
|
||||||
|
/*
|
||||||
|
X9ECParameters ecP = new X9ECParameters(
|
||||||
|
curve,
|
||||||
|
EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
|
||||||
|
ecSpec.getOrder(),
|
||||||
|
BigInteger.valueOf(ecSpec.getCofactor()),
|
||||||
|
ecSpec.getCurve().getSeed());
|
||||||
|
*/
|
||||||
|
|
||||||
|
params = new X962Parameters(ecP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String convertEncodedDataToPEM(String type, byte[] encodedData)
|
||||||
|
throws IOException {
|
||||||
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||||
|
PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
|
||||||
|
try {
|
||||||
|
PemObject pemObj = new PemObject(type, encodedData);
|
||||||
|
pWrt.writeObject(pemObj);
|
||||||
|
} finally {
|
||||||
|
pWrt.close();
|
||||||
|
}
|
||||||
|
return new String(bOut.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] convertPEMToEncodedData(String pemString) throws IOException {
|
||||||
|
ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes());
|
||||||
|
PemReader pRdr = new PemReader(new InputStreamReader(bIn));
|
||||||
|
try {
|
||||||
|
PemObject pemObject = pRdr.readPemObject();
|
||||||
|
return pemObject.getContent();
|
||||||
|
} finally {
|
||||||
|
pRdr.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/org.zz/gmhelper/GMBaseUtil.java
Normal file
11
src/main/java/org.zz/gmhelper/GMBaseUtil.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
|
public class GMBaseUtil {
|
||||||
|
static {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/org.zz/gmhelper/SM2Cipher.java
Normal file
55
src/main/java/org.zz/gmhelper/SM2Cipher.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
public class SM2Cipher {
|
||||||
|
/**
|
||||||
|
* ECC密钥
|
||||||
|
*/
|
||||||
|
private byte[] c1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 真正的密文
|
||||||
|
*/
|
||||||
|
private byte[] c2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对(c1+c2)的SM3-HASH值
|
||||||
|
*/
|
||||||
|
private byte[] c3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SM2标准的密文,即(c1+c2+c3)
|
||||||
|
*/
|
||||||
|
private byte[] cipherText;
|
||||||
|
|
||||||
|
public byte[] getC1() {
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setC1(byte[] c1) {
|
||||||
|
this.c1 = c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getC2() {
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setC2(byte[] c2) {
|
||||||
|
this.c2 = c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getC3() {
|
||||||
|
return c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setC3(byte[] c3) {
|
||||||
|
this.c3 = c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCipherText() {
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCipherText(byte[] cipherText) {
|
||||||
|
this.cipherText = cipherText;
|
||||||
|
}
|
||||||
|
}
|
109
src/main/java/org.zz/gmhelper/SM2KeyExchangeUtil.java
Normal file
109
src/main/java/org.zz/gmhelper/SM2KeyExchangeUtil.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.agreement.SM2KeyExchange;
|
||||||
|
import org.bouncycastle.crypto.params.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class SM2KeyExchangeUtil {
|
||||||
|
/**
|
||||||
|
* @param initiator true表示发起方,false表示响应方
|
||||||
|
* @param keyBits 生成的密钥长度
|
||||||
|
* @param selfStaticPriv 己方固定私钥
|
||||||
|
* @param selfEphemeralPriv 己方临时私钥
|
||||||
|
* @param selfId 己方ID
|
||||||
|
* @param otherStaticPub 对方固定公钥
|
||||||
|
* @param otherEphemeralPub 对方临时公钥
|
||||||
|
* @param otherId 对方ID
|
||||||
|
* @return 返回协商出的密钥,但是这个密钥是没有经过确认的
|
||||||
|
*/
|
||||||
|
public static byte[] calculateKey(boolean initiator, int keyBits,
|
||||||
|
ECPrivateKeyParameters selfStaticPriv, ECPrivateKeyParameters selfEphemeralPriv, byte[] selfId,
|
||||||
|
ECPublicKeyParameters otherStaticPub, ECPublicKeyParameters otherEphemeralPub, byte[] otherId) {
|
||||||
|
SM2KeyExchange exch = new SM2KeyExchange();
|
||||||
|
exch.init(new ParametersWithID(
|
||||||
|
new SM2KeyExchangePrivateParameters(initiator, selfStaticPriv, selfEphemeralPriv),
|
||||||
|
selfId));
|
||||||
|
return exch.calculateKey(
|
||||||
|
keyBits,
|
||||||
|
new ParametersWithID(new SM2KeyExchangePublicParameters(otherStaticPub, otherEphemeralPub), otherId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param initiator true表示发起方,false表示响应方
|
||||||
|
* @param keyBits 生成的密钥长度
|
||||||
|
* @param confirmationTag 确认信息,如果是响应方可以为null;如果是发起方则应为响应方的s1
|
||||||
|
* @param selfStaticPriv 己方固定私钥
|
||||||
|
* @param selfEphemeralPriv 己方临时私钥
|
||||||
|
* @param selfId 己方ID
|
||||||
|
* @param otherStaticPub 对方固定公钥
|
||||||
|
* @param otherEphemeralPub 对方临时公钥
|
||||||
|
* @param otherId 对方ID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ExchangeResult calculateKeyWithConfirmation(boolean initiator, int keyBits, byte[] confirmationTag,
|
||||||
|
ECPrivateKeyParameters selfStaticPriv, ECPrivateKeyParameters selfEphemeralPriv, byte[] selfId,
|
||||||
|
ECPublicKeyParameters otherStaticPub, ECPublicKeyParameters otherEphemeralPub, byte[] otherId) {
|
||||||
|
SM2KeyExchange exch = new SM2KeyExchange();
|
||||||
|
exch.init(new ParametersWithID(
|
||||||
|
new SM2KeyExchangePrivateParameters(initiator, selfStaticPriv, selfEphemeralPriv),
|
||||||
|
selfId));
|
||||||
|
byte[][] result = exch.calculateKeyWithConfirmation(
|
||||||
|
keyBits,
|
||||||
|
confirmationTag,
|
||||||
|
new ParametersWithID(new SM2KeyExchangePublicParameters(otherStaticPub, otherEphemeralPub), otherId));
|
||||||
|
ExchangeResult confirmResult = new ExchangeResult();
|
||||||
|
confirmResult.setKey(result[0]);
|
||||||
|
if (initiator) {
|
||||||
|
confirmResult.setS2(result[1]);
|
||||||
|
} else {
|
||||||
|
confirmResult.setS1(result[1]);
|
||||||
|
confirmResult.setS2(result[2]);
|
||||||
|
}
|
||||||
|
return confirmResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s2
|
||||||
|
* @param confirmationTag 实际上是发起方的s2
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean responderConfirm(byte[] s2, byte[] confirmationTag) {
|
||||||
|
return Arrays.equals(s2, confirmationTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExchangeResult {
|
||||||
|
private byte[] key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起方没有s1
|
||||||
|
*/
|
||||||
|
private byte[] s1;
|
||||||
|
|
||||||
|
private byte[] s2;
|
||||||
|
|
||||||
|
public byte[] getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(byte[] key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getS1() {
|
||||||
|
return s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setS1(byte[] s1) {
|
||||||
|
this.s1 = s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getS2() {
|
||||||
|
return s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setS2(byte[] s2) {
|
||||||
|
this.s2 = s2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
src/main/java/org.zz/gmhelper/SM2KeyPair.java
Normal file
110
src/main/java/org.zz/gmhelper/SM2KeyPair.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
|
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SM2密钥对Bean
|
||||||
|
*
|
||||||
|
* @author Potato
|
||||||
|
*/
|
||||||
|
public class SM2KeyPair {
|
||||||
|
private final ECPublicKeyParameters publicKey;
|
||||||
|
private final ECPrivateKeyParameters privateKey;
|
||||||
|
|
||||||
|
public SM2KeyPair(ECPublicKeyParameters publicKey, BigInteger privateKey) {
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
this.privateKey = new ECPrivateKeyParameters(privateKey, SM2Util.DOMAIN_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SM2KeyPair fromJson(String jsonStr) {
|
||||||
|
Map<String, String> jo =
|
||||||
|
new Gson().fromJson(jsonStr, new TypeToken<Map<String, String>>() {
|
||||||
|
}.getType());
|
||||||
|
String publicKeyStr = jo.get("publicKey");
|
||||||
|
String privateKeyStr = jo.get("privateKey");
|
||||||
|
ECPublicKeyParameters point =
|
||||||
|
BCECUtil.createECPublicKeyFromStrParameters(
|
||||||
|
publicKeyStr, SM2Util.CURVE, SM2Util.DOMAIN_PARAMS);
|
||||||
|
return new SM2KeyPair(point, new BigInteger(privateKeyStr, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPublicKeyParameters publicKeyStr2ECPoint(String pubKey) {
|
||||||
|
return BCECUtil.createECPublicKeyFromStrParameters(
|
||||||
|
pubKey, SM2Util.CURVE, SM2Util.DOMAIN_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECPublicKeyParameters getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKeyStr() {
|
||||||
|
return ByteUtils.toHexString(publicKey.getQ().getEncoded(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getPrivateKey() {
|
||||||
|
return privateKey.getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECPrivateKeyParameters getPrivateKeyParameter() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJson() {
|
||||||
|
String ret = "{\"publicKey\":\"";
|
||||||
|
ret += ByteUtils.toHexString(publicKey.getQ().getEncoded(false));
|
||||||
|
ret += "\",\"privateKey\":\"";
|
||||||
|
ret += privateKey.getD().toString(16);
|
||||||
|
ret += "\"}";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKeyStr() {
|
||||||
|
return privateKey.getD().toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyPair toJavaSecurity() {
|
||||||
|
ECDomainParameters domainParams = privateKey.getParameters();
|
||||||
|
ECParameterSpec spec =
|
||||||
|
new ECParameterSpec(
|
||||||
|
domainParams.getCurve(),
|
||||||
|
domainParams.getG(),
|
||||||
|
domainParams.getN(),
|
||||||
|
domainParams.getH());
|
||||||
|
BCECPublicKey bcPublicKey = null;
|
||||||
|
BCECPrivateKey bcPrivateKey;
|
||||||
|
if (null != publicKey) {
|
||||||
|
bcPublicKey =
|
||||||
|
new BCECPublicKey(
|
||||||
|
BCECUtil.ALGO_NAME_EC,
|
||||||
|
publicKey,
|
||||||
|
spec,
|
||||||
|
BouncyCastleProvider.CONFIGURATION);
|
||||||
|
}
|
||||||
|
bcPrivateKey =
|
||||||
|
new BCECPrivateKey(
|
||||||
|
BCECUtil.ALGO_NAME_EC,
|
||||||
|
privateKey,
|
||||||
|
bcPublicKey,
|
||||||
|
spec,
|
||||||
|
BouncyCastleProvider.CONFIGURATION);
|
||||||
|
return new KeyPair(bcPublicKey, bcPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toX509DEREncoded() {
|
||||||
|
return BCECUtil.convertECPublicKeyToX509(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
274
src/main/java/org.zz/gmhelper/SM2PreprocessSigner.java
Normal file
274
src/main/java/org.zz/gmhelper/SM2PreprocessSigner.java
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.*;
|
||||||
|
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.*;
|
||||||
|
import org.bouncycastle.crypto.signers.DSAKCalculator;
|
||||||
|
import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
|
||||||
|
import org.bouncycastle.math.ec.*;
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
}
|
213
src/main/java/org.zz/gmhelper/SM2Signer.java
Normal file
213
src/main/java/org.zz/gmhelper/SM2Signer.java
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by Fernflower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.*;
|
||||||
|
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||||
|
import org.bouncycastle.crypto.params.*;
|
||||||
|
import org.bouncycastle.crypto.signers.DSAEncoding;
|
||||||
|
import org.bouncycastle.crypto.signers.DSAKCalculator;
|
||||||
|
import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
|
||||||
|
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
|
||||||
|
import org.bouncycastle.math.ec.*;
|
||||||
|
import org.bouncycastle.util.BigIntegers;
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class SM2Signer implements Signer, ECConstants {
|
||||||
|
private final DSAKCalculator kCalculator;
|
||||||
|
private final Digest digest;
|
||||||
|
private final DSAEncoding encoding;
|
||||||
|
private ECDomainParameters ecParams;
|
||||||
|
private ECPoint pubPoint;
|
||||||
|
private ECKeyParameters ecKey;
|
||||||
|
private byte[] z;
|
||||||
|
|
||||||
|
public SM2Signer() {
|
||||||
|
this(StandardDSAEncoding.INSTANCE, new SM3Digest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SM2Signer(Digest var1) {
|
||||||
|
this(StandardDSAEncoding.INSTANCE, var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SM2Signer(DSAEncoding var1) {
|
||||||
|
this.kCalculator = new RandomDSAKCalculator();
|
||||||
|
this.encoding = var1;
|
||||||
|
this.digest = new SM3Digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SM2Signer(DSAEncoding var1, Digest var2) {
|
||||||
|
this.kCalculator = new RandomDSAKCalculator();
|
||||||
|
this.encoding = var1;
|
||||||
|
this.digest = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(boolean var1, CipherParameters var2) {
|
||||||
|
CipherParameters var3;
|
||||||
|
byte[] var4;
|
||||||
|
if (var2 instanceof ParametersWithID) {
|
||||||
|
var3 = ((ParametersWithID) var2).getParameters();
|
||||||
|
var4 = ((ParametersWithID) var2).getID();
|
||||||
|
if (var4.length >= 8192) {
|
||||||
|
throw new IllegalArgumentException("SM2 user ID must be less than 2^16 bits long");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var3 = var2;
|
||||||
|
var4 = Hex.decodeStrict("31323334353637383132333435363738");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var1) {
|
||||||
|
if (var3 instanceof ParametersWithRandom) {
|
||||||
|
ParametersWithRandom var5 = (ParametersWithRandom) var3;
|
||||||
|
this.ecKey = (ECKeyParameters) var5.getParameters();
|
||||||
|
this.ecParams = this.ecKey.getParameters();
|
||||||
|
this.kCalculator.init(this.ecParams.getN(), var5.getRandom());
|
||||||
|
} else {
|
||||||
|
this.ecKey = (ECKeyParameters) var3;
|
||||||
|
this.ecParams = this.ecKey.getParameters();
|
||||||
|
this.kCalculator.init(this.ecParams.getN(), CryptoServicesRegistrar.getSecureRandom());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pubPoint =
|
||||||
|
this.createBasePointMultiplier()
|
||||||
|
.multiply(this.ecParams.getG(), ((ECPrivateKeyParameters) this.ecKey).getD())
|
||||||
|
.normalize();
|
||||||
|
} else {
|
||||||
|
this.ecKey = (ECKeyParameters) var3;
|
||||||
|
this.ecParams = this.ecKey.getParameters();
|
||||||
|
this.pubPoint = ((ECPublicKeyParameters) this.ecKey).getQ();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.z = this.getZ(var4);
|
||||||
|
this.digest.update(this.z, 0, this.z.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte var1) {
|
||||||
|
this.digest.update(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] var1, int var2, int var3) {
|
||||||
|
this.digest.update(var1, var2, var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verifySignature(byte[] var1) {
|
||||||
|
try {
|
||||||
|
BigInteger[] var2 = this.encoding.decode(this.ecParams.getN(), var1);
|
||||||
|
return this.verifySignature(var2[0], var2[1]);
|
||||||
|
} catch (Exception var3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.digest.reset();
|
||||||
|
if (this.z != null) {
|
||||||
|
this.digest.update(this.z, 0, this.z.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] generateSignature() throws CryptoException {
|
||||||
|
byte[] var1 = this.digestDoFinal();
|
||||||
|
BigInteger n = this.ecParams.getN();
|
||||||
|
BigInteger var3 = this.calculateE(n , var1);
|
||||||
|
BigInteger var4 = ((ECPrivateKeyParameters) this.ecKey).getD();
|
||||||
|
ECMultiplier var7 = this.createBasePointMultiplier();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
BigInteger var5;
|
||||||
|
BigInteger k;
|
||||||
|
do {
|
||||||
|
k = this.kCalculator.nextK();
|
||||||
|
ECPoint var9 = var7.multiply(this.ecParams.getG(), k).normalize();
|
||||||
|
var5 = var3.add(var9.getAffineXCoord().toBigInteger()).mod(n);
|
||||||
|
} while (var5.equals(ZERO));
|
||||||
|
|
||||||
|
if (!var5.add(k).equals(n )) {
|
||||||
|
BigInteger var11 = BigIntegers.modOddInverse(n, var4.add(ONE));
|
||||||
|
BigInteger var6 = k.subtract(var5.multiply(var4)).mod(n);
|
||||||
|
var6 = var11.multiply(var6).mod(n);
|
||||||
|
if (!var6.equals(ZERO)) {
|
||||||
|
try {
|
||||||
|
return this.encoding.encode(this.ecParams.getN(), var5, var6);
|
||||||
|
} catch (Exception var10) {
|
||||||
|
throw new CryptoException("unable to encode signature: " + var10.getMessage(), var10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifySignature(BigInteger var1, BigInteger var2) {
|
||||||
|
BigInteger var3 = this.ecParams.getN();
|
||||||
|
if (var1.compareTo(ONE) >= 0 && var1.compareTo(var3) < 0) {
|
||||||
|
if (var2.compareTo(ONE) >= 0 && var2.compareTo(var3) < 0) {
|
||||||
|
byte[] var4 = this.digestDoFinal();
|
||||||
|
BigInteger var5 = this.calculateE(var3, var4);
|
||||||
|
BigInteger var6 = var1.add(var2).mod(var3);
|
||||||
|
if (var6.equals(ZERO)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ECPoint var7 = ((ECPublicKeyParameters) this.ecKey).getQ();
|
||||||
|
ECPoint var8 =
|
||||||
|
ECAlgorithms.sumOfTwoMultiplies(this.ecParams.getG(), var2, var7, var6).normalize();
|
||||||
|
if (var8.isInfinity()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BigInteger var9 = var5.add(var8.getAffineXCoord().toBigInteger()).mod(var3);
|
||||||
|
return var9.equals(var1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] digestDoFinal() {
|
||||||
|
byte[] var1 = new byte[this.digest.getDigestSize()];
|
||||||
|
this.digest.doFinal(var1, 0);
|
||||||
|
this.reset();
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getZ(byte[] var1) {
|
||||||
|
this.digest.reset();
|
||||||
|
this.addUserID(this.digest, var1);
|
||||||
|
this.addFieldElement(this.digest, this.ecParams.getCurve().getA());
|
||||||
|
this.addFieldElement(this.digest, this.ecParams.getCurve().getB());
|
||||||
|
this.addFieldElement(this.digest, this.ecParams.getG().getAffineXCoord());
|
||||||
|
this.addFieldElement(this.digest, this.ecParams.getG().getAffineYCoord());
|
||||||
|
this.addFieldElement(this.digest, this.pubPoint.getAffineXCoord());
|
||||||
|
this.addFieldElement(this.digest, this.pubPoint.getAffineYCoord());
|
||||||
|
byte[] var2 = new byte[this.digest.getDigestSize()];
|
||||||
|
this.digest.doFinal(var2, 0);
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUserID(Digest var1, byte[] var2) {
|
||||||
|
int var3 = var2.length * 8;
|
||||||
|
var1.update((byte) (var3 >> 8 & 255));
|
||||||
|
var1.update((byte) (var3 & 255));
|
||||||
|
var1.update(var2, 0, var2.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFieldElement(Digest var1, ECFieldElement var2) {
|
||||||
|
byte[] var3 = var2.getEncoded();
|
||||||
|
var1.update(var3, 0, var3.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ECMultiplier createBasePointMultiplier() {
|
||||||
|
return new FixedPointCombMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BigInteger calculateE(BigInteger var1, byte[] var2) {
|
||||||
|
return new BigInteger(1, var2);
|
||||||
|
}
|
||||||
|
}
|
662
src/main/java/org.zz/gmhelper/SM2Util.java
Normal file
662
src/main/java/org.zz/gmhelper/SM2Util.java
Normal file
@ -0,0 +1,662 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.*;
|
||||||
|
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
import org.bouncycastle.crypto.CryptoException;
|
||||||
|
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||||
|
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||||
|
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
|
||||||
|
import org.bouncycastle.crypto.params.*;
|
||||||
|
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.ECCurve;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
|
||||||
|
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.ECFieldFp;
|
||||||
|
import java.security.spec.EllipticCurve;
|
||||||
|
|
||||||
|
public class SM2Util extends GMBaseUtil {
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
* 以下为SM2推荐曲线参数
|
||||||
|
*/
|
||||||
|
public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
|
||||||
|
public static final BigInteger SM2_ECC_P = CURVE.getQ();
|
||||||
|
public static final BigInteger SM2_ECC_A = CURVE.getA().toBigInteger();
|
||||||
|
public static final BigInteger SM2_ECC_B = CURVE.getB().toBigInteger();
|
||||||
|
public static final BigInteger SM2_ECC_N = CURVE.getOrder();
|
||||||
|
public static final BigInteger SM2_ECC_H = CURVE.getCofactor();
|
||||||
|
public static final BigInteger SM2_ECC_GX =
|
||||||
|
new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
|
||||||
|
public static final BigInteger SM2_ECC_GY =
|
||||||
|
new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
|
||||||
|
public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
|
||||||
|
public static final ECDomainParameters DOMAIN_PARAMS =
|
||||||
|
new ECDomainParameters(CURVE, G_POINT, SM2_ECC_N, SM2_ECC_H);
|
||||||
|
public static final int CURVE_LEN = BCECUtil.getCurveLength(DOMAIN_PARAMS);
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
public static final EllipticCurve JDK_CURVE =
|
||||||
|
new EllipticCurve(new ECFieldFp(SM2_ECC_P), SM2_ECC_A, SM2_ECC_B);
|
||||||
|
public static final java.security.spec.ECPoint JDK_G_POINT =
|
||||||
|
new java.security.spec.ECPoint(
|
||||||
|
G_POINT.getAffineXCoord().toBigInteger(),
|
||||||
|
G_POINT.getAffineYCoord().toBigInteger());
|
||||||
|
public static final java.security.spec.ECParameterSpec JDK_EC_SPEC =
|
||||||
|
new java.security.spec.ECParameterSpec(
|
||||||
|
JDK_CURVE, JDK_G_POINT, SM2_ECC_N, SM2_ECC_H.intValue());
|
||||||
|
public static final int SM3_DIGEST_LENGTH = 32;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static ECCurve.Fp SM2_ECC_FP =
|
||||||
|
new ECCurve.Fp(
|
||||||
|
SM2_ECC_P, // q
|
||||||
|
SM2_ECC_A, // a
|
||||||
|
SM2_ECC_B); // b
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成ECC密钥对
|
||||||
|
*
|
||||||
|
* @return ECC密钥对
|
||||||
|
*/
|
||||||
|
public static AsymmetricCipherKeyPair generateKeyPairParameter() {
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
return BCECUtil.generateKeyPairParameter(DOMAIN_PARAMS, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generate ECC key pair
|
||||||
|
*
|
||||||
|
* @param random
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws InvalidAlgorithmParameterException
|
||||||
|
*/
|
||||||
|
public static KeyPair generateKeyPair(SecureRandom random)
|
||||||
|
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||||
|
InvalidAlgorithmParameterException {
|
||||||
|
return BCECUtil.generateKeyPair(DOMAIN_PARAMS, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SM2KeyPair generateSM2KeyPair() {
|
||||||
|
return generateSM2KeyPair(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SM2KeyPair generateSM2KeyPair(SecureRandom r) {
|
||||||
|
try {
|
||||||
|
KeyPair key = generateKeyPair(r);
|
||||||
|
BCECPrivateKey privateKey = (BCECPrivateKey) key.getPrivate();
|
||||||
|
BCECPublicKey publicKey = (BCECPublicKey) key.getPublic();
|
||||||
|
byte[] point = publicKey.getQ().getEncoded(false);
|
||||||
|
ECPublicKeyParameters parameters =
|
||||||
|
BCECUtil.createECPublicKeyFromStrParameters(
|
||||||
|
ByteUtils.toHexString(point), CURVE, DOMAIN_PARAMS);
|
||||||
|
return new SM2KeyPair(parameters, privateKey.getD());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只获取私钥里的d值,32字节
|
||||||
|
*
|
||||||
|
* @param privateKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] getRawPrivateKey(BCECPrivateKey privateKey) {
|
||||||
|
return fixToCurveLengthBytes(privateKey.getD().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只获取公钥里的XY分量,64字节
|
||||||
|
*
|
||||||
|
* @param publicKey
|
||||||
|
* @return 64字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getRawPublicKey(BCECPublicKey publicKey) {
|
||||||
|
byte[] src65 = publicKey.getQ().getEncoded(false);
|
||||||
|
byte[] rawXY = new byte[CURVE_LEN * 2]; // SM2的话这里应该是64字节
|
||||||
|
System.arraycopy(src65, 1, rawXY, 0, rawXY.length);
|
||||||
|
return rawXY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pubKey 公钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return 默认输出C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] encrypt(BCECPublicKey pubKey, byte[] srcData)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
ECPublicKeyParameters pubKeyParameters = BCECUtil.convertPublicKeyToParameters(pubKey);
|
||||||
|
return encrypt(Mode.C1C3C2, pubKeyParameters, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param pubKey 公钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] encrypt(Mode mode, BCECPublicKey pubKey, byte[] srcData)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
ECPublicKeyParameters pubKeyParameters = BCECUtil.convertPublicKeyToParameters(pubKey);
|
||||||
|
return encrypt(mode, pubKeyParameters, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pubKeyParameters 公钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return 默认输出C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] encrypt(ECPublicKeyParameters pubKeyParameters, byte[] srcData)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
return encrypt(Mode.C1C3C2, pubKeyParameters, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param pubKeyParameters 公钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] encrypt(Mode mode, ECPublicKeyParameters pubKeyParameters, byte[] srcData)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
SM2Engine engine = new SM2Engine(mode);
|
||||||
|
ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom());
|
||||||
|
engine.init(true, pwr);
|
||||||
|
return engine.processBlock(srcData, 0, srcData.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param priKey 私钥
|
||||||
|
* @param sm2Cipher 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] decrypt(BCECPrivateKey priKey, byte[] sm2Cipher)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
ECPrivateKeyParameters priKeyParameters = BCECUtil.convertPrivateKeyToParameters(priKey);
|
||||||
|
return decrypt(Mode.C1C3C2, priKeyParameters, sm2Cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param priKey 私钥
|
||||||
|
* @param sm2Cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] decrypt(Mode mode, BCECPrivateKey priKey, byte[] sm2Cipher)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
ECPrivateKeyParameters priKeyParameters = BCECUtil.convertPrivateKeyToParameters(priKey);
|
||||||
|
return decrypt(mode, priKeyParameters, sm2Cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param priKeyParameters 私钥
|
||||||
|
* @param sm2Cipher 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] decrypt(ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
return decrypt(Mode.C1C3C2, priKeyParameters, sm2Cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param priKeyParameters 私钥
|
||||||
|
* @param sm2Cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
||||||
|
* @throws InvalidCipherTextException
|
||||||
|
*/
|
||||||
|
public static byte[] decrypt(
|
||||||
|
Mode mode, ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
SM2Engine engine = new SM2Engine(mode);
|
||||||
|
engine.init(false, priKeyParameters);
|
||||||
|
if (sm2Cipher[0] != 4) {
|
||||||
|
byte[] cipher2 = new byte[sm2Cipher.length + 1];
|
||||||
|
System.arraycopy(sm2Cipher, 0, cipher2, 1, sm2Cipher.length);
|
||||||
|
cipher2[0] = 4;
|
||||||
|
sm2Cipher = cipher2;
|
||||||
|
}
|
||||||
|
return engine.processBlock(sm2Cipher, 0, sm2Cipher.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分解SM2密文
|
||||||
|
*
|
||||||
|
* @param cipherText 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static SM2Cipher parseSM2Cipher(byte[] cipherText) throws Exception {
|
||||||
|
int curveLength = BCECUtil.getCurveLength(DOMAIN_PARAMS);
|
||||||
|
return parseSM2Cipher(Mode.C1C3C2, curveLength, SM3_DIGEST_LENGTH, cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分解SM2密文
|
||||||
|
*
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param cipherText 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static SM2Cipher parseSM2Cipher(Mode mode, byte[] cipherText) throws Exception {
|
||||||
|
int curveLength = BCECUtil.getCurveLength(DOMAIN_PARAMS);
|
||||||
|
return parseSM2Cipher(mode, curveLength, SM3_DIGEST_LENGTH, cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param curveLength 曲线长度,SM2的话就是256位。
|
||||||
|
* @param digestLength 摘要长度,如果是SM2的话因为默认使用SM3摘要,SM3摘要长度为32字节。
|
||||||
|
* @param cipherText 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static SM2Cipher parseSM2Cipher(int curveLength, int digestLength, byte[] cipherText)
|
||||||
|
throws Exception {
|
||||||
|
return parseSM2Cipher(Mode.C1C3C2, curveLength, digestLength, cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分解SM2密文
|
||||||
|
*
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param curveLength 曲线长度,SM2的话就是256位。
|
||||||
|
* @param digestLength 摘要长度,如果是SM2的话因为默认使用SM3摘要,SM3摘要长度为32字节。
|
||||||
|
* @param cipherText 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static SM2Cipher parseSM2Cipher(
|
||||||
|
Mode mode, int curveLength, int digestLength, byte[] cipherText) throws Exception {
|
||||||
|
byte[] c1 = new byte[curveLength * 2 + 1];
|
||||||
|
byte[] c2 = new byte[cipherText.length - c1.length - digestLength];
|
||||||
|
byte[] c3 = new byte[digestLength];
|
||||||
|
|
||||||
|
System.arraycopy(cipherText, 0, c1, 0, c1.length);
|
||||||
|
if (mode == Mode.C1C2C3) {
|
||||||
|
System.arraycopy(cipherText, c1.length, c2, 0, c2.length);
|
||||||
|
System.arraycopy(cipherText, c1.length + c2.length, c3, 0, c3.length);
|
||||||
|
} else if (mode == Mode.C1C3C2) {
|
||||||
|
System.arraycopy(cipherText, c1.length, c3, 0, c3.length);
|
||||||
|
System.arraycopy(cipherText, c1.length + c3.length, c2, 0, c2.length);
|
||||||
|
} else {
|
||||||
|
throw new Exception("Unsupported mode:" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
SM2Cipher result = new SM2Cipher();
|
||||||
|
result.setC1(c1);
|
||||||
|
result.setC2(c2);
|
||||||
|
result.setC3(c3);
|
||||||
|
result.setCipherText(cipherText);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DER编码密文
|
||||||
|
*
|
||||||
|
* @param cipher 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return DER编码后的密文
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2CipherToDER(byte[] cipher) throws Exception {
|
||||||
|
int curveLength = BCECUtil.getCurveLength(DOMAIN_PARAMS);
|
||||||
|
return encodeSM2CipherToDER(Mode.C1C3C2, curveLength, SM3_DIGEST_LENGTH, cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DER编码密文
|
||||||
|
*
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 按指定mode DER编码后的密文
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2CipherToDER(Mode mode, byte[] cipher) throws Exception {
|
||||||
|
int curveLength = BCECUtil.getCurveLength(DOMAIN_PARAMS);
|
||||||
|
return encodeSM2CipherToDER(mode, curveLength, SM3_DIGEST_LENGTH, cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DER编码密文
|
||||||
|
*
|
||||||
|
* @param curveLength 曲线长度,SM2的话就是256位。
|
||||||
|
* @param digestLength 摘要长度,如果是SM2的话因为默认使用SM3摘要,SM3摘要长度为32字节。
|
||||||
|
* @param cipher 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 默认输出按C1C3C2编码的结果
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2CipherToDER(int curveLength, int digestLength, byte[] cipher)
|
||||||
|
throws Exception {
|
||||||
|
return encodeSM2CipherToDER(Mode.C1C3C2, curveLength, digestLength, cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param curveLength 曲线长度,SM2的话就是256位。
|
||||||
|
* @param digestLength 摘要长度,如果是SM2的话因为默认使用SM3摘要,SM3摘要长度为32字节。
|
||||||
|
* @param cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @return 按指定mode DER编码后的密文
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2CipherToDER(
|
||||||
|
Mode mode, int curveLength, int digestLength, byte[] cipher) throws Exception {
|
||||||
|
|
||||||
|
byte[] c1x = new byte[curveLength];
|
||||||
|
byte[] c1y = new byte[curveLength];
|
||||||
|
byte[] c2 = new byte[cipher.length - c1x.length - c1y.length - 1 - digestLength];
|
||||||
|
byte[] c3 = new byte[digestLength];
|
||||||
|
|
||||||
|
int startPos = 1;
|
||||||
|
System.arraycopy(cipher, startPos, c1x, 0, c1x.length);
|
||||||
|
startPos += c1x.length;
|
||||||
|
System.arraycopy(cipher, startPos, c1y, 0, c1y.length);
|
||||||
|
startPos += c1y.length;
|
||||||
|
if (mode == Mode.C1C2C3) {
|
||||||
|
System.arraycopy(cipher, startPos, c2, 0, c2.length);
|
||||||
|
startPos += c2.length;
|
||||||
|
System.arraycopy(cipher, startPos, c3, 0, c3.length);
|
||||||
|
} else if (mode == Mode.C1C3C2) {
|
||||||
|
System.arraycopy(cipher, startPos, c3, 0, c3.length);
|
||||||
|
startPos += c3.length;
|
||||||
|
System.arraycopy(cipher, startPos, c2, 0, c2.length);
|
||||||
|
} else {
|
||||||
|
throw new Exception("Unsupported mode:" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASN1Encodable[] arr = new ASN1Encodable[4];
|
||||||
|
arr[0] = new ASN1Integer(c1x);
|
||||||
|
arr[1] = new ASN1Integer(c1y);
|
||||||
|
if (mode == Mode.C1C2C3) {
|
||||||
|
arr[2] = new DEROctetString(c2);
|
||||||
|
arr[3] = new DEROctetString(c3);
|
||||||
|
} else if (mode == Mode.C1C3C2) {
|
||||||
|
arr[2] = new DEROctetString(c3);
|
||||||
|
arr[3] = new DEROctetString(c2);
|
||||||
|
}
|
||||||
|
DERSequence ds = new DERSequence(arr);
|
||||||
|
return ds.getEncoded(ASN1Encoding.DER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码DER密文
|
||||||
|
*
|
||||||
|
* @param derCipher 默认输入按C1C3C2顺序DER编码的密文
|
||||||
|
* @return 输出按C1C3C2排列的字节数组,C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
*/
|
||||||
|
public static byte[] decodeDERSM2Cipher(byte[] derCipher) throws Exception {
|
||||||
|
return decodeDERSM2Cipher(Mode.C1C3C2, derCipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
||||||
|
* @param derCipher 根据mode输入C1C2C3或C1C3C2顺序DER编码后的密文
|
||||||
|
* @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static byte[] decodeDERSM2Cipher(Mode mode, byte[] derCipher) throws Exception {
|
||||||
|
ASN1Sequence as = DERSequence.getInstance(derCipher);
|
||||||
|
byte[] c1x = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray();
|
||||||
|
byte[] c1y = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray();
|
||||||
|
byte[] c3;
|
||||||
|
byte[] c2;
|
||||||
|
if (mode == Mode.C1C2C3) {
|
||||||
|
c2 = ((DEROctetString) as.getObjectAt(2)).getOctets();
|
||||||
|
c3 = ((DEROctetString) as.getObjectAt(3)).getOctets();
|
||||||
|
} else if (mode == Mode.C1C3C2) {
|
||||||
|
c3 = ((DEROctetString) as.getObjectAt(2)).getOctets();
|
||||||
|
c2 = ((DEROctetString) as.getObjectAt(3)).getOctets();
|
||||||
|
} else {
|
||||||
|
throw new Exception("Unsupported mode:" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
byte[] cipherText = new byte[1 + c1x.length + c1y.length + c2.length + c3.length];
|
||||||
|
final byte uncompressedFlag = 0x04;
|
||||||
|
cipherText[0] = uncompressedFlag;
|
||||||
|
pos += 1;
|
||||||
|
System.arraycopy(c1x, 0, cipherText, pos, c1x.length);
|
||||||
|
pos += c1x.length;
|
||||||
|
System.arraycopy(c1y, 0, cipherText, pos, c1y.length);
|
||||||
|
pos += c1y.length;
|
||||||
|
if (mode == Mode.C1C2C3) {
|
||||||
|
System.arraycopy(c2, 0, cipherText, pos, c2.length);
|
||||||
|
pos += c2.length;
|
||||||
|
System.arraycopy(c3, 0, cipherText, pos, c3.length);
|
||||||
|
} else if (mode == Mode.C1C3C2) {
|
||||||
|
System.arraycopy(c3, 0, cipherText, pos, c3.length);
|
||||||
|
pos += c3.length;
|
||||||
|
System.arraycopy(c2, 0, cipherText, pos, c2.length);
|
||||||
|
}
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名
|
||||||
|
*
|
||||||
|
* @param priKey 私钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return DER编码后的签名值
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoException {
|
||||||
|
ECPrivateKeyParameters priKeyParameters = BCECUtil.convertPrivateKeyToParameters(priKey);
|
||||||
|
return sign(priKeyParameters, null, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名 不指定withId,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
*
|
||||||
|
* @param priKeyParameters 私钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return DER编码后的签名值
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
public static byte[] sign(ECPrivateKeyParameters priKeyParameters, byte[] srcData)
|
||||||
|
throws CryptoException {
|
||||||
|
return sign(priKeyParameters, null, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥签名
|
||||||
|
*
|
||||||
|
* @param priKey 私钥
|
||||||
|
* @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return DER编码后的签名值
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
public static byte[] sign(BCECPrivateKey priKey, byte[] withId, byte[] srcData)
|
||||||
|
throws CryptoException {
|
||||||
|
ECPrivateKeyParameters priKeyParameters = BCECUtil.convertPrivateKeyToParameters(priKey);
|
||||||
|
return sign(priKeyParameters, withId, srcData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名
|
||||||
|
*
|
||||||
|
* @param priKeyParameters 私钥
|
||||||
|
* @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
* @param srcData 源数据
|
||||||
|
* @return DER编码后的签名值
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
public static byte[] sign(
|
||||||
|
ECPrivateKeyParameters priKeyParameters, byte[] withId, byte[] srcData)
|
||||||
|
throws CryptoException {
|
||||||
|
SM2Signer signer = new SM2Signer();
|
||||||
|
CipherParameters param = null;
|
||||||
|
ParametersWithRandom pwr = new ParametersWithRandom(priKeyParameters, new SecureRandom());
|
||||||
|
if (withId != null) {
|
||||||
|
param = new ParametersWithID(pwr, withId);
|
||||||
|
} else {
|
||||||
|
param = pwr;
|
||||||
|
}
|
||||||
|
signer.init(true, param);
|
||||||
|
signer.update(srcData, 0, srcData.length);
|
||||||
|
return signer.generateSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将DER编码的SM2签名解码成64字节的纯R+S字节流
|
||||||
|
*
|
||||||
|
* @param derSign
|
||||||
|
* @return 64字节数组,前32字节为R,后32字节为S
|
||||||
|
*/
|
||||||
|
public static byte[] decodeDERSM2Sign(byte[] derSign) {
|
||||||
|
ASN1Sequence as = DERSequence.getInstance(derSign);
|
||||||
|
byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray();
|
||||||
|
byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray();
|
||||||
|
// 由于大数的补0规则,所以可能会出现33个字节的情况,要修正回32个字节
|
||||||
|
rBytes = fixToCurveLengthBytes(rBytes);
|
||||||
|
sBytes = fixToCurveLengthBytes(sBytes);
|
||||||
|
byte[] rawSign = new byte[rBytes.length + sBytes.length];
|
||||||
|
System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length);
|
||||||
|
System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length);
|
||||||
|
return rawSign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把64字节的纯R+S字节数组编码成DER编码
|
||||||
|
*
|
||||||
|
* @param rawSign 64字节数组形式的SM2签名值,前32字节为R,后32字节为S
|
||||||
|
* @return DER编码后的SM2签名值
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2SignToDER(byte[] rawSign) throws IOException {
|
||||||
|
// 要保证大数是正数
|
||||||
|
BigInteger r = new BigInteger(1, extractBytes(rawSign, 0, 32));
|
||||||
|
BigInteger s = new BigInteger(1, extractBytes(rawSign, 32, 32));
|
||||||
|
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||||
|
v.add(new ASN1Integer(r));
|
||||||
|
v.add(new ASN1Integer(s));
|
||||||
|
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把64字节的纯R+S字节数组编码成DER编码
|
||||||
|
*
|
||||||
|
* @param str 64字节数组形式的SM2签名值,前32字节为R,后32字节为S
|
||||||
|
* @return DER编码后的SM2签名值
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] encodeSM2Sign16ToDER(String str) throws IOException {
|
||||||
|
// 要保证大数是正数
|
||||||
|
BigInteger r, s;
|
||||||
|
if (str.indexOf(",") != -1) {
|
||||||
|
r = new BigInteger(str.substring(0, 32), 16);
|
||||||
|
s = new BigInteger(str.substring(33, 65), 16);
|
||||||
|
} else {
|
||||||
|
r = new BigInteger(str.substring(0, 32), 16);
|
||||||
|
s = new BigInteger(str.substring(32, 64), 16);
|
||||||
|
}
|
||||||
|
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||||
|
v.add(new ASN1Integer(r));
|
||||||
|
v.add(new ASN1Integer(s));
|
||||||
|
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验签 不指定withId,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
*
|
||||||
|
* @param pubKeyParameters 公钥
|
||||||
|
* @param srcData 原文
|
||||||
|
* @param sign DER编码的签名值
|
||||||
|
* @return 验签成功返回true,失败返回false
|
||||||
|
*/
|
||||||
|
public static boolean verify(
|
||||||
|
ECPublicKeyParameters pubKeyParameters, byte[] srcData, byte[] sign) {
|
||||||
|
return verify(pubKeyParameters, null, srcData, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验签
|
||||||
|
*
|
||||||
|
* @param pubKey 公钥
|
||||||
|
* @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
* @param srcData 原文
|
||||||
|
* @param sign DER编码的签名值
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean verify(BCECPublicKey pubKey, byte[] withId, byte[] srcData, byte[] sign) {
|
||||||
|
ECPublicKeyParameters pubKeyParameters = BCECUtil.convertPublicKeyToParameters(pubKey);
|
||||||
|
return verify(pubKeyParameters, withId, srcData, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验签
|
||||||
|
*
|
||||||
|
* @param pubKeyParameters 公钥
|
||||||
|
* @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||||
|
* @param srcData 原文
|
||||||
|
* @param sign DER编码的签名值
|
||||||
|
* @return 验签成功返回true,失败返回false
|
||||||
|
*/
|
||||||
|
public static boolean verify(
|
||||||
|
ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] srcData, byte[] sign) {
|
||||||
|
SM2Signer signer = new SM2Signer();
|
||||||
|
CipherParameters param;
|
||||||
|
if (withId != null) {
|
||||||
|
param = new ParametersWithID(pubKeyParameters, withId);
|
||||||
|
} else {
|
||||||
|
param = pubKeyParameters;
|
||||||
|
}
|
||||||
|
signer.init(false, param);
|
||||||
|
signer.update(srcData, 0, srcData.length);
|
||||||
|
return signer.verifySignature(sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] extractBytes(byte[] src, int offset, int length) {
|
||||||
|
byte[] result = new byte[length];
|
||||||
|
System.arraycopy(src, offset, result, 0, result.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] fixToCurveLengthBytes(byte[] src) {
|
||||||
|
if (src.length == CURVE_LEN) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] result = new byte[CURVE_LEN];
|
||||||
|
if (src.length > CURVE_LEN) {
|
||||||
|
System.arraycopy(src, src.length - result.length, result, 0, result.length);
|
||||||
|
} else {
|
||||||
|
System.arraycopy(src, 0, result, result.length - src.length, src.length);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean plainStrVerify(String publicKey, String toVerify, String signature) {
|
||||||
|
try {
|
||||||
|
byte[] sigByte = ByteUtils.fromHexString(signature);
|
||||||
|
// TODO 是否需要der编码?
|
||||||
|
// try {
|
||||||
|
// sigByte = SM2Util.encodeSM2SignToDER(sigByte);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
ECPublicKeyParameters pubkey =
|
||||||
|
BCECUtil.createECPublicKeyFromStrParameters(
|
||||||
|
publicKey, SM2Util.CURVE, SM2Util.DOMAIN_PARAMS);
|
||||||
|
boolean result = SM2Util.verify(pubkey, toVerify.getBytes(), sigByte);
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
58
src/main/java/org.zz/gmhelper/SM3Util.java
Normal file
58
src/main/java/org.zz/gmhelper/SM3Util.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||||
|
import org.bouncycastle.crypto.macs.HMac;
|
||||||
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class SM3Util extends GMBaseUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算SM3摘要值
|
||||||
|
*
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return 摘要值,对于SM3算法来说是32字节
|
||||||
|
*/
|
||||||
|
public static byte[] hash(byte[] srcData) {
|
||||||
|
SM3Digest digest = new SM3Digest();
|
||||||
|
digest.update(srcData, 0, srcData.length);
|
||||||
|
byte[] hash = new byte[digest.getDigestSize()];
|
||||||
|
digest.doFinal(hash, 0);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证摘要
|
||||||
|
*
|
||||||
|
* @param srcData 原文
|
||||||
|
* @param sm3Hash 摘要值
|
||||||
|
* @return 返回true标识验证成功,false标识验证失败
|
||||||
|
*/
|
||||||
|
public static boolean verify(byte[] srcData, byte[] sm3Hash) {
|
||||||
|
byte[] newHash = hash(srcData);
|
||||||
|
if (Arrays.equals(newHash, sm3Hash)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算SM3 Mac值
|
||||||
|
*
|
||||||
|
* @param key key值,可以是任意长度的字节数组
|
||||||
|
* @param srcData 原文
|
||||||
|
* @return Mac值,对于HMac-SM3来说是32字节
|
||||||
|
*/
|
||||||
|
public static byte[] hmac(byte[] key, byte[] srcData) {
|
||||||
|
KeyParameter keyParameter = new KeyParameter(key);
|
||||||
|
SM3Digest digest = new SM3Digest();
|
||||||
|
HMac mac = new HMac(digest);
|
||||||
|
mac.init(keyParameter);
|
||||||
|
mac.update(srcData, 0, srcData.length);
|
||||||
|
byte[] result = new byte[mac.getMacSize()];
|
||||||
|
mac.doFinal(result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
181
src/main/java/org.zz/gmhelper/SM4Util.java
Normal file
181
src/main/java/org.zz/gmhelper/SM4Util.java
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package org.zz.gmhelper;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
import org.bouncycastle.crypto.engines.SM4Engine;
|
||||||
|
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
|
||||||
|
import org.bouncycastle.crypto.macs.GMac;
|
||||||
|
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||||
|
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
|
||||||
|
import org.bouncycastle.crypto.paddings.PKCS7Padding;
|
||||||
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.security.*;
|
||||||
|
|
||||||
|
public class SM4Util extends GMBaseUtil {
|
||||||
|
public static final String ALGORITHM_NAME = "SM4";
|
||||||
|
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
|
||||||
|
public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";
|
||||||
|
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
|
||||||
|
public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SM4算法目前只支持128位(即密钥16字节)
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_KEY_SIZE = 128;
|
||||||
|
|
||||||
|
public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||||
|
return generateKey(DEFAULT_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||||
|
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
kg.init(keySize, new SecureRandom());
|
||||||
|
return kg.generateKey().getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
|
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText)
|
||||||
|
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||||
|
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
|
||||||
|
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
|
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText)
|
||||||
|
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||||
|
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
|
||||||
|
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);
|
||||||
|
return cipher.doFinal(cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
|
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
|
||||||
|
InvalidAlgorithmParameterException {
|
||||||
|
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText)
|
||||||
|
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||||
|
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||||
|
InvalidAlgorithmParameterException {
|
||||||
|
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
|
||||||
|
return cipher.doFinal(cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
|
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
|
||||||
|
InvalidAlgorithmParameterException {
|
||||||
|
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText)
|
||||||
|
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||||
|
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||||
|
InvalidAlgorithmParameterException {
|
||||||
|
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);
|
||||||
|
return cipher.doFinal(cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||||
|
InvalidKeyException {
|
||||||
|
Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||||
|
return doMac("SM4-CMAC", keyObj, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) {
|
||||||
|
org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8);
|
||||||
|
return doMac(mac, key, iv, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param iv
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) {
|
||||||
|
SM4Engine engine = new SM4Engine();
|
||||||
|
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding());
|
||||||
|
return doMac(mac, key, iv, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param iv
|
||||||
|
* @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception {
|
||||||
|
SM4Engine engine = new SM4Engine();
|
||||||
|
if (padding == null) {
|
||||||
|
if (data.length % engine.getBlockSize() != 0) {
|
||||||
|
throw new Exception("if no padding, data length must be multiple of SM4 BlockSize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding);
|
||||||
|
return doMac(mac, key, iv, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) {
|
||||||
|
CipherParameters cipherParameters = new KeyParameter(key);
|
||||||
|
mac.init(new ParametersWithIV(cipherParameters, iv));
|
||||||
|
mac.update(data, 0, data.length);
|
||||||
|
byte[] result = new byte[mac.getMacSize()];
|
||||||
|
mac.doFinal(result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException,
|
||||||
|
NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
mac.init(key);
|
||||||
|
mac.update(data);
|
||||||
|
return mac.doFinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)
|
||||||
|
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||||
|
InvalidKeyException {
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||||
|
cipher.init(mode, sm4Key);
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
|
||||||
|
throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
|
||||||
|
NoSuchProviderException, NoSuchPaddingException {
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||||
|
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
||||||
|
cipher.init(mode, sm4Key, ivParameterSpec);
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
}
|
7
src/main/java/org.zz/gmhelper/cert/CertSNAllocator.java
Normal file
7
src/main/java/org.zz/gmhelper/cert/CertSNAllocator.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public interface CertSNAllocator {
|
||||||
|
BigInteger nextSerialNumber() throws Exception;
|
||||||
|
}
|
69
src/main/java/org.zz/gmhelper/cert/CommonUtil.java
Normal file
69
src/main/java/org.zz/gmhelper/cert/CommonUtil.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.operator.ContentSigner;
|
||||||
|
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
|
||||||
|
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
|
||||||
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
|
||||||
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
|
||||||
|
import org.zz.gmhelper.cert.exception.InvalidX500NameException;
|
||||||
|
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CommonUtil {
|
||||||
|
/**
|
||||||
|
* 如果不知道怎么填充names,可以查看org.bouncycastle.asn1.x500.style.BCStyle这个类,
|
||||||
|
* names的key值必须是BCStyle.DefaultLookUp中存在的(可以不关心大小写)
|
||||||
|
*
|
||||||
|
* @param names
|
||||||
|
* @return
|
||||||
|
* @throws InvalidX500NameException
|
||||||
|
*/
|
||||||
|
public static X500Name buildX500Name(Map<String, String> names) throws InvalidX500NameException {
|
||||||
|
if (names == null || names.size() == 0) {
|
||||||
|
throw new InvalidX500NameException("names can not be empty");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
X500NameBuilder builder = new X500NameBuilder();
|
||||||
|
Iterator itr = names.entrySet().iterator();
|
||||||
|
BCStyle x500NameStyle = (BCStyle) BCStyle.INSTANCE;
|
||||||
|
Map.Entry entry;
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
entry = (Map.Entry) itr.next();
|
||||||
|
ASN1ObjectIdentifier oid = x500NameStyle.attrNameToOID((String) entry.getKey());
|
||||||
|
builder.addRDN(oid, (String) entry.getValue());
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new InvalidX500NameException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PKCS10CertificationRequest createCSR(X500Name subject, SM2PublicKey pubKey, PrivateKey priKey,
|
||||||
|
String signAlgo) throws OperatorCreationException {
|
||||||
|
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey);
|
||||||
|
ContentSigner signerBuilder = new JcaContentSignerBuilder(signAlgo)
|
||||||
|
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey);
|
||||||
|
return csrBuilder.build(signerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AlgorithmIdentifier findSignatureAlgorithmIdentifier(String algoName) {
|
||||||
|
DefaultSignatureAlgorithmIdentifierFinder sigFinder = new DefaultSignatureAlgorithmIdentifierFinder();
|
||||||
|
return sigFinder.find(algoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AlgorithmIdentifier findDigestAlgorithmIdentifier(String algoName) {
|
||||||
|
DefaultDigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
|
||||||
|
return digFinder.find(findSignatureAlgorithmIdentifier(algoName));
|
||||||
|
}
|
||||||
|
}
|
48
src/main/java/org.zz/gmhelper/cert/FileSNAllocator.java
Normal file
48
src/main/java/org.zz/gmhelper/cert/FileSNAllocator.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class FileSNAllocator implements CertSNAllocator {
|
||||||
|
private static final String SN_FILENAME = "sn.dat";
|
||||||
|
private static String snFilePath;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClassLoader loader = FileSNAllocator.class.getClassLoader();
|
||||||
|
snFilePath = loader.getResource(SN_FILENAME).getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized BigInteger nextSerialNumber() throws Exception {
|
||||||
|
BigInteger sn = readSN();
|
||||||
|
writeSN(sn.add(BigInteger.ONE));
|
||||||
|
return sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger readSN() throws IOException {
|
||||||
|
RandomAccessFile raf = null;
|
||||||
|
try {
|
||||||
|
raf = new RandomAccessFile(snFilePath, "r");
|
||||||
|
byte[] data = new byte[(int) raf.length()];
|
||||||
|
raf.read(data);
|
||||||
|
String snStr = new String(data);
|
||||||
|
return new BigInteger(snStr);
|
||||||
|
} finally {
|
||||||
|
if (raf != null) {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSN(BigInteger sn) throws IOException {
|
||||||
|
RandomAccessFile raf = null;
|
||||||
|
try {
|
||||||
|
raf = new RandomAccessFile(snFilePath, "rw");
|
||||||
|
raf.writeBytes(sn.toString(10));
|
||||||
|
} finally {
|
||||||
|
if (raf != null) {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
src/main/java/org.zz/gmhelper/cert/RandomSNAllocator.java
Normal file
78
src/main/java/org.zz/gmhelper/cert/RandomSNAllocator.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* This is simplified version of the RandomSerialNumberGenerator from the project
|
||||||
|
* https://github.com/xipki/xipki.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random serial number generator.
|
||||||
|
*
|
||||||
|
* This class is thread safe.
|
||||||
|
*
|
||||||
|
* @author Lijun Liao
|
||||||
|
*/
|
||||||
|
public class RandomSNAllocator implements CertSNAllocator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The highest bit is always set to 1, so the effective bit length is bitLen - 1. To ensure that
|
||||||
|
* at least 64 bit entropy, bitLen must be at least 65.
|
||||||
|
*/
|
||||||
|
private final static int MIN_SERIALNUMBER_SIZE = 65;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since serial number should be positive and maximal 20 bytes, the maximal value of bitLen is
|
||||||
|
* 159.
|
||||||
|
*/
|
||||||
|
private final static int MAX_SERIALNUMBER_SIZE = 159;
|
||||||
|
|
||||||
|
private static int[] AND_MASKS = new int[] {0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F};
|
||||||
|
|
||||||
|
private static int[] OR_MASKS = new int[] {0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40};
|
||||||
|
|
||||||
|
private final SecureRandom random;
|
||||||
|
|
||||||
|
private final int bitLen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with the bitLen = 65.
|
||||||
|
*/
|
||||||
|
public RandomSNAllocator() {
|
||||||
|
this(MIN_SERIALNUMBER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with the specification of bitLen.
|
||||||
|
* @param bitLen bit length of the serial number. The highest bit is always set to 1, so the
|
||||||
|
* effective bit length is bitLen - 1. Valid value is [65, 159].
|
||||||
|
*/
|
||||||
|
public RandomSNAllocator(int bitLen) {
|
||||||
|
if (bitLen < MIN_SERIALNUMBER_SIZE || bitLen > MAX_SERIALNUMBER_SIZE) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"%s may not be out of the range [%d, %d]: %d",
|
||||||
|
"bitLen", MIN_SERIALNUMBER_SIZE, MAX_SERIALNUMBER_SIZE, bitLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.random = new SecureRandom();
|
||||||
|
this.bitLen = bitLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger nextSerialNumber() {
|
||||||
|
final byte[] rdnBytes = new byte[(bitLen + 7) / 8];
|
||||||
|
final int ci = bitLen % 8;
|
||||||
|
|
||||||
|
random.nextBytes(rdnBytes);
|
||||||
|
if (ci != 0) {
|
||||||
|
rdnBytes[0] = (byte) (rdnBytes[0] & AND_MASKS[ci]);
|
||||||
|
}
|
||||||
|
rdnBytes[0] = (byte) (rdnBytes[0] | OR_MASKS[ci]);
|
||||||
|
|
||||||
|
return new BigInteger(1, rdnBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
166
src/main/java/org.zz/gmhelper/cert/SM2CertUtil.java
Normal file
166
src/main/java/org.zz/gmhelper/cert/SM2CertUtil.java
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.pkcs.ContentInfo;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||||
|
import org.bouncycastle.jce.interfaces.ECPublicKey;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
|
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
import org.bouncycastle.operator.InputDecryptorProvider;
|
||||||
|
import org.bouncycastle.pkcs.PKCS12PfxPdu;
|
||||||
|
import org.bouncycastle.pkcs.PKCS12SafeBag;
|
||||||
|
import org.bouncycastle.pkcs.PKCS12SafeBagFactory;
|
||||||
|
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||||
|
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
|
||||||
|
import org.zz.gmhelper.BCECUtil;
|
||||||
|
import org.zz.gmhelper.SM2Util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.cert.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SM2CertUtil {
|
||||||
|
public static BCECPublicKey getBCECPublicKey(X509Certificate sm2Cert) {
|
||||||
|
ECPublicKey pubKey = (ECPublicKey) sm2Cert.getPublicKey();
|
||||||
|
ECPoint q = pubKey.getQ();
|
||||||
|
ECParameterSpec parameterSpec = new ECParameterSpec(SM2Util.CURVE, SM2Util.G_POINT,
|
||||||
|
SM2Util.SM2_ECC_N, SM2Util.SM2_ECC_H);
|
||||||
|
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(q, parameterSpec);
|
||||||
|
return new BCECPublicKey(pubKey.getAlgorithm(), pubKeySpec,
|
||||||
|
BouncyCastleProvider.CONFIGURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验证书
|
||||||
|
*
|
||||||
|
* @param issuerPubKey 从颁发者CA证书中提取出来的公钥
|
||||||
|
* @param cert 待校验的证书
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean verifyCertificate(BCECPublicKey issuerPubKey, X509Certificate cert) {
|
||||||
|
try {
|
||||||
|
cert.verify(issuerPubKey, BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate getX509Certificate(String certFilePath) throws IOException, CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
is = new FileInputStream(certFilePath);
|
||||||
|
return getX509Certificate(is);
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate getX509Certificate(byte[] certBytes) throws CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);
|
||||||
|
return getX509Certificate(bais);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate getX509Certificate(InputStream is) throws CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return (X509Certificate) cf.generateCertificate(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CertPath getCertificateChain(String certChainPath) throws IOException, CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
is = new FileInputStream(certChainPath);
|
||||||
|
return getCertificateChain(is);
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CertPath getCertificateChain(byte[] certChainBytes) throws CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(certChainBytes);
|
||||||
|
return getCertificateChain(bais);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getCertificateChainBytes(CertPath certChain) throws CertificateEncodingException {
|
||||||
|
return certChain.getEncoded("PKCS7");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CertPath getCertificateChain(InputStream is) throws CertificateException, NoSuchProviderException {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return cf.generateCertPath(is, "PKCS7");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CertPath getCertificateChain(List<X509Certificate> certs) throws CertificateException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return cf.generateCertPath(certs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509Certificate getX509CertificateFromPfx(byte[] pfxDER, String passwd) throws Exception {
|
||||||
|
InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder()
|
||||||
|
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passwd.toCharArray());
|
||||||
|
PKCS12PfxPdu pfx = new PKCS12PfxPdu(pfxDER);
|
||||||
|
|
||||||
|
ContentInfo[] infos = pfx.getContentInfos();
|
||||||
|
if (infos.length != 2) {
|
||||||
|
throw new Exception("Only support one pair ContentInfo");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i != infos.length; i++) {
|
||||||
|
if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) {
|
||||||
|
PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider);
|
||||||
|
PKCS12SafeBag[] bags = dataFact.getSafeBags();
|
||||||
|
X509CertificateHolder certHoler = (X509CertificateHolder) bags[0].getBagValue();
|
||||||
|
return SM2CertUtil.getX509Certificate(certHoler.getEncoded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Not found X509Certificate in this pfx");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BCECPublicKey getPublicKeyFromPfx(byte[] pfxDER, String passwd) throws Exception {
|
||||||
|
return SM2CertUtil.getBCECPublicKey(getX509CertificateFromPfx(pfxDER, passwd));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BCECPrivateKey getPrivateKeyFromPfx(byte[] pfxDER, String passwd) throws Exception {
|
||||||
|
InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder()
|
||||||
|
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passwd.toCharArray());
|
||||||
|
PKCS12PfxPdu pfx = new PKCS12PfxPdu(pfxDER);
|
||||||
|
|
||||||
|
ContentInfo[] infos = pfx.getContentInfos();
|
||||||
|
if (infos.length != 2) {
|
||||||
|
throw new Exception("Only support one pair ContentInfo");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i != infos.length; i++) {
|
||||||
|
if (!infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData)) {
|
||||||
|
PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]);
|
||||||
|
PKCS12SafeBag[] bags = dataFact.getSafeBags();
|
||||||
|
PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo) bags[0].getBagValue();
|
||||||
|
PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider);
|
||||||
|
BCECPrivateKey privateKey = BCECUtil.convertPKCS8ToECPrivateKey(info.getEncoded());
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Not found Private Key in this pfx");
|
||||||
|
}
|
||||||
|
}
|
113
src/main/java/org.zz/gmhelper/cert/SM2PfxMaker.java
Normal file
113
src/main/java/org.zz/gmhelper/cert/SM2PfxMaker.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.DERBMPString;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||||
|
import org.bouncycastle.crypto.engines.DESedeEngine;
|
||||||
|
import org.bouncycastle.crypto.engines.RC2Engine;
|
||||||
|
import org.bouncycastle.crypto.modes.CBCBlockCipher;
|
||||||
|
import org.bouncycastle.pkcs.*;
|
||||||
|
import org.bouncycastle.pkcs.bc.BcPKCS12MacCalculatorBuilder;
|
||||||
|
import org.bouncycastle.pkcs.bc.BcPKCS12PBEOutputEncryptorBuilder;
|
||||||
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS12SafeBagBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
public class SM2PfxMaker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param privKey 用户私钥
|
||||||
|
* @param pubKey 用户公钥
|
||||||
|
* @param chain X509证书数组,切记这里固定了必须是3个元素的数组,且第一个必须是叶子证书、第二个为中级CA证书、第三个为根CA证书
|
||||||
|
* @param passwd 口令
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PKCSException
|
||||||
|
*/
|
||||||
|
public PKCS12PfxPdu makePfx(PrivateKey privKey, PublicKey pubKey, X509Certificate[] chain, String passwd)
|
||||||
|
throws NoSuchAlgorithmException, IOException, PKCSException {
|
||||||
|
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||||
|
|
||||||
|
PKCS12SafeBagBuilder taCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[2]);
|
||||||
|
taCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("Primary Certificate"));
|
||||||
|
|
||||||
|
PKCS12SafeBagBuilder caCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[1]);
|
||||||
|
caCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("Intermediate Certificate"));
|
||||||
|
|
||||||
|
PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[0]);
|
||||||
|
eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("User Key"));
|
||||||
|
eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
|
||||||
|
extUtils.createSubjectKeyIdentifier(pubKey));
|
||||||
|
|
||||||
|
char[] passwdChars = passwd.toCharArray();
|
||||||
|
PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey,
|
||||||
|
new BcPKCS12PBEOutputEncryptorBuilder(
|
||||||
|
PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC,
|
||||||
|
new CBCBlockCipher(new DESedeEngine())).build(passwdChars));
|
||||||
|
keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("User Key"));
|
||||||
|
keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
|
||||||
|
extUtils.createSubjectKeyIdentifier(pubKey));
|
||||||
|
|
||||||
|
PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder();
|
||||||
|
PKCS12SafeBag[] certs = new PKCS12SafeBag[3];
|
||||||
|
certs[0] = eeCertBagBuilder.build();
|
||||||
|
certs[1] = caCertBagBuilder.build();
|
||||||
|
certs[2] = taCertBagBuilder.build();
|
||||||
|
pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder(
|
||||||
|
PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC,
|
||||||
|
new CBCBlockCipher(new RC2Engine())).build(passwdChars),
|
||||||
|
certs);
|
||||||
|
pfxPduBuilder.addData(keyBagBuilder.build());
|
||||||
|
return pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwdChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param privKey 用户私钥
|
||||||
|
* @param pubKey 用户公钥
|
||||||
|
* @param cert X509证书
|
||||||
|
* @param passwd 口令
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PKCSException
|
||||||
|
*/
|
||||||
|
public PKCS12PfxPdu makePfx(PrivateKey privKey, PublicKey pubKey, X509Certificate cert, String passwd)
|
||||||
|
throws NoSuchAlgorithmException, IOException, PKCSException {
|
||||||
|
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||||
|
|
||||||
|
PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(cert);
|
||||||
|
eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("User Key"));
|
||||||
|
eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
|
||||||
|
extUtils.createSubjectKeyIdentifier(pubKey));
|
||||||
|
|
||||||
|
char[] passwdChars = passwd.toCharArray();
|
||||||
|
PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey,
|
||||||
|
new BcPKCS12PBEOutputEncryptorBuilder(
|
||||||
|
PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC,
|
||||||
|
new CBCBlockCipher(new DESedeEngine())).build(passwdChars));
|
||||||
|
keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
|
||||||
|
new DERBMPString("User Key"));
|
||||||
|
keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
|
||||||
|
extUtils.createSubjectKeyIdentifier(pubKey));
|
||||||
|
|
||||||
|
PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder();
|
||||||
|
PKCS12SafeBag[] certs = new PKCS12SafeBag[1];
|
||||||
|
certs[0] = eeCertBagBuilder.build();
|
||||||
|
pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder(
|
||||||
|
PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC,
|
||||||
|
new CBCBlockCipher(new RC2Engine())).build(passwdChars),
|
||||||
|
certs);
|
||||||
|
pfxPduBuilder.addData(keyBagBuilder.build());
|
||||||
|
return pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwdChars);
|
||||||
|
}
|
||||||
|
}
|
49
src/main/java/org.zz/gmhelper/cert/SM2Pkcs12Maker.java
Normal file
49
src/main/java/org.zz/gmhelper/cert/SM2Pkcs12Maker.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lijun Liao https:/github.com/xipki
|
||||||
|
*/
|
||||||
|
public class SM2Pkcs12Maker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param privKey 用户私钥
|
||||||
|
* @param chain X509证书数组,
|
||||||
|
* 第一个(index 0)为privKey对应的证书,index i+1 是index i的CA证书
|
||||||
|
* @param passwd 口令
|
||||||
|
* @return the PKCS#12 keystore
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws KeyStoreException
|
||||||
|
* @throws CertificateException
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PKCSException
|
||||||
|
*/
|
||||||
|
public KeyStore makePkcs12(PrivateKey privKey, X509Certificate[] chain, char[] passwd)
|
||||||
|
throws KeyStoreException, NoSuchProviderException,
|
||||||
|
NoSuchAlgorithmException, CertificateException, IOException {
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
|
||||||
|
ks.load(null, passwd);
|
||||||
|
ks.setKeyEntry("User Key", privKey, passwd, chain);
|
||||||
|
return ks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param privKey 用户私钥
|
||||||
|
* @param cert X509证书
|
||||||
|
* @param passwd 口令
|
||||||
|
* @return the PKCS12 keystore
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PKCSException
|
||||||
|
*/
|
||||||
|
public KeyStore makePkcs12(PrivateKey privKey, X509Certificate cert, char[] passwd)
|
||||||
|
throws KeyStoreException, NoSuchProviderException,
|
||||||
|
NoSuchAlgorithmException, CertificateException, IOException {
|
||||||
|
return makePkcs12(privKey, new X509Certificate[] {cert}, passwd);
|
||||||
|
}
|
||||||
|
}
|
81
src/main/java/org.zz/gmhelper/cert/SM2PrivateKey.java
Normal file
81
src/main/java/org.zz/gmhelper/cert/SM2PrivateKey.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1Encodable;
|
||||||
|
import org.bouncycastle.asn1.ASN1Encoding;
|
||||||
|
import org.bouncycastle.asn1.ASN1Primitive;
|
||||||
|
import org.bouncycastle.asn1.DERBitString;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||||
|
import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.spec.ECParameterSpec;
|
||||||
|
|
||||||
|
public class SM2PrivateKey extends BCECPrivateKey {
|
||||||
|
private transient DERBitString sm2PublicKey;
|
||||||
|
private boolean withCompression;
|
||||||
|
|
||||||
|
public SM2PrivateKey(BCECPrivateKey privateKey, BCECPublicKey publicKey) {
|
||||||
|
super(privateKey.getAlgorithm(), privateKey);
|
||||||
|
this.sm2PublicKey = getSM2PublicKeyDetails(new SM2PublicKey(publicKey.getAlgorithm(), publicKey));
|
||||||
|
this.withCompression = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPointFormat(String style) {
|
||||||
|
withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a PKCS8 representation of the key. The sequence returned
|
||||||
|
* represents a full PrivateKeyInfo object.
|
||||||
|
*
|
||||||
|
* @return a PKCS8 representation of the key.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
ECParameterSpec ecSpec = getParams();
|
||||||
|
ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
|
||||||
|
ASN1Encodable params = SM2PublicKey.ID_SM2_PUBKEY_PARAM;
|
||||||
|
|
||||||
|
int orderBitLength;
|
||||||
|
if (ecSpec == null) {
|
||||||
|
orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
|
||||||
|
} else {
|
||||||
|
orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
|
||||||
|
}
|
||||||
|
|
||||||
|
PrivateKeyInfo info;
|
||||||
|
org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
|
||||||
|
|
||||||
|
if (sm2PublicKey != null) {
|
||||||
|
keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), sm2PublicKey, params);
|
||||||
|
} else {
|
||||||
|
keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
|
||||||
|
|
||||||
|
return info.getEncoded(ASN1Encoding.DER);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DERBitString getSM2PublicKeyDetails(SM2PublicKey pub) {
|
||||||
|
try {
|
||||||
|
SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded()));
|
||||||
|
|
||||||
|
return info.getPublicKeyData();
|
||||||
|
} catch (IOException e) { // should never happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/main/java/org.zz/gmhelper/cert/SM2PublicKey.java
Normal file
44
src/main/java/org.zz/gmhelper/cert/SM2PublicKey.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||||
|
import org.bouncycastle.asn1.ASN1OctetString;
|
||||||
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECPoint;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
|
||||||
|
|
||||||
|
public class SM2PublicKey extends BCECPublicKey {
|
||||||
|
public static final ASN1ObjectIdentifier ID_SM2_PUBKEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
|
||||||
|
|
||||||
|
private boolean withCompression;
|
||||||
|
|
||||||
|
public SM2PublicKey(BCECPublicKey key) {
|
||||||
|
super(key.getAlgorithm(), key);
|
||||||
|
this.withCompression = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SM2PublicKey(String algorithm, BCECPublicKey key) {
|
||||||
|
super(algorithm, key);
|
||||||
|
this.withCompression = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
ASN1OctetString p = ASN1OctetString.getInstance(
|
||||||
|
new X9ECPoint(getQ(), withCompression).toASN1Primitive());
|
||||||
|
|
||||||
|
// stored curve is null if ImplicitlyCa
|
||||||
|
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
|
||||||
|
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ID_SM2_PUBKEY_PARAM),
|
||||||
|
p.getOctets());
|
||||||
|
|
||||||
|
return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPointFormat(String style) {
|
||||||
|
withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
|
||||||
|
}
|
||||||
|
}
|
273
src/main/java/org.zz/gmhelper/cert/SM2X509CertMaker.java
Normal file
273
src/main/java/org.zz/gmhelper/cert/SM2X509CertMaker.java
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
package org.zz.gmhelper.cert;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||||
|
import org.bouncycastle.asn1.DERIA5String;
|
||||||
|
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
|
||||||
|
import org.bouncycastle.asn1.x500.RDN;
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
|
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
||||||
|
import org.bouncycastle.asn1.x509.*;
|
||||||
|
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SM2X509CertMaker {
|
||||||
|
|
||||||
|
private static enum CertLevel {
|
||||||
|
RootCA,
|
||||||
|
SubCA,
|
||||||
|
EndEntity
|
||||||
|
} // class CertLevel
|
||||||
|
|
||||||
|
public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2";
|
||||||
|
|
||||||
|
private long certExpire;
|
||||||
|
private X500Name issuerDN;
|
||||||
|
private CertSNAllocator snAllocator;
|
||||||
|
private KeyPair issuerKeyPair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param issuerKeyPair 证书颁发者的密钥对。
|
||||||
|
* 其实一般的CA的私钥都是要严格保护的。
|
||||||
|
* 一般CA的私钥都会放在加密卡/加密机里,证书的签名由加密卡/加密机完成。
|
||||||
|
* 这里仅是为了演示BC库签发证书的用法,所以暂时不作太多要求。
|
||||||
|
* @param certExpire 证书有效时间,单位毫秒
|
||||||
|
* @param issuer 证书颁发者信息
|
||||||
|
* @param snAllocator 维护/分配证书序列号的实例,证书序列号应该递增且不重复
|
||||||
|
*/
|
||||||
|
public SM2X509CertMaker(KeyPair issuerKeyPair, long certExpire, X500Name issuer,
|
||||||
|
CertSNAllocator snAllocator) {
|
||||||
|
this.issuerKeyPair = issuerKeyPair;
|
||||||
|
this.certExpire = certExpire;
|
||||||
|
this.issuerDN = issuer;
|
||||||
|
this.snAllocator = snAllocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成根CA证书
|
||||||
|
*
|
||||||
|
* @param csr CSR
|
||||||
|
* @return 新的证书
|
||||||
|
* @throws Exception 如果错误发生
|
||||||
|
*/
|
||||||
|
public X509Certificate makeRootCACert(byte[] csr)
|
||||||
|
throws Exception {
|
||||||
|
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||||
|
return makeCertificate(CertLevel.RootCA, null, csr, usage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成SubCA证书
|
||||||
|
*
|
||||||
|
* @param csr CSR
|
||||||
|
* @return 新的证书
|
||||||
|
* @throws Exception 如果错误发生
|
||||||
|
*/
|
||||||
|
public X509Certificate makeSubCACert(byte[] csr)
|
||||||
|
throws Exception {
|
||||||
|
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||||
|
return makeCertificate(CertLevel.SubCA, 0, csr, usage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成SSL用户证书
|
||||||
|
*
|
||||||
|
* @param csr CSR
|
||||||
|
* @return 新的证书
|
||||||
|
* @throws Exception 如果错误发生
|
||||||
|
*/
|
||||||
|
public X509Certificate makeSSLEndEntityCert(byte[] csr)
|
||||||
|
throws Exception {
|
||||||
|
return makeEndEntityCert(csr,
|
||||||
|
new KeyPurposeId[] {KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成用户证书
|
||||||
|
*
|
||||||
|
* @param csr CSR
|
||||||
|
* @param extendedKeyUsages 扩展指数用途。
|
||||||
|
* @return 新的证书
|
||||||
|
* @throws Exception 如果错误发生
|
||||||
|
*/
|
||||||
|
public X509Certificate makeEndEntityCert(byte[] csr,
|
||||||
|
KeyPurposeId[] extendedKeyUsages)
|
||||||
|
throws Exception {
|
||||||
|
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
|
||||||
|
| KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);
|
||||||
|
return makeCertificate(CertLevel.SubCA, null, csr, usage, extendedKeyUsages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param isCA 是否是颁发给CA的证书
|
||||||
|
* @param keyUsage 证书用途
|
||||||
|
* @param csr CSR
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private X509Certificate makeCertificate(CertLevel certLevel, Integer pathLenConstrain,
|
||||||
|
byte[] csr, KeyUsage keyUsage, KeyPurposeId[] extendedKeyUsages)
|
||||||
|
throws Exception {
|
||||||
|
if (certLevel == CertLevel.EndEntity) {
|
||||||
|
if (keyUsage.hasUsages(KeyUsage.keyCertSign)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"keyusage keyCertSign is not allowed in EndEntity Certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr);
|
||||||
|
SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo();
|
||||||
|
|
||||||
|
PrivateKey issPriv = issuerKeyPair.getPrivate();
|
||||||
|
PublicKey issPub = issuerKeyPair.getPublic();
|
||||||
|
|
||||||
|
X500Name subject = request.getSubject();
|
||||||
|
String email = null;
|
||||||
|
String commonName = null;
|
||||||
|
/*
|
||||||
|
* RFC 5280 §4.2.1.6 Subject
|
||||||
|
* Conforming implementations generating new certificates with
|
||||||
|
* electronic mail addresses MUST use the rfc822Name in the subject
|
||||||
|
* alternative name extension (Section 4.2.1.6) to describe such
|
||||||
|
* identities. Simultaneous inclusion of the emailAddress attribute in
|
||||||
|
* the subject distinguished name to support legacy implementations is
|
||||||
|
* deprecated but permitted.
|
||||||
|
*/
|
||||||
|
RDN[] rdns = subject.getRDNs();
|
||||||
|
List<RDN> newRdns = new ArrayList<>(rdns.length);
|
||||||
|
for (int i = 0; i < rdns.length; i++) {
|
||||||
|
RDN rdn = rdns[i];
|
||||||
|
|
||||||
|
AttributeTypeAndValue atv = rdn.getFirst();
|
||||||
|
ASN1ObjectIdentifier type = atv.getType();
|
||||||
|
if (BCStyle.EmailAddress.equals(type)) {
|
||||||
|
email = IETFUtils.valueToString(atv.getValue());
|
||||||
|
} else {
|
||||||
|
if (BCStyle.CN.equals(type)) {
|
||||||
|
commonName = IETFUtils.valueToString(atv.getValue());
|
||||||
|
}
|
||||||
|
newRdns.add(rdn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GeneralName> subjectAltNames = new LinkedList<>();
|
||||||
|
if (email != null) {
|
||||||
|
subject = new X500Name(newRdns.toArray(new RDN[0]));
|
||||||
|
subjectAltNames.add(
|
||||||
|
new GeneralName(GeneralName.rfc822Name,
|
||||||
|
new DERIA5String(email, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean selfSignedEECert = false;
|
||||||
|
switch (certLevel) {
|
||||||
|
case RootCA:
|
||||||
|
if (issuerDN.equals(subject)) {
|
||||||
|
subject = issuerDN;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SubCA:
|
||||||
|
if (issuerDN.equals(subject)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"subject MUST not equals issuer for certLevel " + certLevel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (issuerDN.equals(subject)) {
|
||||||
|
selfSignedEECert = true;
|
||||||
|
subject = issuerDN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger serialNumber = snAllocator.nextSerialNumber();
|
||||||
|
Date notBefore = new Date();
|
||||||
|
Date notAfter = new Date(notBefore.getTime() + certExpire);
|
||||||
|
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
|
||||||
|
issuerDN, serialNumber,
|
||||||
|
notBefore, notAfter,
|
||||||
|
subject, subPub);
|
||||||
|
|
||||||
|
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||||
|
v3CertGen.addExtension(Extension.subjectKeyIdentifier, false,
|
||||||
|
extUtils.createSubjectKeyIdentifier(subPub));
|
||||||
|
if (certLevel != CertLevel.RootCA && !selfSignedEECert) {
|
||||||
|
v3CertGen.addExtension(Extension.authorityKeyIdentifier, false,
|
||||||
|
extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 5280 §4.2.1.9 Basic Constraints:
|
||||||
|
// Conforming CAs MUST include this extension in all CA certificates
|
||||||
|
// that contain public keys used to validate digital signatures on
|
||||||
|
// certificates and MUST mark the extension as critical in such
|
||||||
|
// certificates.
|
||||||
|
BasicConstraints basicConstraints;
|
||||||
|
if (certLevel == CertLevel.EndEntity) {
|
||||||
|
basicConstraints = new BasicConstraints(false);
|
||||||
|
} else {
|
||||||
|
basicConstraints = pathLenConstrain == null
|
||||||
|
? new BasicConstraints(true) : new BasicConstraints(pathLenConstrain.intValue());
|
||||||
|
}
|
||||||
|
v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints);
|
||||||
|
|
||||||
|
// RFC 5280 §4.2.1.3 Key Usage: When present, conforming CAs SHOULD mark this extension as critical.
|
||||||
|
v3CertGen.addExtension(Extension.keyUsage, true, keyUsage);
|
||||||
|
|
||||||
|
if (extendedKeyUsages != null) {
|
||||||
|
ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
|
||||||
|
v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku);
|
||||||
|
|
||||||
|
boolean forSSLServer = false;
|
||||||
|
for (KeyPurposeId purposeId : extendedKeyUsages) {
|
||||||
|
if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) {
|
||||||
|
forSSLServer = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forSSLServer) {
|
||||||
|
if (commonName == null) {
|
||||||
|
throw new IllegalArgumentException("commonName must not be null");
|
||||||
|
}
|
||||||
|
GeneralName name = new GeneralName(GeneralName.dNSName,
|
||||||
|
new DERIA5String(commonName, true));
|
||||||
|
subjectAltNames.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subjectAltNames.isEmpty()) {
|
||||||
|
v3CertGen.addExtension(Extension.subjectAlternativeName, false,
|
||||||
|
new GeneralNames(subjectAltNames.toArray(new GeneralName[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
|
||||||
|
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||||
|
.getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
|
||||||
|
cert.verify(issPub);
|
||||||
|
|
||||||
|
return cert;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception {
|
||||||
|
if (issPub.getAlgorithm().equals("EC")) {
|
||||||
|
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2);
|
||||||
|
contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
|
||||||
|
return contentSignerBuilder;
|
||||||
|
}
|
||||||
|
throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.zz.gmhelper.cert.exception;
|
||||||
|
|
||||||
|
public class InvalidX500NameException extends Exception {
|
||||||
|
private static final long serialVersionUID = 3192247087539921768L;
|
||||||
|
|
||||||
|
public InvalidX500NameException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidX500NameException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidX500NameException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidX500NameException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
109
src/main/java/org/paillier/CommonUtils.java
Executable file
109
src/main/java/org/paillier/CommonUtils.java
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
package org.paillier;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class CommonUtils {
|
||||||
|
public CommonUtils() {}
|
||||||
|
|
||||||
|
public static byte[] intToByte4(int i) {
|
||||||
|
byte[] targets = new byte[4];
|
||||||
|
targets[3] = (byte) (i & 0xFF);
|
||||||
|
targets[2] = (byte) (i >> 8 & 0xFF);
|
||||||
|
targets[1] = (byte) (i >> 16 & 0xFF);
|
||||||
|
targets[0] = (byte) (i >> 24 & 0xFF);
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] longToByte8(long lo) {
|
||||||
|
byte[] targets = new byte[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
int offset = (targets.length - 1 - i) * 8;
|
||||||
|
targets[i] = (byte) ((lo >>> offset) & 0xFF);
|
||||||
|
}
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] unsignedShortToByte2(int s) {
|
||||||
|
byte[] targets = new byte[2];
|
||||||
|
targets[0] = (byte) (s >> 8 & 0xFF);
|
||||||
|
targets[1] = (byte) (s & 0xFF);
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int byte2ToUnsignedShort(byte[] bytes) {
|
||||||
|
return byte2ToUnsignedShort(bytes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int byte2ToUnsignedShort(byte[] bytes, int off) {
|
||||||
|
int high = bytes[off];
|
||||||
|
int low = bytes[off + 1];
|
||||||
|
return (high << 8 & 0xFF00) | (low & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int byte4ToInt(byte[] bytes, int off) {
|
||||||
|
int b0 = bytes[off] & 0xFF;
|
||||||
|
int b1 = bytes[off + 1] & 0xFF;
|
||||||
|
int b2 = bytes[off + 2] & 0xFF;
|
||||||
|
int b3 = bytes[off + 3] & 0xFF;
|
||||||
|
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] asUnsignedByteArray(BigInteger paramBigInteger) {
|
||||||
|
byte[] arrayOfByte1 = paramBigInteger.toByteArray();
|
||||||
|
if (arrayOfByte1[0] == 0) {
|
||||||
|
byte[] arrayOfByte2 = new byte[arrayOfByte1.length - 1];
|
||||||
|
System.arraycopy(arrayOfByte1, 1, arrayOfByte2, 0, arrayOfByte2.length);
|
||||||
|
return arrayOfByte2;
|
||||||
|
}
|
||||||
|
return arrayOfByte1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] asUnsignedByteArray(BigInteger paramBigInteger, int byteLength) {
|
||||||
|
byte[] arrayOfByte1 = asUnsignedByteArray(paramBigInteger);
|
||||||
|
if (arrayOfByte1.length < byteLength) {
|
||||||
|
byte[] arrayOfByte2 = new byte[byteLength];
|
||||||
|
int offset = byteLength - arrayOfByte1.length;
|
||||||
|
for (int i = 0; i < offset; i++) {
|
||||||
|
arrayOfByte2[i] = 0;
|
||||||
|
}
|
||||||
|
System.arraycopy(arrayOfByte1, 0, arrayOfByte2, offset, arrayOfByte1.length);
|
||||||
|
return arrayOfByte2;
|
||||||
|
}
|
||||||
|
return arrayOfByte1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger fromUnsignedByteArray(byte[] paramArrayOfByte) {
|
||||||
|
return new BigInteger(1, paramArrayOfByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String byteToHexString(byte[] bt) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < bt.length; i++) {
|
||||||
|
String hex = Integer.toHexString(bt[i] & 0xFF);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hex = '0' + hex;
|
||||||
|
}
|
||||||
|
sb.append(hex.toUpperCase());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hexStringToBytes(String hexString) {
|
||||||
|
if (hexString == null || hexString.equals("")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
hexString = hexString.toUpperCase();
|
||||||
|
int length = hexString.length() / 2;
|
||||||
|
char[] hexChars = hexString.toCharArray();
|
||||||
|
byte[] d = new byte[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int pos = i * 2;
|
||||||
|
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | (charToByte(hexChars[pos + 1]) & 0xff));
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte charToByte(char c) {
|
||||||
|
return (byte) "0123456789ABCDEF".indexOf(c);
|
||||||
|
}
|
||||||
|
}
|
129
src/main/java/org/paillier/PaillierCipher.java
Executable file
129
src/main/java/org/paillier/PaillierCipher.java
Executable file
@ -0,0 +1,129 @@
|
|||||||
|
package org.paillier;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.RSAPrivateCrtKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class PaillierCipher {
|
||||||
|
|
||||||
|
public static String encrypt(BigInteger m, PublicKey publicKey) {
|
||||||
|
return CommonUtils.byteToHexString(encryptAsBytes(m, publicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encryptAsBytes(BigInteger m, PublicKey publicKey) {
|
||||||
|
RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;
|
||||||
|
BigInteger n = rsaPubKey.getModulus();
|
||||||
|
BigInteger g = n.add(BigInteger.ONE);
|
||||||
|
|
||||||
|
BigInteger random;
|
||||||
|
do {
|
||||||
|
random = new BigInteger(n.bitLength(), new Random());
|
||||||
|
} while (random.signum() != 1);
|
||||||
|
|
||||||
|
if (m.signum() == -1) {
|
||||||
|
m = m.mod(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger nsquare = n.multiply(n);
|
||||||
|
BigInteger ciphertext =
|
||||||
|
g.modPow(m, nsquare).multiply(random.modPow(n, nsquare)).mod(nsquare);
|
||||||
|
|
||||||
|
byte[] nBytes = CommonUtils.asUnsignedByteArray(n);
|
||||||
|
byte[] nLenBytes = CommonUtils.unsignedShortToByte2(nBytes.length);
|
||||||
|
byte[] cipherBytes = CommonUtils.asUnsignedByteArray(ciphertext, n.bitLength() / 4);
|
||||||
|
byte[] data = new byte[nLenBytes.length + nBytes.length + cipherBytes.length];
|
||||||
|
System.arraycopy(nLenBytes, 0, data, 0, nLenBytes.length);
|
||||||
|
System.arraycopy(nBytes, 0, data, nLenBytes.length, nBytes.length);
|
||||||
|
System.arraycopy(
|
||||||
|
cipherBytes, 0, data, nLenBytes.length + nBytes.length, cipherBytes.length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger decrypt(String ciphertext, PrivateKey privateKey) {
|
||||||
|
return decrypt(CommonUtils.hexStringToBytes(ciphertext), privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger decrypt(byte[] ciphertext, PrivateKey privateKey) {
|
||||||
|
RSAPrivateCrtKey rsaPriKey = (RSAPrivateCrtKey) privateKey;
|
||||||
|
BigInteger n = rsaPriKey.getModulus();
|
||||||
|
BigInteger lambda =
|
||||||
|
rsaPriKey
|
||||||
|
.getPrimeP()
|
||||||
|
.subtract(BigInteger.ONE)
|
||||||
|
.multiply(rsaPriKey.getPrimeQ().subtract(BigInteger.ONE));
|
||||||
|
|
||||||
|
int nLen = CommonUtils.byte2ToUnsignedShort(ciphertext);
|
||||||
|
byte[] nBytes = new byte[nLen];
|
||||||
|
System.arraycopy(ciphertext, 2, nBytes, 0, nLen);
|
||||||
|
BigInteger n1 = CommonUtils.fromUnsignedByteArray(nBytes);
|
||||||
|
if (n1.compareTo(n) != 0) {
|
||||||
|
System.err.println("Invalid ciphertext, cannot match n parameter");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = new byte[ciphertext.length - nLen - 2];
|
||||||
|
System.arraycopy(ciphertext, 2 + nLen, data, 0, ciphertext.length - nLen - 2);
|
||||||
|
BigInteger intCiphertext = CommonUtils.fromUnsignedByteArray(data);
|
||||||
|
|
||||||
|
BigInteger mu = lambda.modInverse(n);
|
||||||
|
BigInteger nsquare = n.multiply(n);
|
||||||
|
BigInteger message =
|
||||||
|
intCiphertext
|
||||||
|
.modPow(lambda, nsquare)
|
||||||
|
.subtract(BigInteger.ONE)
|
||||||
|
.divide(n)
|
||||||
|
.multiply(mu)
|
||||||
|
.mod(n);
|
||||||
|
BigInteger maxValue = BigInteger.ONE.shiftLeft(n.bitLength() / 2);
|
||||||
|
if (message.compareTo(maxValue) > 0) {
|
||||||
|
return message.subtract(n);
|
||||||
|
} else {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String ciphertextAdd(String ciphertext1, String ciphertext2) {
|
||||||
|
return CommonUtils.byteToHexString(
|
||||||
|
ciphertextAdd(
|
||||||
|
CommonUtils.hexStringToBytes(ciphertext1),
|
||||||
|
CommonUtils.hexStringToBytes(ciphertext2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ciphertextAdd(byte[] ciphertext1, byte[] ciphertext2) {
|
||||||
|
int nLen1 = CommonUtils.byte2ToUnsignedShort(ciphertext1);
|
||||||
|
byte[] nBytes1 = new byte[nLen1];
|
||||||
|
System.arraycopy(ciphertext1, 2, nBytes1, 0, nLen1);
|
||||||
|
BigInteger n1 = CommonUtils.fromUnsignedByteArray(nBytes1);
|
||||||
|
byte[] data1 = new byte[ciphertext1.length - nLen1 - 2];
|
||||||
|
System.arraycopy(ciphertext1, 2 + nLen1, data1, 0, ciphertext1.length - nLen1 - 2);
|
||||||
|
|
||||||
|
int nLen2 = CommonUtils.byte2ToUnsignedShort(ciphertext2);
|
||||||
|
byte[] nBytes2 = new byte[nLen2];
|
||||||
|
System.arraycopy(ciphertext2, 2, nBytes2, 0, nLen2);
|
||||||
|
BigInteger n2 = CommonUtils.fromUnsignedByteArray(nBytes2);
|
||||||
|
if (n2.compareTo(n1) != 0) {
|
||||||
|
System.err.println("ciphertext1 cannot match ciphertext2");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data2 = new byte[ciphertext2.length - nLen2 - 2];
|
||||||
|
System.arraycopy(ciphertext2, 2 + nLen2, data2, 0, ciphertext2.length - nLen2 - 2);
|
||||||
|
|
||||||
|
BigInteger ct1 = CommonUtils.fromUnsignedByteArray(data1);
|
||||||
|
BigInteger ct2 = CommonUtils.fromUnsignedByteArray(data2);
|
||||||
|
BigInteger nsquare = n1.multiply(n1);
|
||||||
|
BigInteger ct = ct1.multiply(ct2).mod(nsquare);
|
||||||
|
|
||||||
|
byte[] nLenBytes = CommonUtils.unsignedShortToByte2(nBytes1.length);
|
||||||
|
byte[] cipherBytes = CommonUtils.asUnsignedByteArray(ct, n1.bitLength() / 4);
|
||||||
|
byte[] data = new byte[nLenBytes.length + nBytes1.length + cipherBytes.length];
|
||||||
|
System.arraycopy(nLenBytes, 0, data, 0, nLenBytes.length);
|
||||||
|
System.arraycopy(nBytes1, 0, data, nLenBytes.length, nBytes1.length);
|
||||||
|
System.arraycopy(
|
||||||
|
cipherBytes, 0, data, nLenBytes.length + nBytes1.length, cipherBytes.length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
113
src/main/java/org/paillier/PaillierKeyPair.java
Executable file
113
src/main/java/org/paillier/PaillierKeyPair.java
Executable file
@ -0,0 +1,113 @@
|
|||||||
|
package org.paillier;
|
||||||
|
|
||||||
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
|
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
||||||
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
|
import org.bouncycastle.util.io.pem.PemWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
public class PaillierKeyPair {
|
||||||
|
|
||||||
|
public static KeyPair generateGoodKeyPair() {
|
||||||
|
return generateKeyPair(2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyPair generateStrongKeyPair() {
|
||||||
|
return generateKeyPair(4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeyPair generateKeyPair(int len) {
|
||||||
|
if (len < 2048) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyPairGenerator generator;
|
||||||
|
try {
|
||||||
|
generator = KeyPairGenerator.getInstance("RSA");
|
||||||
|
generator.initialize(len, new SecureRandom());
|
||||||
|
return generator.generateKeyPair();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String publicKeyToPem(PublicKey publicKey) {
|
||||||
|
StringWriter pemStrWriter = new StringWriter();
|
||||||
|
PemWriter pemWriter = null;
|
||||||
|
try {
|
||||||
|
pemWriter = new PemWriter(pemStrWriter);
|
||||||
|
PemObject pemObject = new PemObject("PUBLIC KEY", publicKey.getEncoded());
|
||||||
|
pemWriter.writeObject(pemObject);
|
||||||
|
pemWriter.flush();
|
||||||
|
pemWriter.close();
|
||||||
|
return pemStrWriter.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey pemToPublicKey(String publicKeyStr) {
|
||||||
|
try {
|
||||||
|
StringReader pemStrReader = new StringReader(publicKeyStr);
|
||||||
|
PemReader pemReader = new PemReader(pemStrReader);
|
||||||
|
byte[] pubKey = pemReader.readPemObject().getContent();
|
||||||
|
pemReader.close();
|
||||||
|
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||||
|
return kf.generatePublic(new X509EncodedKeySpec(pubKey));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String privateKeyToPem(PrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
StringWriter pemStrWriter = new StringWriter();
|
||||||
|
PemWriter pemWriter = new PemWriter(pemStrWriter);
|
||||||
|
PemObjectGenerator pemObjectGenerator =
|
||||||
|
new PemObject("PRIVATE KEY", privateKey.getEncoded());
|
||||||
|
pemWriter.writeObject(pemObjectGenerator);
|
||||||
|
pemWriter.flush();
|
||||||
|
pemWriter.close();
|
||||||
|
return pemStrWriter.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey pemToPrivateKey(String privateKeyStr) {
|
||||||
|
try {
|
||||||
|
StringReader pemStrReader = new StringReader(privateKeyStr);
|
||||||
|
PemReader pemReader = new PemReader(pemStrReader);
|
||||||
|
byte[] priKey = pemReader.readPemObject().getContent();
|
||||||
|
pemReader.close();
|
||||||
|
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||||
|
return kf.generatePrivate(new PKCS8EncodedKeySpec(priKey));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/resources/libgmssljni.so
Executable file
BIN
src/main/resources/libgmssljni.so
Executable file
Binary file not shown.
161
src/test/java/org/paillier/PaillierTest.java
Executable file
161
src/test/java/org/paillier/PaillierTest.java
Executable file
@ -0,0 +1,161 @@
|
|||||||
|
package org.paillier;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.paillier.PaillierCipher;
|
||||||
|
import org.paillier.PaillierKeyPair;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.interfaces.RSAPrivateCrtKey;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
public class PaillierTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void goodKeyPairTest() {
|
||||||
|
KeyPair keypair = PaillierKeyPair.generateGoodKeyPair();
|
||||||
|
RSAPublicKey pubKey = (RSAPublicKey) keypair.getPublic();
|
||||||
|
RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) keypair.getPrivate();
|
||||||
|
System.out.println("e:" + priKey.getPublicExponent().intValue());
|
||||||
|
|
||||||
|
String publicKeyStr = PaillierKeyPair.publicKeyToPem(pubKey);
|
||||||
|
String privateKeyStr = PaillierKeyPair.privateKeyToPem(priKey);
|
||||||
|
|
||||||
|
RSAPublicKey pubKey1 = (RSAPublicKey) PaillierKeyPair.pemToPublicKey(publicKeyStr);
|
||||||
|
RSAPrivateKey priKey1 = (RSAPrivateKey) PaillierKeyPair.pemToPrivateKey(privateKeyStr);
|
||||||
|
Assert.assertEquals(pubKey, pubKey1);
|
||||||
|
Assert.assertEquals(priKey, priKey1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void strongKeyPairTest() {
|
||||||
|
KeyPair keypair = PaillierKeyPair.generateStrongKeyPair();
|
||||||
|
RSAPublicKey pubKey = (RSAPublicKey) keypair.getPublic();
|
||||||
|
RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) keypair.getPrivate();
|
||||||
|
|
||||||
|
String publicKeyStr = PaillierKeyPair.publicKeyToPem(pubKey);
|
||||||
|
String privateKeyStr = PaillierKeyPair.privateKeyToPem(priKey);
|
||||||
|
|
||||||
|
RSAPublicKey pubKey1 = (RSAPublicKey) PaillierKeyPair.pemToPublicKey(publicKeyStr);
|
||||||
|
RSAPrivateKey priKey1 = (RSAPrivateKey) PaillierKeyPair.pemToPrivateKey(privateKeyStr);
|
||||||
|
Assert.assertEquals(pubKey, pubKey1);
|
||||||
|
Assert.assertEquals(priKey, priKey1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptTest() {
|
||||||
|
KeyPair keypair = PaillierKeyPair.generateGoodKeyPair();
|
||||||
|
RSAPublicKey pubKey = (RSAPublicKey) keypair.getPublic();
|
||||||
|
|
||||||
|
BigInteger i1 = BigInteger.valueOf(1000000);
|
||||||
|
String c1 = PaillierCipher.encrypt(i1, pubKey);
|
||||||
|
System.out.println("c1.length:" + c1.length());
|
||||||
|
System.out.println("c1:" + c1);
|
||||||
|
|
||||||
|
BigInteger i2 = BigInteger.valueOf(-20000);
|
||||||
|
String c2 = PaillierCipher.encrypt(i2, pubKey);
|
||||||
|
System.out.println("c2.length:" + c2.length());
|
||||||
|
System.out.println("c2:" + c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptTest() {
|
||||||
|
String privateKeyStr =
|
||||||
|
"-----BEGIN PRIVATE KEY-----\n"
|
||||||
|
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1f/Oa//I+SFNN\n"
|
||||||
|
+ "v+PmHGJ1vLGFDu6A0IQV9DcX/hf2R2JFW/ONxfjlrtAh4dW7itMwIA6u64UlKiml\n"
|
||||||
|
+ "4UuVBT9ca4wv7R87DFqhxquh+ObMzo2x6QAaXnvL5WMmu8+/aLg6Nc06BXtJHsiw\n"
|
||||||
|
+ "TO7B09AcDNccdDyI+gXP4LEsIoh9HR1p41cq1RKe5c9khLryc5zQqf6NWlw4aWr9\n"
|
||||||
|
+ "AI5w4m7zhsTiDH1R9qLSJugOltY+9hLF7E9LexMLwXoQUYyi0WGxRe0h03+EQszv\n"
|
||||||
|
+ "4gQA4paJPAnk8k6VrUyTlBdeoYneInxZ0FEtFffk/rqVt4B80jjVRTbleSmDNvb7\n"
|
||||||
|
+ "KFp+KHSrAgMBAAECggEAG0jM2jQ3ul0tCLccD2+c7Y4cMaB5AixWbuZzkcvE1mUM\n"
|
||||||
|
+ "xNh52Io2THDnIPDOLI9GCCoJiwokzd10vVcNAa30RHR2co327+1/gmpXStYb/BXg\n"
|
||||||
|
+ "/ynDtjMV8STeruf05xVa/IUyANLqIafbC4XFLqYk1tKnU8O1hfHwBbwFZlkao5QH\n"
|
||||||
|
+ "IN9CyDIsyyv30JbHz47HJePZkTm/2iOq4ru+pKYFHeDz4EIEJRGl8HD7wtpo3F5E\n"
|
||||||
|
+ "mxe9uI5GMCdbswGnmw1a0u/VrSXpxwVyQMGHP6KWZ0dIunFW0Sh4DEdf9/tygtwx\n"
|
||||||
|
+ "nJ+/RYGqTKLv59+OkJGH/e6J6BV9rKYfjTiRC+hAAQKBgQD/1dW9WSXnquVGdJkq\n"
|
||||||
|
+ "QnohnMp/EkaXA3e3RiS9Bx9hhNwfdijqx3zyV1LnHGhuEaCXSCoy6zIOLQxOkkA8\n"
|
||||||
|
+ "rLCzpN/hp3c8pQUwYlQodZ+G/0272jCsD2vmdOuRP2WuFA9hqGgFvUbXkXh4mcEB\n"
|
||||||
|
+ "xKJY/jAddZvmnvs/dcLt6oJMqwKBgQC1nd18hcjQmIRARRUGs1ZU75yTaLKp+lXf\n"
|
||||||
|
+ "M7lg8RKE9sWf4ZoT3Snj+pimUOqliE10LiHruawa137q/UC+iI0/I4H9AUhv2mAw\n"
|
||||||
|
+ "m5drd+G4s6uTiCf7OQxBTmGHEvv5xH7gQih6sjOJI+N57xanC1XMGxijMOSy/D+O\n"
|
||||||
|
+ "sLxB8yJ4AQKBgQDYYX3kJnB+3vYIfznEmnE92KUUkNqPg2lP483S6yFJk9ux6Hh3\n"
|
||||||
|
+ "Cr7NIbqGqmpRHiubiHfYlUDC6KsOEXivWMgjSQHqk3+wFUqsP546kjGZNnoCtmqQ\n"
|
||||||
|
+ "PILgameLc/mGIIVZ7dv9brdqQCmKp1CtNCiz6Fm9sOlpR3HtnKaAH+aQ/QKBgFVW\n"
|
||||||
|
+ "37tidfEmqYY1r+KdJGT2zqEpokJi4jTmbiZSQPx/pG8zKB5LXyLEHzSPcyLjQFnm\n"
|
||||||
|
+ "T4Qfk/Js7jNnWyPssEpJ2gvTrYD5oRdWFTmndEZBDs9dPEQ9Ezggp40763D61w9z\n"
|
||||||
|
+ "pue4kqTPW1Vxdjh6CA/Hb7VHBT/hbdAT1fI7WCgBAoGAZq1rFESL3roi8DtOWl51\n"
|
||||||
|
+ "nduNO20Yloe6tlhUAKo63krRHKBeKTyLXycpZHcq6UEfys3dixFfu0lN6002lUku\n"
|
||||||
|
+ "MTbmNOJWhOCa2xuZY0CeINKFnKBnbiauBpo6x+2J0PoWFn8wd1tzFJPbodk8Km1f\n"
|
||||||
|
+ "qySov+6mrQxHojQYBu9/yYQ=\n"
|
||||||
|
+ "-----END PRIVATE KEY-----";
|
||||||
|
|
||||||
|
String cipher =
|
||||||
|
"0100B57FF39AFFF23E48534DBFE3E61C6275BCB1850EEE80D08415F43717FE17F64762455BF38DC5F8E5AED021E1D5BB8AD330200EAEEB85252A29A5E14B95053F5C6B8C2FED1F3B0C5AA1C6ABA1F8E6CCCE8DB1E9001A5E7BCBE56326BBCFBF68B83A35CD3A057B491EC8B04CEEC1D3D01C0CD71C743C88FA05CFE0B12C22887D1D1D69E3572AD5129EE5CF6484BAF2739CD0A9FE8D5A5C38696AFD008E70E26EF386C4E20C7D51F6A2D226E80E96D63EF612C5EC4F4B7B130BC17A10518CA2D161B145ED21D37F8442CCEFE20400E296893C09E4F24E95AD4C9394175EA189DE227C59D0512D15F7E4FEBA95B7807CD238D54536E579298336F6FB285A7E2874AB6914BF4FF089BFD98EC18D9E8B3D7FB2F5CFC20715C62D34F08E36D84F2CDABA2D1A1798C95161B7831167ED27E8894F1EB25D4E74DF382BF276D9ACBEADB56795F3DF8A4E6CE9DF7B6CFEFD5C66F0BC45D24CCC8E8095A7BF5CE69FEC5579B874A4C9B7C8F13126EC59D7C6DF0404816F638C7D4A84FE038E6F00B5667AC88E4307990E4C06B3864D86B7349275B20A3FB50FBA64706F214CC642219DCEF4453C30B89790F6FB1566A5D557AD7EC5890CA50E80111319F9742943FBD675D18753E5ABD21941832A11332ED902C334309E3770512AC042E1556C3F0ECCFC056C66D7362BA4E7896EA0807412817C68D7B5434AFA0D95A12B950573994F081F996545B871E485C392288E2D61C3B0CBB9FC4E68C1C558A598B03BACFF27967BE8AEA8F1322EC3E0957A3ED84810164A59BDEE2D1514EA68228CB96B59D8BA1E9234A24D57E5F8D7E55724EF0AE9D83F6E2A84B9A1E47B59091201B1B65542BBBB5A988CBBD5395335C4DF821ACEF289D20444B74CABC406A7C4F810EFF85838994DBDD38EDF74D4821153A5128AB98C15409C73891415B194803B3ABF761CEE57D1F58813A7125260E58864970CA2650E0D46C239ED92FCC3491C5FA372838B475D14E4946FCC3C421A76C434C5310D1A17A744551CFB5F99547BB216AD7C1ADA5C27CA64B34C29152D29B0A4B90B0C72A7A18BD19CF278B6F39186A39F91FB4D";
|
||||||
|
RSAPrivateKey priKey1 = (RSAPrivateKey) PaillierKeyPair.pemToPrivateKey(privateKeyStr);
|
||||||
|
|
||||||
|
BigInteger plain = PaillierCipher.decrypt(cipher, priKey1);
|
||||||
|
Assert.assertEquals(BigInteger.valueOf(1000000), plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void homAddTest() {
|
||||||
|
KeyPair keypair = PaillierKeyPair.generateGoodKeyPair();
|
||||||
|
RSAPublicKey pubKey = (RSAPublicKey) keypair.getPublic();
|
||||||
|
RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) keypair.getPrivate();
|
||||||
|
// System.out.println("e:" + priKey.getPublicExponent().intValue());
|
||||||
|
|
||||||
|
BigInteger i1 = BigInteger.valueOf(100000);
|
||||||
|
String c1 = PaillierCipher.encrypt(i1, pubKey);
|
||||||
|
|
||||||
|
BigInteger i2 = BigInteger.valueOf(-20000);
|
||||||
|
String c2 = PaillierCipher.encrypt(i2, pubKey);
|
||||||
|
|
||||||
|
String c3 = PaillierCipher.ciphertextAdd(c1, c2);
|
||||||
|
|
||||||
|
BigInteger o3 = PaillierCipher.decrypt(c3, priKey);
|
||||||
|
Assert.assertEquals(BigInteger.valueOf(80000), o3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void printTest() {
|
||||||
|
KeyPair keypair = PaillierKeyPair.generateGoodKeyPair();
|
||||||
|
RSAPublicKey pubKey = (RSAPublicKey) keypair.getPublic();
|
||||||
|
RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) keypair.getPrivate();
|
||||||
|
|
||||||
|
String publicKeyStr = PaillierKeyPair.publicKeyToPem(pubKey);
|
||||||
|
String privateKeyStr = PaillierKeyPair.privateKeyToPem(priKey);
|
||||||
|
|
||||||
|
System.out.println("public key:" + publicKeyStr);
|
||||||
|
System.out.println("private key:" + privateKeyStr);
|
||||||
|
|
||||||
|
RSAPublicKey pubKey1 = (RSAPublicKey) PaillierKeyPair.pemToPublicKey(publicKeyStr);
|
||||||
|
RSAPrivateKey priKey1 = (RSAPrivateKey) PaillierKeyPair.pemToPrivateKey(privateKeyStr);
|
||||||
|
|
||||||
|
BigInteger i1 = BigInteger.valueOf(1000000);
|
||||||
|
String c1 = PaillierCipher.encrypt(i1, pubKey1);
|
||||||
|
|
||||||
|
System.out.println("c1.length:" + c1.length());
|
||||||
|
System.out.println("c1:" + c1);
|
||||||
|
|
||||||
|
BigInteger o1 = PaillierCipher.decrypt(c1, priKey1);
|
||||||
|
System.out.println("o1:" + o1);
|
||||||
|
|
||||||
|
BigInteger i2 = BigInteger.valueOf(-20000);
|
||||||
|
String c2 = PaillierCipher.encrypt(i2, pubKey1);
|
||||||
|
System.out.println("c2.length:" + c2.length());
|
||||||
|
System.out.println("c2:" + c2);
|
||||||
|
|
||||||
|
BigInteger o2 = PaillierCipher.decrypt(c2, priKey1);
|
||||||
|
System.out.println("o2:" + o2);
|
||||||
|
|
||||||
|
String c3 = PaillierCipher.ciphertextAdd(c1, c2);
|
||||||
|
System.out.println("c3.length:" + c3.length());
|
||||||
|
System.out.println("c3:" + c3);
|
||||||
|
|
||||||
|
BigInteger o3 = PaillierCipher.decrypt(c3, priKey1);
|
||||||
|
System.out.println("o3:" + o3);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user