非对称加密-RSA
什么是RSA
RSA(Rivest-Shamir-Adleman)是一种基于数论的非对称加密算法,由 Ron Rivest、Adi Shamir 和 Leonard Adleman 在 1978 年共同提出。RSA 算法使用一对密钥来进行加密和解密,包括公钥和私钥。
什么是RSA2
RSA2 并不是一种具体的加密算法,而是一种常见的标记,用于指代 RSA 签名算法中使用 SHA-256(或更强大的 SHA-2 系列哈希函数)作为哈希算法的一种变体。RSA2 通常与传统的 RSA 签名算法区分开来,传统的 RSA 签名算法可能使用较弱的哈希算法,如 MD5 或 SHA-1,而 RSA2 使用更安全的 SHA-256 等哈希算法。
在实际应用中,随着哈希算法的安全性逐渐被提高,越来越多的系统和协议开始采用 RSA2(即 RSA with SHA-256)作为默认的签名算法。这是为了提高系统的安全性,防止因为哈希算法的弱点而导致的安全漏洞。
总之,RSA2 并不是一种独立的加密算法,而是一种标记,用于表示 RSA 签名算法中使用更安全的哈希算法(如 SHA-256)的变体。
简单理解比如MD5加盐再MD5,并不是一种独立的加密算法
RSA的应用场景
-
数据加密和解密:RSA 可以用于加密敏感数据,只有持有私钥的一方才能解密数据。这在安全传输和存储敏感信息时非常有用,比如在网络通信中传输信用卡信息、密码等。
-
数字签名:RSA 可以用于生成和验证数字签名,确保数据的完整性和认证发送方。发送方使用私钥对消息进行签名,接收方使用对应的公钥验证签名。这在网络通信中确保数据的完整性和来源可信性方面至关重要。
-
密钥协商:RSA 还可以用于安全地协商对称密钥,从而保护通信中传输的对称密钥的安全性。在安全通信协议中,使用 RSA 加密的方式,发送方可以使用接收方的公钥加密对称密钥,接收方再使用自己的私钥解密,从而实现对称密钥的安全交换。
-
身份认证:RSA 可以用于身份认证,例如在 SSL/TLS 握手过程中,服务器可以使用其私钥生成数字证书,证书包含了服务器的公钥和其他相关信息,客户端使用这些信息来验证服务器的身份,并通过加密通信建立安全连接。
-
数字票据和令牌:RSA 可以用于生成数字票据和令牌,用于访问控制、身份验证和授权等场景。例如,在身份验证系统中,服务器可以生成令牌并使用其私钥签名,客户端使用公钥验证令牌的有效性,从而实现安全的身份验证。
MAC下如何生成RSA秘钥
.安装openssl brew reinstall openssl 2.生成RSA私钥: openssl genrsa -out rsa_private_key.pem 1024 3.把RSA私钥转换成PKCS8格式 openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt 4.生成公钥 openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
什么是PKCS8
PKCS#8 是一种密码学标准,定义了一种通用的私钥信息语法(Private-Key Information Syntax),用于表示和存储私钥信息。PKCS#8 的全称是 "Public-Key Cryptography Standards #8",它是由 RSA Security 公司于 1995 年发布的。PKCS#8 通常用于与私钥相关的操作,如存储、传输和导出。
使用PKCS8好处
rsa_private_key.pem
-----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJfqsa3fdgoMkGFH vAcjFdtsUdO5p9qOJPAHMTdeWNBGNMsCST5iLE374ixE//l4Az8799JT4R1ZdJQ2 yB+NWH5sv+xKUBA57fcLrlFCZstxVAsqYGVVA0EWVxu1wcbtU9PwKnPlUCNLWFXL uUXzM9bbfF9OWABgHV753GmWwUgNAgMBAAECgYAYSAfsPZpVUtrE4b1Y+q+kqbSO fc8pbKzUvKGOMwNJDt+jTxM76F8ie88TulebnSYPtlsPAB8sHL84bWUhPetV0qoC X1whAuyS6ZnRuGujOLvX7ldQDBFCndTFlLdopV6jklMZKMURp/lbSG9/1qFpMEIh EvrG7goVT8Evrl/5tQJBAMfFMkfaCN3mqmP6XXReeXnchbnX53oQBqzoHu8H9aKV 7Ypu22TV2YUJy41zPPzHf35+XOPIK9WKT3oPohlNM/MCQQDCrVOyxvZMtx5c8z3s QUWqKg6vbv36+3jNgJkTu9yQVmbTcrsWIy06if6BXUUaqQ1xZCHqaBgrPK8dflfp YpP/AkBg9RSySuCC0rFgVOOG11OeEanVyTRPyfdzdDWPTg2qG5pu1d8l0aUBLP/e Z0/yX+FBQ5NR+pVLzxVD6NKD9TSBAkAj6ERf3ULgs4XuLw9k3EU1NbyvrxE5kdie TvHtKeFs3gLpTWgklShqe7ltsv5kBHpuR8HZsomCsBa8oeklKwGRAkEAkVDurxcP jOYq3BrU/En6t7mu6araJhUnYQ5+S+2pxBR8knTx9k5df+zcbAJv0PzLWVfeu4Pd N+9wlfCo050cLQ== -----END PRIVATE KEY-----
rsa_public_key.pem
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCX6rGt33YKDJBhR7wHIxXbbFHT uafajiTwBzE3XljQRjTLAkk+YixN++IsRP/5eAM/O/fSU+EdWXSUNsgfjVh+bL/s SlAQOe33C65RQmbLcVQLKmBlVQNBFlcbtcHG7VPT8Cpz5VAjS1hVy7lF8zPW23xf TlgAYB1e+dxplsFIDQIDAQAB -----END PUBLIC KEY-----
工具类demo
引入依赖
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency>
package com.example.demo; import org.apache.commons.codec.binary.Base64; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; /** * @description: RSA加密工具类 * @author: admin */ public class RsaTestUtils {
//hash算法,参考hash算法 private final static String SIGN_ALGORITHM= "MD5withRSA"; private final static String ALGORITHM="RSA"; public static void main(String[] args) throws Exception { //动态生成公钥和私钥 // Map<Integer, String> keyMap = genKeyPair(); //读取已有的公钥和私钥 Map<Integer, String> keyMap = getKeyPairByFile(); //加密字符串 String content = "我是原文啊!132456789"; System.out.println("加密前内容:" + content); System.out.println("公钥为:" + keyMap.get(0)); System.out.println("私钥为:" + keyMap.get(1)); //公钥加密 String messageEn = encrypt(content, keyMap.get(0)); System.out.println("加密后的内容为:" + messageEn); //私钥解密 String messageDe = decrypt(messageEn, keyMap.get(1)); System.out.println("还原后的内容为:" + messageDe); //生成签名 long signTimestamp = System.currentTimeMillis(); String signContent = "signTimestamp=" + signTimestamp; String sign=executeSignature(keyMap.get(1),signContent); System.out.println("签名为:"+sign); //验证签名 boolean sha1Verifty = verifySignature(keyMap.get(0),signContent,sign); System.out.println("验证结果:"+sha1Verifty); } /** * 执行签名 * * @param rsaPrivateKey 私钥 * @param src 参数内容 * @return 签名后的内容,base64后的字符串 * @throws InvalidKeyException InvalidKeyException * @throws NoSuchAlgorithmException NoSuchAlgorithmException * @throws InvalidKeySpecException InvalidKeySpecException * @throws SignatureException SignatureException */ public static String executeSignature(String rsaPrivateKey, String src) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { // base64解码私钥 byte[] decodePrivateKey = java.util.Base64.getDecoder().decode(rsaPrivateKey.replace("\r\n", "")); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateKey); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance(SIGN_ALGORITHM); //用md5生成内容摘要,再用RSA的私钥加密,进而生成数字签名 signature.initSign(privateKey); signature.update(src.getBytes()); // 生成签名 byte[] result = signature.sign(); // base64编码签名为字符串 return java.util.Base64.getEncoder().encodeToString(result); } /** * 验证签名 * * @param rsaPublicKey 公钥 * @param src 原始参数内容 * @param signature 签名 * @return 签名是否有效 * @throws NoSuchAlgorithmException NoSuchAlgorithmException * @throws InvalidKeySpecException InvalidKeySpecException * @throws InvalidKeyException InvalidKeyException * @throws SignatureException SignatureException */ public static boolean verifySignature(String rsaPublicKey, String src, String signature) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { // base64解码公钥 byte[] decodePublicKey = Base64.decodeBase64(rsaPublicKey.replace("\r\n", "")); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicKey); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Signature sig = Signature.getInstance(SIGN_ALGORITHM); sig.initVerify(publicKey); sig.update(src.getBytes()); // 对签名进行 base64 解码 byte[] sigBytes = Base64.decodeBase64(signature); // 验证签名 return sig.verify(sigBytes); } public static Map<Integer, String> getKeyPairByFile() throws Exception { InputStream is = RsaTestUtils.class.getResourceAsStream("/rsa_public_key.pem"); byte[] publicKeyBytes = new byte[is.available()]; is.read(publicKeyBytes); is.close(); publicKeyBytes=new String(publicKeyBytes).replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s+", "").getBytes(); is = RsaTestUtils.class.getResourceAsStream("/rsa_private_key.pem"); byte[] privateKeyBytes = new byte[is.available()]; is.read(privateKeyBytes); is.close(); privateKeyBytes=new String(privateKeyBytes).replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", "").getBytes(); Map<Integer,String> result=new HashMap<>(); result.put(0,new String(publicKeyBytes)); result.put(1,new String(privateKeyBytes)); return result; } /** * 随机生成密钥对 */ public static Map<Integer, String> genKeyPair() { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = null; try { keyPairGen = KeyPairGenerator.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } // 初始化密钥对生成器,密钥大小为96-1024位 assert keyPairGen != null; keyPairGen.initialize(1024, new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); // 得到私钥字符串 String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); // 将公钥和私钥保存到Map Map<Integer, String> keyMap = new HashMap<>(); // 0表示公钥 keyMap.put(0, publicKeyString); // 1表示私钥 keyMap.put(1, privateKeyString); return keyMap; } /** * RSA公钥加密 * * @param str 加密字符串 * @param publicKey 公钥 * @return 密文 */ public static String encrypt(String str, String publicKey) { // base64编码的公钥 byte[] decoded = Base64.decodeBase64(publicKey); RSAPublicKey pubKey; String outStr = null; try { pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8))); } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException e) { e.printStackTrace(); } // RSA加密 return outStr; } /** * RSA私钥解密 * * @param str 加密字符串 * @param privateKey 私钥 * @return 明文 */ public static String decrypt(String str, String privateKey) { //64位解码加密后的字符串 byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8)); //base64编码的私钥 byte[] decoded = Base64.decodeBase64(privateKey); RSAPrivateKey priKey; //RSA解密 Cipher cipher; String outStr = null; try { priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, priKey); outStr = new String(cipher.doFinal(inputByte)); } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) { e.printStackTrace(); } return outStr; } }
输出
加密前内容:我是原文啊!132456789 公钥为:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCX6rGt33YKDJBhR7wHIxXbbFHTuafajiTwBzE3XljQRjTLAkk+YixN++IsRP/5eAM/O/fSU+EdWXSUNsgfjVh+bL/sSlAQOe33C65RQmbLcVQLKmBlVQNBFlcbtcHG7VPT8Cpz5VAjS1hVy7lF8zPW23xfTlgAYB1e+dxplsFIDQIDAQAB 私钥为:MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJfqsa3fdgoMkGFHvAcjFdtsUdO5p9qOJPAHMTdeWNBGNMsCST5iLE374ixE//l4Az8799JT4R1ZdJQ2yB+NWH5sv+xKUBA57fcLrlFCZstxVAsqYGVVA0EWVxu1wcbtU9PwKnPlUCNLWFXLuUXzM9bbfF9OWABgHV753GmWwUgNAgMBAAECgYAYSAfsPZpVUtrE4b1Y+q+kqbSOfc8pbKzUvKGOMwNJDt+jTxM76F8ie88TulebnSYPtlsPAB8sHL84bWUhPetV0qoCX1whAuyS6ZnRuGujOLvX7ldQDBFCndTFlLdopV6jklMZKMURp/lbSG9/1qFpMEIhEvrG7goVT8Evrl/5tQJBAMfFMkfaCN3mqmP6XXReeXnchbnX53oQBqzoHu8H9aKV7Ypu22TV2YUJy41zPPzHf35+XOPIK9WKT3oPohlNM/MCQQDCrVOyxvZMtx5c8z3sQUWqKg6vbv36+3jNgJkTu9yQVmbTcrsWIy06if6BXUUaqQ1xZCHqaBgrPK8dflfpYpP/AkBg9RSySuCC0rFgVOOG11OeEanVyTRPyfdzdDWPTg2qG5pu1d8l0aUBLP/eZ0/yX+FBQ5NR+pVLzxVD6NKD9TSBAkAj6ERf3ULgs4XuLw9k3EU1NbyvrxE5kdieTvHtKeFs3gLpTWgklShqe7ltsv5kBHpuR8HZsomCsBa8oeklKwGRAkEAkVDurxcPjOYq3BrU/En6t7mu6araJhUnYQ5+S+2pxBR8knTx9k5df+zcbAJv0PzLWVfeu4PdN+9wlfCo050cLQ== 加密后的内容为:F7En6M8GUY3IFq0AV52fRnsU4HGDnz/aPD3ueLt7iTTs0iU5GMsCdgTGP7DghoLPgDgTcPHUBhPvVyHIPO1xmRx5J0zwfkV8w0qv1WXGTlDnwKAlRC3fBTMdeqIov+EFF2L2B1Gp1gmKq9DoUi2yjBGzuHmBpo4eWw2FfCzl1Sw= 还原后的内容为:我是原文啊!132456789 签名为:PheOR0z8M8+AgtnvZYF9/9T8yxlRbA0J5SH3qA7OUd/2ZqYaU98hcRh2tVwFFbehJqyVULiW2fGk7I+Qr66IOmKXalk32QBPWLLdPseFjSYm+a6wE2Y5j82N1ESeKKRukKVnI87X6oMnxfuzwY7Cjp3RY21YSMomUOe4tYdzTdc= 验证结果:true
获取签名对象的几种类型
在 Java 中,java.security.Signature.getInstance() 方法用于获取签名对象,该对象可用于执行数字签名和验证操作。参数 SIGN_ALGORITHMS 应该是一个字符串,指定要使用的签名算法。Java 支持的签名算法取决于 Java 运行时环境的安全提供者。一般来说,Java 支持的签名算法包括但不限于以下几种:
RSA:RSA 是一种非对称加密算法,常用于数字签名和密钥交换。
DSA:DSA(数字签名算法)是一种基于离散对数问题的数字签名算法。
ECDSA:ECDSA(椭圆曲线数字签名算法)是基于椭圆曲线的数字签名算法,具有与 RSA 和 DSA 相比更短的密钥长度。
SHAwithRSA:结合了 RSA 和 SHA-1、SHA-256 等哈希算法的签名算法。
SHAwithDSA:结合了 DSA 和 SHA-1、SHA-256 等哈希算法的签名算法。
SHAwithECDSA:结合了 ECDSA 和 SHA-1、SHA-256 等哈希算法的签名算法。
在使用 Signature.getInstance() 方法时,您需要提供与您要使用的算法相匹配的字符串。例如,对于使用 SHA-256 和 RSA 的数字签名,您可以使用 "SHA256withRSA" 作为算法字符串。具体的支持情况可能会根据您使用的 Java 版本和提供者而有所不同。
场景举例
服务提供者签名验证
1.使用者生成RSA公钥和私钥
2.使用者上传公钥到服务提供者
3.使用按照约定通过参数生成签名
4.使用者调用服务提供者接口传入签名和加签参数
5.服务提供者通过传入签名参数通过公钥验证签名是否有效
传输内容加密
双方都生成RSA秘钥,互相传递公钥
参数传递使用私钥加密
接收方用各自的公钥进行解密