RSA加密和数字签名在Java中常见应用【原创】
相关术语解释:
RSA,参考: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
非对称加密算法 ,参考:https://baike.baidu.com/item/%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/1208652?fr=aladdin
PEM,参考:https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail
Base64,参考:https://en.wikipedia.org/wiki/Base64
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. |
以上是 RSA加密和数字签名在Java中常见应用【原创】 的全部内容, 来源链接: utcz.com/z/394400.html