非对称加密算法
非对称密码算法,也称为公开密钥加密或公钥加密技术。
非对称加密算法分类
主要包括以下几种分类:
- RSA算法:
- RSA算法是目前最广泛使用的非对称加密算法之一。
- 其安全性主要基于大质数分解难题。
- 公钥由两个参数组成:模数n和公钥指数e;私钥由模数n和私钥指数d组成。
- RSA算法的安全性取决于密钥长度,一般需要使用较长的密钥长度以保证安全性。
- 椭圆曲线密码算法(ECC):
- ECC是一种基于椭圆曲线数学理论的非对称加密算法。
- 与RSA算法相比,ECC算法使用的密钥长度更短,但其安全性仍然很高。
- ECC算法的安全性取决于选择的椭圆曲线,需要仔细选择和设计。
- ECC算法特别适用于资源受限的移动设备。
- ElGamal算法:
- ElGamal算法是一种基于离散对数问题的非对称加密算法。
- 其公钥由两个参数组成:素数p和本原根α;私钥由一个整数x组成。
- ElGamal算法的安全性也取决于密钥长度,一般需要使用较长的密钥长度以保证安全性。
- Diffie-Hellman算法:
- 该算法主要用于密钥交换,允许两个用户在公开通道上创建一个共享的秘密密钥。
- 虽然它本身不直接用于加密或解密数据,但它是许多非对称加密算法中的关键组成部分。
- 其他算法:
- 还包括背包算法、Rabin、McEliece密码、零知识证明等非对称加密算法。
这些非对称加密算法各有特点和优势,适用于不同的应用场景。在选择加密算法时,需要综合考虑安全性、效率、可扩展性等因素。例如,在需要高安全性的场合可以选择RSA算法,而在资源受限的移动设备上可以选择ECC算法。
RSA
算法
公钥和私钥的生成和使用
openSSL生成私钥,注意:这个钥匙不能用于作为应用的privateKey,需要使用下面的pkcs8格式作为privateKey
bash
openssl genrsa -out private.pem 1024
openSSL生成公钥
bash
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
openSSL转换公钥PEM到PKCS8格式,注意:这个钥匙作为privateKey
bash
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private.pem -out private.pem.pkcs8
注意:使用公钥和密钥时,只需要复制除-----xxx-----
外的内容即可,否则java
在解析钥匙时报告错误。
加密和解密
示例详细用法请参考 链接
示例代码:
java
@Test
public void testEncryptDecrypt() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String plainText = RandomStringUtils.random(16);
String algorithm = "RSA";
String plainSecretKey = RandomStringUtils.random(2048);
int keySize = 512;
// 生成秘钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keySize, new SecureRandom(plainSecretKey.getBytes()));
KeyPair keyPair = keyPairGenerator.generateKeyPair();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
// 私钥加密,公钥解密
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// PKCS#8标准中定义的一个结构,用于表示私钥信息。它包含一个版本字段、一个私钥算法标识符、一个私钥字段以及一个可选的属性字段。这个结构允许私钥以独立于公钥或证书的格式进行存储和传输
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptBytes = cipher.doFinal(plainText.getBytes());
keyFactory = KeyFactory.getInstance(algorithm);
// 于将X.509证书中的公钥转换为Java的公钥对象
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptBytes = cipher.doFinal(encryptBytes);
String plainTextDecrypt = new String(decryptBytes);
Assert.assertEquals(plainText, plainTextDecrypt);
// 公钥加密,私钥解密
keyFactory = KeyFactory.getInstance(algorithm);
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptBytes = cipher.doFinal(plainText.getBytes());
keyFactory = KeyFactory.getInstance(algorithm);
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptBytes = cipher.doFinal(encryptBytes);
plainTextDecrypt = new String(decryptBytes);
Assert.assertEquals(plainText, plainTextDecrypt);
}
读取公钥和私钥密钥对进行加密和解密
示例详细用法请参考 链接
参考上面的公钥和私钥的生成和使用生成密钥对
示例代码:
java
@Test
public void testOpenSSLKeyPair() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String publicKeyString = System.getenv("publicKey");
String privateKeyString = System.getenv("privateKey");
String plainText = RandomStringUtils.random(16);
String algorithm = "RSA";
byte[] privateKeyBytes = Base64.decode(privateKeyString);
byte[] publicKeyBytes = Base64.decode(publicKeyString);
// 私钥加密,公钥解密
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptBytes = cipher.doFinal(plainText.getBytes());
keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptBytes = cipher.doFinal(encryptBytes);
String plainTextDecrypt = new String(decryptBytes);
Assert.assertEquals(plainText, plainTextDecrypt);
// 公钥加密,私钥解密
keyFactory = KeyFactory.getInstance(algorithm);
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptBytes = cipher.doFinal(plainText.getBytes());
keyFactory = KeyFactory.getInstance(algorithm);
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptBytes = cipher.doFinal(encryptBytes);
plainTextDecrypt = new String(decryptBytes);
Assert.assertEquals(plainText, plainTextDecrypt);
}
数字签名和验签
示例详细用法请参考 链接
参考上面的公钥和私钥的生成和使用生成密钥对
示例代码:
java
@Test
public void testDigitalSignAndVerify() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
String publicKeyString = System.getenv("publicKey");
String privateKeyString = System.getenv("privateKey");
String plainText = RandomStringUtils.random(16);
byte[] privateKeyBytes = Base64.decode(privateKeyString);
byte[] publicKeyBytes = Base64.decode(publicKeyString);
// 签名
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
signature.initSign(priKey);
signature.update(plainText.getBytes());
byte[] signed = signature.sign();
String contentSigned = Base64.encode(signed);
// 签名验证
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
signature = java.security.Signature.getInstance("SHA256WithRSA");
signature.initVerify(pubKey);
// 使用公钥验证数字签名是否是对应的密钥签发的
signature.update(plainText.getBytes());
boolean bverify = signature.verify(Base64.decode(contentSigned));
Assert.assertTrue(bverify);
}