Java 实现 ECC 非对称加密算法加解密和签名验签
前言
文章字数比较多,可直接查看代码:源码地址,文中描述有误的地方欢迎各位大神指导。
一、ECC 椭圆曲线算法简介
ECC是椭圆曲线算法,其加密算法叫ECIES,签名算法叫ECDSA。JDK 并不支持 ECC 算法,可以引入 BouncyCastle 库使用。ECC算法相当耗费资源,如果单纯使用CPU进行加密/解密,效率低下。
引入 BouncyCastle 库
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.64</version>
</dependency>
二、ECC 加解密代码实例
1.生成 ECC 密钥
由于 BouncyCastle 库对 keysize 有指定要求的,可以看 BouncyCastle 库中的类:KeyPairGeneratorSpi,从中可以看出支持的 keySize 就这么几个:192、239、256、224、384、521。
代码如下:
/**
* 生成密钥对
*
* @param keySize 密钥长度
* @return 密钥对
*/
public static KeyPair generateKeyPair(int keySize) throws Exception {
// 获取指定算法的密钥对生成器
Security.addProvider(new BouncyCastleProvider());
final KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "BC");
// 初始化密钥对生成器(指定密钥长度, 使用默认的安全随机数源)
generator.initialize(keySize);
// 随机生成一对密钥(包含公钥和私钥)
return generator.generateKeyPair();
}
2.ECC 加解密
代码如下:
/**
* ECC 加密
*
* @param publicKeyBytes 公钥
* @param plain 原文
* @return 密文
*/
public static byte[] encrypt(byte[] publicKeyBytes, byte[] plain) throws Exception {
final KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
final PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
final Cipher cipher = Cipher.getInstance("ECIES", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plain);
}
/**
* ECC 解密
*
* @param privateKeyBytes 私钥
* @param encrypted 密文
* @return 原文
*/
public static byte[] decrypt(byte[] privateKeyBytes, byte[] encrypted) throws Exception {
final KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
final PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
final PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
final Cipher cipher = Cipher.getInstance("ECIES", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
3.测试代码
public static void main(String[] args) throws Exception {
final String data = "test";
final KeyPair keyPair = generateKeyPair(256);
final byte[] encrypted = encrypt(keyPair.getPublic().getEncoded(), data.getBytes());
final byte[] decrypted = decrypt(keyPair.getPrivate().getEncoded(), encrypted);
System.out.println(new String(decrypted));
}
三、ECC 签名验签代码实例
签名验签使用的密钥同上,签名算法使用:SHA256withECDSA 即 SIGNATURE = “SHA256withECDSA”。
1. ECC 签名验签
/**
* 私钥签名
*
* @param privateKeyBytes 私钥
* @param plain 原文
* @return 签名
*/
public static byte[] signature(byte[] privateKeyBytes, byte[] plain) throws Exception {
final KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
final PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
final PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
final Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update(plain);
return signature.sign();
}
/**
* 公钥验签
*
* @param publicKeyBytes 公钥
* @param plain 原文
* @param sign 签名
*/
public static boolean verify(byte[] publicKeyBytes, byte[] plain, byte[] sign) throws Exception {
final KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
final PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
final Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initVerify(publicKey);
signature.update(plain);
return signature.verify(sign);
}
2.测试代码
public static void main(String[] args) throws Exception {
final String data = "test";
final KeyPair keyPair = generateKeyPair(256);
final byte[] signature = signature(keyPair.getPrivate().getEncoded(), data.getBytes());
final boolean verify = verify(keyPair.getPublic().getEncoded(), data.getBytes(), signature);
System.out.println("verify: " + verify);
}