RSA加密和数字签名在Java中常见应用【原创】
相关术语解释:
1、私钥(PrivateKey)的生成
1.1、加载 PKCS #8 标准的PEM编码的字符串,并生成私钥(RSAPrivateKey)
关于PKCS #8: In cryptography, PKCS #8 is a standard syntax for storing private key information. PKCS #8 is one of the family of standards called Public-Key Cryptography Standards (PKCS) created by RSA Laboratories。PKCS #8 private keys are typically exchanged in the PEM base64-encoded format
如和生成RSA PEM 格式的私钥文件以及如何转换成 PKCS #8,参考: 《通过OpenSSL来生成PEM格式的私钥、PKCS8格式的私钥、公钥|pfx格式的私钥、cer格式的公钥》
私钥 PEM 内容样例如下:
-----BEGIN PRIVATE KEY-----
MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAq7BFUpkGp3+LQmlQ
Yx2eqzDV+xeG8kx/sQFV18S5JhzGeIJNA72wSeukEPojtqUyX2J0CciPBh7eqclQ
2zpAswIDAQABAkAgisq4+zRdrzkwH1ITV1vpytnkO/NiHcnePQiOW0VUybPyHoGM
/jf75C5xET7ZQpBe5kx5VHsPZj0CBb3b+wSRAiEA2mPWCBytosIU/ODRfq6EiV04
lt6waE7I2uSPqIC20LcCIQDJQYIHQII+3YaPqyhGgqMexuuuGx+lDKD6/Fu/JwPb
5QIhAKthiYcYKlL9h8bjDsQhZDUACPasjzdsDEdq8inDyLOFAiEAmCr/tZwA3qeA
ZoBzI10DGPIuoKXBd3nk/eBxPkaxlEECIQCNymjsoI7GldtujVnr1qT+3yedLfHK
srDVjIT3LsvTqw==
-----END PRIVATE KEY-----
使用下面的方法来生成私钥(RSAPrivateKey)需要删除上面的“-----BEGIN PRIVATE KEY-----” 和“-----END PRIVATE KEY-----”
Java 代码:
package rsa; import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.commons.codec.binary.Base64; @UtilityClass
public class PrivateKeyGen { /**
* 加载 PKCS8 私钥证书(PEM base64-encoded format)
* <br/> PKCS #8 is a standard syntax for storing private key information.
* <br/> PKCS #8 is one of the family of standards called Public-Key Cryptography Standards (PKCS) created by RSA Laboratories.
*
* @param privateKeyPem 私钥文件内容(PEM Base64编码)
*/
@SneakyThrows
public static RSAPrivateKey getPrivateKey(String privateKeyPem){
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyPem));
return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
} }
1.2、加载 PFX(PKCS #12 标准)文件并生成私钥(PrivateKey)
java代码:
package rsa; import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; @UtilityClass
public class PrivateKeyGen { /**
* 读取 PFX 格式的证书文件并生成 {@link PrivateKey} 类型实例
*
* @param keyStorePath PFX 格式的证书文件路径
* @param keyStorePasswd KeyStroe 的 password
*/
@SneakyThrows
public static PrivateKey getPrivateKey(String keyStorePath, String keyStorePasswd) {
@Cleanup FileInputStream fis = new FileInputStream(keyStorePath);
KeyStore store = KeyStore.getInstance("PKCS12");
store.load(fis, keyStorePasswd.toCharArray());
String alia = store.aliases().nextElement();
return (PrivateKey) store.getKey(alia, keyStorePasswd.toCharArray());
} }
1.3、根据证书的模(Modulus)和指数(Exponent)来生成私钥(PrivateKey)
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.RSAPrivateKeySpec;
import lombok.SneakyThrows; /**
* @author xfyou
*/
public class RsaPrivateKey { @SneakyThrows
private Key generatePrivateKey(byte[] keyModulus, byte[] keyExponent) {
return KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(newBigInteger(keyModulus), newBigInteger(keyExponent)));
} private static BigInteger newBigInteger(byte[] keyInfo) {
return new BigInteger(1, keyInfo);
} }
2、公钥(PublicKey)的生成
2.1、加载 PFX(PKCS #12 标准)文件并生成(导出)公钥(PublicKey)
Java代码:
package rsa; import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PublicKey;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; /**
* KeyGen
*/
@UtilityClass
public class KeyGen { /**
* 读取 PFX 格式的证书文件并生成 {@link PublicKey} 类型实例
*
* @param keyStorePath PFX 格式的证书文件路径
* @param keyStorePasswd KeyStroe 的 password
*/
@SneakyThrows
public static PublicKey getPublicKey(String keyStorePath, String keyStorePasswd) {
@Cleanup FileInputStream fis = new FileInputStream(keyStorePath);
KeyStore store = KeyStore.getInstance("PKCS12");
store.load(fis, keyStorePasswd.toCharArray());
String alia = store.aliases().nextElement();
return store.getCertificate(alia).getPublicKey();
} }
2.2、加载 符合 X.509 国际标准 PEM base64-encoded format 的证书内容,并生成公钥(RSAPublicKey)
需要删除证书内容(字符串)中的 “-----BEGIN CERTIFICATE-----” 和 “-----END CERTIFICATE-----”
公钥 PEM 证书内容样例如下:
-----BEGIN CERTIFICATE-----
MIIC6DCCAlGgAwIBAgIUI2ZSO2i7FA4iBKUOvjsZRzCQj8YwDQYJKoZIhvcNAQEL
BQAwgYUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ0hhaTERMA8GA1UEBwwI
mu1GI8mCpMYVGyUnJVNHqb3PG5uECbcKk8SfVg==
-----END CERTIFICATE-----
Java代码:
package rsa; import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.commons.codec.binary.Base64; @UtilityClass
public class KeyGen { /**
* 加载 PEM base64-encoded format 的公钥证书内容,并生成 {@link RSAPublicKey} 类型实例
*
* @param pemContent PEM base64-encoded format 的公钥证书内容,此公钥证书符合 X.509 国际标准
* @return {@link RSAPublicKey} 类型实例
*/
@SneakyThrows
private RSAPublicKey getPublicKey(String pemContent) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pemContent));
return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
} }
2.3、加载 符合 X.509 国际标准的CER(*.cer)格式的证书文件,并生成公钥(PublicKey)
Java代码:
package rsa; import java.io.FileInputStream;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; @UtilityClass
public class KeyGen { /**
* 读取符合 X.509 国际标准的 CER 格式的公钥证书文件,并生成 {@link PublicKey} 类型的实例
*
* @param cerPath 公钥证书文件(*.cer)的路径
*/
@SneakyThrows
public static PublicKey getPublicKey(String cerPath) {
@Cleanup FileInputStream bais = new FileInputStream(cerPath);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bais);
return cert.getPublicKey();
} }
如果通过读取完整的 PEM 证书内容(字符串)来生成公钥证书(PublicKey)则通过以下方式。
Java代码
package rsa; import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; @UtilityClass
public class KeyGen { /**
* 读取符合 X.509 国际标准的公钥证书的 PEM base64-encoded 内容字符串 ,并生成 {@link PublicKey} 类型的实例
*
* @param pubKeyCertPem 公钥证书 PEM base64-encoded 内容字符串
*/
@SneakyThrows
public static PublicKey getPublicKey(String pubKeyCertPem) {
@Cleanup InputStream is = new ByteArrayInputStream(pubKeyCertPem.getBytes(StandardCharsets.UTF_8));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
return cert.getPublicKey();
} }
2.4、根据证书的模(Modulus)和指数(Exponent)来生成公钥(PublicKey)
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.RSAPublicKeySpec;
import lombok.SneakyThrows; public class RsaPublicKey { @SneakyThrows
private Key generatePublicKey(byte[] keyModulus, byte[] keyExponent) {
return KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(newBigInteger(keyModulus), newBigInteger(keyExponent)));
} private static BigInteger newBigInteger(byte[] keyInfo) {
return new BigInteger(1, keyInfo);
} }
3、RSA非对称-加密
公钥加密,私钥解密 或 私钥加密,公钥解密
Java代码:
package rsa; import javax.crypto.Cipher;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.commons.codec.binary.Base64; @UtilityClass
public class CryptTool { /**
* RSA 公钥加密
*
* @param data 待加密的数据
* @return 加密后的字节数组
*/
@SneakyThrows
public byte[] encrypt(byte[] data) {
Cipher cipher = Cipher.getInstance("RSA");
// The key is the {@link java.security.PublicKey} instance
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
} /**
* RSA 公钥加密
*
* @param data 待加密的数据
* @return 加密后并Base64的字符串
*/
@SneakyThrows
public String encrypt(byte[] data) {
Cipher cipher = Cipher.getInstance("RSA");
// The key is the {@link java.security.PublicKey} instance
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64String(cipher.doFinal(data));
} }
4、RSA非对称-解密
Java代码:
package rsa; import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; @UtilityClass
public class CryptTool { /**
* RSA 私钥解密
*
* @param data 待解密的数据
* @return 解密后的字节数组
*/
@SneakyThrows
public byte[] decrypt(byte[] data) {
Cipher cipher = Cipher.getInstance("RSA");
// The key is the {@link java.security.PrivateKey} instance
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
} /**
* RSA 私钥解密
*
* @param data 待解密的数据
* @return 解密后的字符串
*/
@SneakyThrows
public String decrypt(String data) {
Cipher cipher = Cipher.getInstance("RSA");
// The key is the {@link java.security.PrivateKey} instance
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
} }
4、RSA非对称-签名
Java代码:
package rsa; import java.security.Signature;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.commons.codec.binary.Base64; @UtilityClass
public class CryptTool { /**
* RSA 使用私钥进行签名,可能的 Signature Algrithom: </br>
* <ol>
* <li>SHA1withRSA</li>
* <li>SHA256withRSA</li>
* <li>SHA384withRSA</li>
* <li>SHA512withRSA</li></li>
* </ol>
* @param data 待签名的数据
* @return 签名后的数据
*/
@SneakyThrows
public byte[] sign(byte[] data) {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
} /**
* RSA 使用私钥进行签名,可能的 Signature Algrithom: </br>
* <ol>
* <li>SHA1withRSA</li>
* <li>SHA256withRSA</li>
* <li>SHA384withRSA</li>
* <li>SHA512withRSA</li></li>
* </ol>
* @param data 待签名的数据
* @return 签名后的数据
*/
@SneakyThrows
public String sign(byte[] data) {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return Base64.encodeBase64String(signature.sign());
} }
4、RSA非对称-验签
Java代码:
package rsa; import java.security.Signature;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.commons.codec.binary.Base64; @UtilityClass
public class CryptTool { /**
* RSA 公钥验签
*
* @param data 待验签的数据
* @param sign 对方已签名的数据
* @return 验证结果
*/
@SneakyThrows
public boolean verify(byte[] data, String sign) {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(Base64.decodeBase64(sign));
} }
附:Signature
Algorithms
The algorithm names in this section can be specified when generating an instance of
Signature
.
Alg. Name | Description |
---|---|
NONEwithRSA |
The RSA signature algorithm which does not use a digesting algorithm (e.g. MD5/SHA1) before performing the RSA operation. For more information about the RSA Signature algorithms, please see PKCS1. |
MD2withRSA MD5withRSA |
The MD2/MD5 with RSA Encryption signature algorithm which uses the MD2/MD5 digest algorithm and RSA to create and verify RSA digital signatures as defined in PKCS1. |
SHA1withRSA SHA256withRSA |
The signature algorithm with SHA-* and the RSA encryption algorithm as defined in the OSI Interoperability Workshop, using the padding conventions described in PKCS1. |
NONEwithDSA |
The Digital Signature Algorithm as defined in FIPS PUB 186-2. The data must be exactly 20 bytes in length. This algorithms is also known under the alias name of rawDSA. |
SHA1withDSA |
The DSA with SHA-1 signature algorithm which uses the SHA-1 digest algorithm and DSA to create and verify DSA digital signatures as defined in FIPS PUB 186. |
NONEwithECDSA SHA1withECDSA SHA256withECDSA SHA384withECDSA SHA512withECDSA (ECDSA) |
The ECDSA signature algorithms as defined in ANSI X9.62. Note:"ECDSA" is an ambiguous name for the "SHA1withECDSA" algorithm and should not be used. The formal name "SHA1withECDSA" should be used instead. |
<digest>with<encryption> |
Use this to form a name for a signature algorithm with a particular message digest (such as MD2 or MD5) and algorithm (such as RSA or DSA), just as was done for the explicitly-defined standard names in this section (MD2withRSA, etc.). For the new signature schemes defined in PKCS1 v 2.0, for which the <digest>with<encryption> form is insufficient, <digest>with<encryption>and<mgf> can be used to form a name. Here, <mgf> should be replaced by a mask generation function such as MGF1. Example: MD5withRSAandMGF1. |
最新文章
- IIS同时实现网站部分使用https协议访问另一部分http访问
- OS存储器管理(一)
- 解决阿里云SLB无法添加https证书的问题
- 【技术贴】Eclipse 右键打开当前文件所在文件夹
- 2016030102 - Ubuntu软件安装与删除相关命令
- 转:synchronized和LOCK的实现原理---深入JVM锁机制
- centos7 开机启动某些程序的方法
- putty保持连接不自动段开
- PHP学习笔记2-流程控制
- 详解MariaDB数据库的外键约束
- 微信小程序【获取验证码】倒计时效果
- Express路由
- go语言的条件语句和循环语句
- 【疑难杂症】gdb调试多线程程序报错:interrupted system call
- numpy 中的 broadcasting 理解
- vue 使用高德地图vue-amap组件
- TcxGrid 复选框
- 【Python3】端口占用监测的程序
- 检查mono兼容性的工具MOAM
- Code First技术介绍