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));
} }
附:SignatureAlgorithms
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中常见应用【原创】的更多相关文章
- Java中常见的5种WEB服务器介绍
这篇文章主要介绍了Java中常见的5种WEB服务器介绍,它们分别是Tomcat.Resin.JBoss.WebSphere.WebLogic,需要的朋友可以参考下 Web服务器是运行及发布Web应用的 ...
- Java中常见的Exception种类
Java中常见的Exception种类 1.ClassNotFoundException 2.IOException 3.NoSuchFieldException 4.NoSuchMethodExce ...
- Java基础-JAVA中常见的数据结构介绍
Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...
- Java中常见的注解
Java中常见的注解 1.JDK自带的注解@Override @Deprecated @Suppvisewarnings 常见第三方注解 Spring:@Autowired @Service ...
- Java中常见的异常类型
一. Java中常见的异常类 异常类 说明 ClassCastException 类型准换异常 ClassNotFoundException 未找到相应类异常 ArithmeticException ...
- Java 中常见的 final 类
Java 中常见的 final 类 java.lang 包 public final class Boolean extends Object implements Serializable, Com ...
- java中常见的六种线程池详解
之前我们介绍了线程池的四种拒绝策略,了解了线程池参数的含义,那么今天我们来聊聊Java 中常见的几种线程池,以及在jdk7 加入的 ForkJoin 新型线程池 首先我们列出Java 中的六种线程池如 ...
- RSA加密常用的填充方式 以及 常见错误
一.RSA加密常用的填充方式 1.RSA_PKCS1_PADDING 输入:比 RSA modulus 短至少11个字节.如果输入的明文过长,必须切割,然后填充 输出:和modulus一样长 根据这个 ...
- java 中常见异常
1. Java.lang.NullPointerException 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的 ...
随机推荐
- Python 序列、列表(List)、元组(Tuple)
序列 序列是Python中最基本的数据结构,包括字符串.列表.元组. 序列,顾名思义,是有序的,序列都有索引,都能进行索引.切片(截取).加(连接).乘(倍增).检查成员的操作. 因为序列有序,可通过 ...
- spring中WebApplicationContext、DispatcherServlet与web容器的ServletContext关系梳理
学习源码过程中,对各种context(上下文)表示很懵逼.特地留此一篇. 1.要了解各个上下文之间的关系.首先走一遍spring在web容器(tomcat)中的启动过程 a) ServletConte ...
- 泛微e-cology OA系统某接口存在数据库配置信息泄露漏洞复现
1.简介(开场废话) 攻击者可通过存在漏洞的页面直接获取到数据库配置信息.如果攻击者可直接访问数据库,则可直接获取用户数据,甚至可以直接控制数据库服务器. 2.影响范围 漏洞涉及范围包括不限于8.0. ...
- X509Store 类
标题:X509Store 类 地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.x509certi ...
- 图论篇2——最小生成树算法(kurskal算法&prim算法)
基本概念 树(Tree) 如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree) 无向连通图G的一个子图如果是一颗包含G的所有顶点的树,则该子图称为G的生成树. 生成 ...
- 201671030123叶虹 实验十四 团队项目评审&课程学习总结
项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十四 团队项目评审&课程学习总结 课程学习目标 掌握软件项目评审会流程:反思总结课程学习内容 一.实验一问题回答 1.实验 ...
- python应用-输入三个数,输出其最大值
""" 输入三个数,输出其最大值 Author:罗万财 Date:2017-7-6 """ a=int(input('a=')) b=int ...
- 26、pathlib文件系统模块(了解)
一.pathlib库官方定义 pathlib 是Python内置库,Python 文档给它的定义是 Object-oriented filesystem paths(面向对象的文件系统路径).path ...
- Maven的下载以及配置
Maven的下载以及配置 Maven的两大核心作用: (1)依赖管理:对Jar包的依赖,解决Jar包之间的冲突 (2)项目构建:项目从编译到测试到运行发布 一.Mavenu的下载(现在的eclipse ...
- (转)虚拟文件系统(VFS)浅析
http://www.cnblogs.com/zsw-1993/p/5048144.html 在我看来, "虚拟"二字主要有两层含义: 1, 在同一个目录结构中, 可以挂载着若干种 ...