一、ECC 橢圓曲線算法簡介
ECC是橢圓曲線算法,其加密算法叫ECIES,簽名算法叫ECDSA。JDK 并不支持 ECC 算法,可以引入 BouncyCastle 庫使用。ECC算法相當耗費資源,如果單純使用CPU進行加密/解密,效率低下。
引入 BouncyCastle 庫
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
二、ECC 加解密代碼實例
1.生成 ECC 密鑰
由于 BouncyCastle 庫對 keysize 有指定要求的,可以看 BouncyCastle 庫中的類:KeyPairGeneratorSpi,從中可以看出支持的 keysize 就這么幾個:192、239、256、224、384、521。
注意: 需要使用靜態初始化塊初始化 Provider,常量 EC_ALGORITHM = ”EC“ EC_PROVIDER = “BC”
static{
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
- 密鑰對對象
/**
* @author: huangyibo
* @Date: 2022/4/29 18:47
* @Description: 非對稱加密 密鑰對對象
*/
public class RsaKeyPair {
private String publicKey;
private String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
- 生成密鑰對
/**
* 生成密鑰對
*
* @param keySize 密鑰長度
* @return 密鑰對象
*/
public static RsaKeyPair generateEccKeyPair(int keySize) {
try {
// 獲取指定算法的密鑰對生成器
KeyPairGenerator generator = KeyPairGenerator.getInstance(EC_ALGORITHM, EC_PROVIDER);
// 初始化密鑰對生成器(指定密鑰長度, 使用默認的安全隨機數源)
generator.initialize(keySize);
// 隨機生成一對密鑰(包含公鑰和私鑰)
KeyPair keyPair = generator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(privateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2.ECC 加解密
ECIES_ALGORITHM = “ECIES”
EC_PROVIDER = “BC”
/**
* ECC 加密
*
* @param publicKeyText 公鑰
* @param data 原文
* @return 密文
*/
public static String eccEncrypt(String publicKeyText, String data) {
try {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance(ECIES_ALGORITHM, EC_PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(data.getBytes());
return Base64.encodeBase64String(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* ECC 解密
*
* @param privateKeyText 私鑰
* @param data 密文
* @return 原文
*/
public static String eccDecrypt(String privateKeyText, String data) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance(ECIES_ALGORITHM, EC_PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(data));
return new String(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.測試代碼
public static void main(String[] args) {
// 測試文本
String plain = "12345678";
// 生成密鑰對
RsaKeyPair rsaKeyPair = generateEccKeyPair(256);
System.out.println("ECC公鑰: "+rsaKeyPair.getPublicKey());
System.out.println("ECC私鑰: "+rsaKeyPair.getPrivateKey());
// 加解密
String encrypt = eccEncrypt(rsaKeyPair.getPublicKey(), plain);
String decrypt = eccDecrypt(rsaKeyPair.getPrivateKey(), encrypt);
System.err.println(plain.equals(decrypt));
}
三、ECC 簽名驗簽代碼實例
簽名驗簽使用的密鑰同上,簽名算法使用:SHA256withECDSA 即 SIGNATURE = “SHA256withECDSA”。
1. ECC 簽名驗簽
/**
* 私鑰簽名
*
* @param privateKey 私鑰
* @param data 原文
* @return 簽名
*/
public static String eccSign(String privateKey, String data) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PrivateKey key = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Signature signature = Signature.getInstance(SIGNATURE);
signature.initSign(key);
signature.update(data.getBytes());
return new String(Base64.encodeBase64(signature.sign()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 公鑰驗簽
*
* @param publicKey 公鑰
* @param srcData 原文
* @param sign 簽名
* @return
*/
public static boolean eccVerify(String publicKey, String srcData, String sign) {
try {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE);
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
2. 測試代碼
public static void main(String[] args) {
// 測試文本
String plain = "12345678";
// 生成密鑰對
RsaKeyPair rsaKeyPair = generateEccKeyPair(256);
System.out.println("ECC公鑰: "+rsaKeyPair.getPublicKey());
System.out.println("ECC私鑰: "+rsaKeyPair.getPrivateKey());
// 簽名驗簽
String sign = eccSign(rsaKeyPair.getPrivateKey(), plain);
boolean verify = eccVerify(rsaKeyPair.getPublicKey(), plain, sign);
System.err.println(verify);
}
完整代碼
import com.yibo.dsa.RsaKeyPair;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author: huangyibo
* @Date: 2022/4/29 10:31
* @Description:
*/
public class ECCUtils {
private static final String EC_ALGORITHM = "EC";
private static final String EC_PROVIDER = "BC";
private static final String ECIES_ALGORITHM = "ECIES";
private static final String SIGNATURE = "SHA256withECDSA";
static{
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 生成密鑰對
*
* @param keySize 密鑰長度
* @return 密鑰對象
*/
public static RsaKeyPair generateEccKeyPair(int keySize) {
try {
// 獲取指定算法的密鑰對生成器
KeyPairGenerator generator = KeyPairGenerator.getInstance(EC_ALGORITHM, EC_PROVIDER);
// 初始化密鑰對生成器(指定密鑰長度, 使用默認的安全隨機數源)
generator.initialize(keySize);
// 隨機生成一對密鑰(包含公鑰和私鑰)
KeyPair keyPair = generator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(privateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* ECC 加密
*
* @param publicKeyText 公鑰
* @param data 原文
* @return 密文
*/
public static String eccEncrypt(String publicKeyText, String data) {
try {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance(ECIES_ALGORITHM, EC_PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(data.getBytes());
return Base64.encodeBase64String(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* ECC 解密
*
* @param privateKeyText 私鑰
* @param data 密文
* @return 原文
*/
public static String eccDecrypt(String privateKeyText, String data) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance(ECIES_ALGORITHM, EC_PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(data));
return new String(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 私鑰簽名
*
* @param privateKey 私鑰
* @param data 原文
* @return 簽名
*/
public static String eccSign(String privateKey, String data) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PrivateKey key = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Signature signature = Signature.getInstance(SIGNATURE);
signature.initSign(key);
signature.update(data.getBytes());
return new String(Base64.encodeBase64(signature.sign()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 公鑰驗簽
*
* @param publicKey 公鑰
* @param srcData 原文
* @param sign 簽名
* @return
*/
public static boolean eccVerify(String publicKey, String srcData, String sign) {
try {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM);
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE);
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
// 測試文本
String plain = "12345678";
// 生成密鑰對
RsaKeyPair rsaKeyPair = generateEccKeyPair(256);
System.out.println("ECC公鑰: "+rsaKeyPair.getPublicKey());
System.out.println("ECC私鑰: "+rsaKeyPair.getPrivateKey());
// 加解密
String encrypt = eccEncrypt(rsaKeyPair.getPublicKey(), plain);
String decrypt = eccDecrypt(rsaKeyPair.getPrivateKey(), encrypt);
System.err.println(plain.equals(decrypt));
// 簽名驗簽
String sign = eccSign(rsaKeyPair.getPrivateKey(), plain);
boolean verify = eccVerify(rsaKeyPair.getPublicKey(), plain, sign);
System.err.println(verify);
}
}
參考:
https://blog.csdn.net/yuanjian0814/article/details/109815473