一、前言 1.1 问题思考 为什么需要加密 / 解密?信息泄露可能造成什么影响? 二、 基础回顾 2.1 加密技术 加密技术是最常用的安全保密手段,利用技术手段把重要的数据变为乱码(加密)传送,到达目
加密技术是最常用的安全保密手段,利用技术手段把重要的数据变为乱码(加密)传送,到达目的地后再用相同或不同的手段还原(解密)。
加密技术包括两个元素:算法和密钥。算法是将普通的信息或者可以理解的信息与一串数字(密钥)结合,产生不可理解的密文的步骤,密钥是用来对数据进行编码和解密的一种算法。在安全保密中,可通过适当的钥加密技术和管理机制来保证网络的信息通信安全。简言之:
加密目的:就是为了保护数据在存储状态下和在传输过程中,不被窃取、解读和利用。简单的说:确保数据的机密性和保护信息的完整性;
加密的方式:包括单向散列加密、对称加密、非对称加密三种。
方式一:单向散列加密
根据输入长度信息进行散列计算,得到固定长度输出,常用于密码保存,常见的是MD5,SHA等,通常会加盐处理;
特点: 加密效率高、单方向加密
安全性:不安全(相对于对称加密)
使用情况:比较主流的加密方式
方式二:对称加密
采用单钥密码系统加密方法,同一个密钥可以同时用作信息的加密和解密。常见有AES
特点:加密效率高、双方使用的密钥相同
安全性:不安全(相对于非对称加密)
使用情况:比较主流的加密方式
方式三:非对称加密
加密和解密使用的是不同的秘钥,其中一个对外公开,称为公钥,另一个被称为私钥。若使用公钥对数据进行加密,则只有使用对应的私钥才能解密,反之亦然。常见的有RSA。
关于密钥
特点
安全性较高,使用的时候,一般配合对称机密使用,建立之初先使用非对称加密,协商好对称加密的算法和密钥,然后使用对称加密,进行后续加解密。
比如:使用对称加密加密很大的数据,非对称加密可以加密对称加密的密钥,报文一起传输,这样既保证了安全性,又保证了加密效率。
import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;import java.security.*;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.HashMap;import java.util.Map;public class RSAUtil01 { private static final String RSA_KEY_ALGoRITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "MD5withRSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; private static final int KEY_SIZE = 1024; private static Map<String, String> iniTKEy() throws Exception { KeyPairGenerator keygen = KeyPairGenerator.getInstance(RSA_KEY_ALGORITHM); SecureRandom secrand = new SecureRandom(); secrand.setSeed("initSeed".getBytes()); keygen.initialize(KEY_SIZE, secrand); KeyPair keys = keygen.genKeyPair(); // 获取公钥 byte[] pub_key = keys.getPublic().getEncoded(); String publicKeyString = Base64.encodeBase64String(pub_key); // 获取私钥 byte[] pri_key = keys.getPrivate().getEncoded(); String privateKeyString = Base64.encodeBase64String(pri_key); Map<String, String> keyPairMap = new HashMap<>(); keyPairMap.put(PUBLIC_KEY, publicKeyString); keyPairMap.put(PRIVATE_KEY, privateKeyString); return keyPairMap; } public static String encodeBase64String(byte[] key) { return Base64.encodeBase64String(key); } public static byte[] decodeBase64(String key) { return Base64.decodeBase64(key); } public static String encryptByPubKey(String data, String publicKey) throws Exception { byte[] pubKey = RSAUtil01.decodeBase64(publicKey); byte[] enSign = encryptByPubKey(data.getBytes(), pubKey); return Base64.encodeBase64String(enSign); } public static byte[] encryptByPubKey(byte[] data, byte[] pubKey) throws Exception { X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } public static String encryptByPriKey(String text, String privateKey) { try { byte[] priKey = RSAUtil01.decodeBase64(privateKey); byte[] enSign = encryptByPriKey(text.getBytes(), priKey); return Base64.encodeBase64String(enSign); } catch (Exception e) { throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e); } } public static byte[] encryptByPriKey(byte[] data, byte[] priKey) throws Exception { PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } public static byte[] decryptByPubKey(byte[] data, byte[] pubKey) throws Exception { X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } public static String decryptByPubKey(String data, String publicKey) throws Exception { byte[] pubKey = RSAUtil01.decodeBase64(publicKey); byte[] design = decryptByPubKey(Base64.decodeBase64(data), pubKey); return new String(design); } public static byte[] decryptByPriKey(byte[] data, byte[] priKey) throws Exception { PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } public static String decryptByPriKey(String secretText, String privateKey) { try { byte[] priKey = RSAUtil01.decodeBase64(privateKey); byte[] design = decryptByPriKey(Base64.decodeBase64(secretText), priKey); return new String(design); } catch (Exception e) { throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e); } } public static String sign(byte[] data, byte[] priKey) throws Exception { // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); // 生成私钥 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 实例化Signature Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); // 初始化Signature signature.initSign(privateKey); // 更新 signature.update(data); return Base64.encodeBase64String(signature.sign()); } public boolean verify(byte[] data, byte[] sign, byte[] pubKey) throws Exception { // 实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); // 初始化公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey); // 产生公钥 PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); // 实例化Signature Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); // 初始化Signature signature.initVerify(publicKey); // 更新 signature.update(data); // 验证 return signature.verify(sign); } public static void main(String[] args) { try { Map<String, String> keyMap = initKey(); String publicKeyString = keyMap.get(PUBLIC_KEY); String privateKeyString = keyMap.get(PRIVATE_KEY); System.out.println("公钥:" + publicKeyString); System.out.println("length: " + publicKeyString.length()); System.out.println("私钥:" + privateKeyString); System.out.println("length: " + privateKeyString.length()); // 待加密数据 String data = "admin123"; // 公钥加密 String encrypt = RSAUtil01.encryptByPubKey(data, publicKeyString); // 私钥解密 String decrypt = RSAUtil01.decryptByPriKey(encrypt, privateKeyString); System.out.println("加密前:" + data); System.out.println("明文length:" + data.length()); System.out.println("加密后:" + encrypt); System.out.println("解密后:" + decrypt); } catch (Exception e) { e.printStackTrace(); } }}
如图所示, 生成一对1024 bit 的 RSA 密钥对,并加解密成功。
案例一:报文长度过长加解密失败
测试发现当明文过长时,加密异常,返回如下报错
原因分析: RSA 加解密时,对加密的数据大小有限制,最大不大于密钥长度。
在使用 1024 位的密钥时,最大可以加密 1024/8 = 128字节的数据,此时需要对数据进行分组加密,分组加密后的加密串拼接成一个字符串返回给客户端。如果 Padding 方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用11字节用于填充),则明文长度最多为 128 - 11 = 117 Bytes。
同理,当解密的密文超过128Byte时,也需要进行分组解密
案例二:分段加解密的的实现
实现代码:
public static String encrypt(String plainText, String publicKeyStr) throws Exception { System.out.println("明文lenth为"+plainText.length()); byte[] plainTextArray = plainText.getBytes(); PublicKey publicKey = getPublicKey(publicKeyStr); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int inputLen = plainTextArray.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; int i = 0; byte[] cache; while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(plainTextArray, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(plainTextArray, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptText = out.toByteArray(); out.close(); return Base64.getEncoder().encodeToString(encryptText); } public static String decrypt(String encryptTextHex, String privateKeyStr) throws Exception{ byte[] encryptText = Base64.getDecoder().decode(encryptTextHex); PrivateKey privateKey = getPrivateKey(privateKeyStr); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, privateKey); int inputLen = encryptText.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptText, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] plainText = out.toByteArray(); out.close(); return new String(plainText);}
案例三:当密钥长度非默认的1024,改为2048bit时,如何生成密钥、如何分段
如上文提到, 当密钥对改为 2048 位时, 最大加密明文大小 = 2048(bit) / 8 - 11(byte) = 245 byte
private static final int MAX_ENCRYPT_BLOCK = 245; private static final int MAX_DECRYPT_BLOCK = 256; public static Map<String, Object> initKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); //设置密钥对的bit数 越大越安全 keyPairGen.initialize(2048); KeyPair keyPair = keyPairGen.generateKeyPair(); //获取公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); //获取私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap;}
PKCS#1格式以-----BEGIN RSA PRIVATE KEY-----开头以-----END RSA PRIVATE KEY-----结束PKCS#8格式以-----BEGIN PRIVATE KEY-----开头以-----END PRIVATE KEY-----结束
通常JAVA中需要PKCS8 格式的密钥,在线 RSA 加解密网站:https://www.toolscat.com/decode/rsa 有助于提效。
来源地址:https://blog.csdn.net/qq_41893274/article/details/130120395
--结束END--
本文标题: Java RSA加解密算法学习
本文链接: https://lsjlt.com/news/496042.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0