簡介:
開發中為了數據的安全性使用加密算法對數據進行加密是一種很常見的情況,但就一種語言來說,直接使用提供的相應的庫進行少許封裝是一件很容易的事。但是在一些情況下我們可能需要跨語言來實現,比如前后端分離的 web 開發中,我們需要前端使用 JS 進行加密與解密,后端則使用 Java、PHP等。這個時候由於不再是使用相同的庫,相應的庫中提供的默認參數設置也就存在不一樣的情況,這個時候要做到前端與后端數據的加密解密交換,就不太容易了。本人也是折騰了兩天啃了書,查了許多資料才搞定的。
這里不對原理進行詳細贅述,如果還不了解加密算法細節和原理的可以自行查找,本人博客也提供 AES 與 RSA 簡單原理介紹,你可以查看。如需詳細全面了解,推薦讀《Java 加密與解密藝術》這本書。
相關參考:
RSA 加密算法原理簡述:https://blog.csdn.net/gulang03/article/details/81176133
AES 加密算法的原理詳解:https://blog.csdn.net/gulang03/article/details/81175854
下面的例子是經過測試的,確保了前端與后端都能對相同的數據進行加解密,使用時請盡量與我的環境保持一致。
JDK:1.8.0_201
JS 的相關庫及版本,參考源碼的注釋,由於前端使用了 webpack 打包工具,所以代碼中會存在 ES6 語法,如果您使用的是原生 JS,只需參考其中的方法即可。
AES
前端 JS 實現:
/** * AES 加密算法封裝, * 密鑰長度定位 256 位, * 對外提供密鑰的生成、加密、解密 * 本模塊測試時使用的是:crypto-js@3.1.9-1,uuid@3.3.2 */ let CryptJS = require("crypto-js"); let UUID = require("uuid") export var AESUtil = { /** * AES 加密 * @param _content 待加密內容 * @param _key aesKey, * @param _iv 初始化向量 * @return 返回經 BASE64 處理之后的密文 */ encrypt: function (_content, _key, _iv) { // 先以 UTF-8 編碼解碼參數 返回 any 類型 let content = CryptJS.enc.Utf8.parse(_content); let aesKey = CryptJS.enc.Utf8.parse(_key); let iv = CryptJS.enc.Utf8.parse(_iv); // 加密 let encrypted = CryptJS.AES.encrypt(content, aesKey, { iv: iv, mode: CryptJS.mode.CBC, padding: CryptJS.pad.Pkcs7 }) // console.log(encrypted) return CryptJS.enc.Base64.stringify(encrypted.ciphertext); }, /** * AES 解密 * @param:_content 待解密的內容[Base64處理過的] * @param:解密用的 AES key * @param: 初始化向量 * @return 返回以 UTF-8 處理之后的明文 */ decrypt: function (_content, _key, _iv) { // let content = CryptJS.enc.Base64.parse(_content); // content = CryptJS.enc.Base64.stringify(content); let aesKey = CryptJS.enc.Utf8.parse(_key); let iv = CryptJS.enc.Utf8.parse(_iv); // 解密 let decrypted = CryptJS.AES.decrypt(_content, aesKey, { iv: iv, mode: CryptJS.mode.CBC, padding: CryptJS.pad.Pkcs7 }) // console.log(decrypted) return decrypted.toString(CryptJS.enc.Utf8); }, /** * 獲得 AES 密鑰 * @returns 32 字節的AES密鑰 */ getAesKey: function () { let uuid = UUID.v1(); // console.log(uuid) let aeskey = CryptJS.enc.Utf8.parse(uuid) aeskey = CryptJS.enc.Base64.stringify(aeskey).substring(2, 34) // console.log(aeskey + "\n" + "長度:" + aeskey.length); return aeskey; }, /** * 獲得初始化向量 * @returns 16 字節的初始化向量 */ getIv: function () { let uuid = UUID.v1(); let iv = CryptJS.enc.Utf8.parse(uuid); iv = CryptJS.enc.Base64.stringify(iv).substring(2, 18); // console.log(iv + "\n" + "長度:" + iv.length); return iv; }, /** * 獲得 AES key 及 初始化向量 iv * 其實 iv 和 aesKey 兩者的生成並沒有什么關系,兩者只是對各自的長度有限制, * 這里只是為了方便使用,進行了一個組合返回。 * @return 返回 iv 和 aesKey 的組合 */ getAESKeyAndIv: function () { let aesKeyAndIv = { "iv": this.getIv(), "aesKey": this.getAesKey(), } return aesKeyAndIv; } }
后端 Java 實現:
import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.UUID; /** * AES 加密方法,是對稱的密碼算法(加密與解密的密鑰一致),這里使用最大的 256 位的密鑰, * 對外提供密鑰生成、加密、解密方法 */ public class AESUtil { /** * 獲得一個 密鑰長度為 8*32 = 256 位的 AES 密鑰, * @return 返回經 BASE64 處理之后的密鑰字符串(並截取 32 字節長度) */ public static String getAESStrKey() throws NoSuchAlgorithmException, UnsupportedEncodingException { UUID uuid = UUID.randomUUID(); String aesKey= Base64.getEncoder().encodeToString(uuid.toString().getBytes()).substring(2,34); return aesKey; } /** * 獲得一個初始化向量,初始化向量長度為 4*4 = 16 個字節 * @return 返回經 BASE64 處理之后的密鑰字符串(並截取 16 字節長度) */ public static String getIv(){ UUID uuid = UUID.randomUUID(); String iv = Base64.getEncoder().encodeToString(uuid.toString().getBytes()).substring(2,18); return iv; } /** * 獲得 AES key 及 初始化向量 iv * 其實 iv 和 aesKey 兩者的生成並沒有什么關系,兩者只是對各自的長度有限制, * 這里只是為了方便使用,進行了一個組合返回。 * @return 返回 iv 和 aesKey 的組合 */ public static HashMap<String, String> getAESKeyAndIv() throws UnsupportedEncodingException, NoSuchAlgorithmException { HashMap<String, String> aesKeyAndIv = new HashMap<>(); aesKeyAndIv.put("aesKey", AESUtil.getAESStrKey()); aesKeyAndIv.put("iv", AESUtil.getIv()); return aesKeyAndIv; } /** * 加密 * @param content 待加密內容 * @param secretKeyStr 加密使用的 AES 密鑰,BASE64 編碼后的字符串 * @param iv 初始化向量,長度為 16 個字節,16*8 = 128 位 * @return 加密后的密文,進行 BASE64 處理之后返回 */ public static String encryptAES(byte[] content, String secretKeyStr, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { // 獲得一個 SecretKeySpec // SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKeyStr), "AES"); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyStr.getBytes(), "AES"); // 獲得加密算法實例對象 Cipher Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/補碼方式" // 獲得一個 IvParameterSpec IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes()); // 使用 CBC 模式,需要一個向量 iv, 可增加加密算法的強度 // 根據參數初始化算法 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); // 執行加密並返回經 BASE64 處助理之后的密文 return Base64.getEncoder().encodeToString(cipher.doFinal(content)); } /** * 解密 * @param content: 待解密內容,是 BASE64 編碼后的字節數組 * @param secretKeyStr: 解密使用的 AES 密鑰,BASE64 編碼后的字符串 * @param iv: 初始化向量,長度 16 字節,16*8 = 128 位 * @return 解密后的明文,直接返回經 UTF-8 編碼轉換后的明文 */ public static String decryptAES(byte[] content, String secretKeyStr, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, UnsupportedEncodingException { // 密文進行 BASE64 解密處理 byte[] contentDecByBase64 = Base64.getDecoder().decode(content); // 獲得一個 SecretKeySpec // SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKeyStr), "AES"); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyStr.getBytes(), "AES"); // 獲得加密算法實例對象 Cipher Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/補碼方式" // 獲得一個初始化 IvParameterSpec IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes()); // 根據參數初始化算法 cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // 解密 return new String(cipher.doFinal(contentDecByBase64), "utf8"); } }
AES 測試:
由於 AES 是對稱加密算法,所以只需保證,前端與后端都是使用相同的密鑰和待加密的明文,只要雙方的結果一樣就表示通過測試。這里只測試加密與解密方法,關於 密鑰 及 初始化向量 的生成和轉換的方法本人已經測試過,這里就不再展示。
js:
import { AESUtil } from './AESUtil' // 測試的明文 let content = "abcdefg789+-*+=" let IV = "BjNzhiZDctOGMxOS" // 注意IV必須是 16 個字節 8*16 = 128 位 let aesKey = "I0YmM5NTgtY2IyYi00OWIzLWFkZTktZj" // 由於采用的 AES-256, 所以 Key 為 32 個字節 32*8 = 256 // 加密: let encrypted = AESUtil.encrypt(content, aesKey, IV); // 解密: let decrypted = AESUtil.decrypt(encrypted, aesKey, IV); console.log("密鑰為:" + aesKey) console.log("初始化向量為:" + IV) console.log("密文:" + encrypted) console.log("明文:" + decrypted) console.log("解密之后是否與之前相同:" + (content === decrypted))
Java:
public static void main(String[] args){ String content = "abcdefg789+-*+="; // 待加密的字符串 String aesKey = "I0YmM5NTgtY2IyYi00OWIzLWFkZTktZj"; String iv = "BjNzhiZDctOGMxOS"; try { // 加密 String encrypted = AESUtil.encryptAES(content.getBytes(), aesKey, iv); // 解密 String decrypted = AESUtil.decryptAES(encrypted.getBytes(), aesKey, iv); System.out.println("加密之后的密文為:" + encrypted + "\n" + "解密之后的明文為:" + decrypted + "\n" + "原始明文為:" + content + "\n" + "解密是否成功:" + (content.equals(decrypted))); }catch (Exception e){ e.printStackTrace(); } }
RSA
前端 JS 實現:
/** * RSA 加密算法封裝, * 對外提供密鑰的生成、加密、解密 * 本模塊測試時使用的是:node-rsa@1.0.1 */ let NodeRSA = require("node-rsa"); export var RSAUtil = { /** * 生成 RSA 密鑰對 * @returns 以 PEM 格式返回 公鑰與私鑰 的組合對象 */ getRSAKeyPair: function () { // 生成空對象 let keyPair = new NodeRSA(); keyPair.setOptions({ encryptionScheme:"pkcs1" }) // keyPairObj, 保存經 BASE64 編碼處理之后 PEM 格式的 RSA 密鑰對 let keyPairObj = { publicKey: '', privateKey: '' }; // keysize: 2048; 公指數為:65537 keyPair.generateKeyPair(2048, 65537); /** * 導出密鑰,對輸出的密鑰做一些格式化處理,以便 Java 端能直接使用,算然經過處理但是並不影響 JS 端的密鑰導入,及正確性。 * 1. 公鑰 * 2. 私鑰 */ keyPairObj.publicKey = keyPair.exportKey("pkcs8-public-pem").replace(/-----BEGIN PUBLIC KEY-----/, '').replace(/-----END PUBLIC KEY-----/, '').replace(/\n/g, ''); keyPairObj.privateKey = keyPair.exportKey("pkcs8-private-pem").replace(/-----BEGIN PRIVATE KEY-----/, '').replace(/-----END PRIVATE KEY-----/, '').replace(/\n/g, ''); return keyPairObj; }, /** * 公鑰加密,加密之后以 BASE64 形式編碼 * @param buffer : 待加密內容 編碼格式:utf-8 * @param publicKey: 加密使用的公鑰, 格式是:pkcs8 pem * @param encoding: 加密之后的輸出編碼類型 默認輸出編碼格式是:base64 * @param source_encoding: 指定代加密內容的編碼方式,默認是:utf-8 * @returns: 返回以 BASE64 處理之后的加密內容 */ publicKeyEncrypt: function(buffer, pubicKey, encoding = "base64", source_encoding = "utf8"){ // 導入 publickey let key = new NodeRSA(); key.setOptions({ encryptionScheme:"pkcs1", // 默認是:pkcs1_oaep,Java 端默認是 pkcs1, 這里做個修改 }) key.importKey(pubicKey, "pkcs8-public-pem"); // 加密並返回加密結果 return key.encrypt(buffer, encoding, source_encoding); }, /** * 私鑰解密,解密之后 返回 utf8編碼的字符串 * @param buffer: Buffer object or base64 encoded string * @param privateKey: 解密用的私鑰,格式是:pkcs8 pem * @param encoding: 加密之后的類型 buffer OR json, 默認是 buffer * @returns:默認返回值類型就是 encoding 的默認值,即 buffer */ privateKeyDecrypt: function(buffer, privateKey, encoding = "buffer"){ // 導入 privatekey let key = new NodeRSA(); key.setOptions({ encryptionScheme: "pkcs1", // 默認是:pkcs1_oaep,Java 端默認是 pkcs1, 這里做個修改 }) key.importKey(privateKey, "pkcs8-private-pem"); // 解密 return key.decrypt(buffer, encoding); } }
后端 Java 實現:
import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * RSA 是非對稱的密碼算法,密鑰分公鑰和私鑰, 可以使用一方加密另一方解密,不過由於私鑰長度往往很長, * 考慮到對網絡資源的消耗,一般就公開公鑰,使用公鑰加密,私鑰進行解密,所以這里只提供這種模式需要 * 的方法。 * 對外提供密鑰對生成、密鑰轉換、公鑰加密、私鑰解密方法 */ public class RSAUtil { /** * 生成密鑰對:密鑰對中包含公鑰和私鑰 * @return 包含 RSA 公鑰與私鑰的 keyPair * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException */ public static KeyPair getKeyPair() throws NoSuchAlgorithmException, UnsupportedEncodingException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); // 獲得RSA密鑰對的生成器實例 SecureRandom secureRandom = new SecureRandom(String.valueOf(System.currentTimeMillis()).getBytes("utf-8")); // 說的一個安全的隨機數 keyPairGenerator.initialize(2048, secureRandom); // 這里可以是1024、2048 初始化一個密鑰對 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 獲得密鑰對 return keyPair; } /** * 獲取公鑰 (並進行 Base64 編碼,返回一個 Base64 編碼后的字符串) * @param keyPair:RSA 密鑰對 * @return 返回一個 Base64 編碼后的公鑰字符串 */ public static String getPublicKey(KeyPair keyPair){ PublicKey publicKey = keyPair.getPublic(); byte[] bytes = publicKey.getEncoded(); return Base64.getEncoder().encodeToString(bytes); } /** * 獲取私鑰(並進行Base64編碼,返回一個 Base64 編碼后的字符串) * @param keyPair:RSA 密鑰對 * @return 返回一個 Base64 編碼后的私鑰字符串 */ public static String getPrivateKey(KeyPair keyPair){ PrivateKey privateKey = keyPair.getPrivate(); byte[] bytes = privateKey.getEncoded(); return Base64.getEncoder().encodeToString(bytes); } /** * 將 Base64 編碼后的公鑰轉換成 PublicKey 對象 * @param pubStr:Base64 編碼后的公鑰字符串 * @return PublicKey */ public static PublicKey string2PublicKey(String pubStr) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = Base64.getDecoder().decode(pubStr); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; } /** * 將 Base64 碼后的私鑰轉換成 PrivateKey 對象 * @param priStr:Base64 編碼后的私鑰字符串 * @return PrivateKey */ public static PrivateKey string2Privatekey(String priStr) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = Base64.getDecoder().decode(priStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; } /** * 公鑰加密 * @param content 待加密的內容 byte[] * @param publicKey 加密所需的公鑰對象 PublicKey * @return 加密后的字節數組 byte[] */ public static byte[] publicEncrytype(byte[] content, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(content); return bytes; } /** * 私鑰解密 * @param content 待解密的內容 byte[],這里要注意,由於我們中間過程用的都是 BASE64 ,所以在傳入參數前應先進行 BASE64 解析 * @param privateKey 解密需要的私鑰對象 PrivateKey * @return 解密后的字節數組 byte[],這里是元數據,需要根據情況自行轉碼 */ public static byte[] privateDecrypt(byte[] content, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] bytes = cipher.doFinal(content); return bytes; } }
RSA 測試:
RSA 加密算法是非對稱加密算法,密鑰匙成對出現的,可以使用一方加密另一方解密,不過由於四幺長度往往很長,考慮到對網絡資源的消耗,一般就公開公鑰,使用公鑰進行加密,私鑰進行解密,所以上述封裝都是僅提供這種模式的封裝。這里還要特別注意的一點時,RSA 在加密的時候即使密鑰不變,每一次加密相同的數據之后的密文結果也是不一樣的。如下圖:
鑒於RSA的這些特殊性,測試的時候,除了要保證密鑰對相同之外,還應采用一端( 例如:JS 使用 publicKey 加密數據 )加另一端( Java 使用 privateKey 解密數據 )解密,只要保證解密之后的結果是對的就 OK 了。鑒於這種思路進行如下測試:
JS:
import {RSAUtil} from "./RSAUtil" // RSA 測試 { let content = "abcdefg456789+-" // 生成密鑰對 // let keyPair = RSAUtil.getRSAKeyPair(); let keyPair = {}; keyPair.publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs02q0YdzYndJXqvr+DiFwfeQ+VSXSz3yj6vlCmvcorkKs0qIchDNd20/xY12a8hh2p8HkAt8lTL0qpC+dDahMJk8OIoeSltAHPwneUMu6EdeG5F5HNBQfuzFcFDEjZFI2mdUMsSFZqyw5HlNGF12YPNbOrR5FTiRcTRUgzlvcXM1gDDwbxpZY3rNZpoXvIwpsKMrlB+DTkn9802Qwrs07u+UCaCCqxnAQGmCiGwKbha/jQTa/1Y5aTtC9Zn+RPjvjZ+M03GZfin3u0rhLRGNJfcRsDd5zcdsZwsf8fId+TpcuHOOUvvkcLT/WEL5I5TQV0o2AR41BNW2cGOxFL+0xwIDAQAB" keyPair.privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzTarRh3Nid0leq+v4OIXB95D5VJdLPfKPq+UKa9yiuQqzSohyEM13bT/FjXZryGHanweQC3yVMvSqkL50NqEwmTw4ih5KW0Ac/Cd5Qy7oR14bkXkc0FB+7MVwUMSNkUjaZ1QyxIVmrLDkeU0YXXZg81s6tHkVOJFxNFSDOW9xczWAMPBvGlljes1mmhe8jCmwoyuUH4NOSf3zTZDCuzTu75QJoIKrGcBAaYKIbApuFr+NBNr/VjlpO0L1mf5E+O+Nn4zTcZl+Kfe7SuEtEY0l9xGwN3nNx2xnCx/x8h35Oly4c45S++RwtP9YQvkjlNBXSjYBHjUE1bZwY7EUv7THAgMBAAECggEAQHjD3DV1Ism7owP0hDtmtRkcktp80DxFFK39XGLuYcBhfZhmOYWbK78nuBQmqZjSvraCFKRctpUs7ou/P7BJA12GDtpzC8+F3SY511t16WWIDCehwd+RoiHm2HziP/kmlgmjd+G8CfA8ZtrLAuDQaQn4GsK76wp9GZR0cv7a+JKW0LyaTDidTx8fYGsKHV4odps/erR08SBBs5rUETRXcJpGktOUe7k7YlNHCJf+Y1W5yKKehWvTh71veayhjBzxv9RqR0fVgnJDUMVEPATaTj1pVKyZgA/9oia9m8yXAnCVR6GutH0P1FisFijnAN1CpVbHeKhUy1KLZeemGG0zgQKBgQDqQRg9HBwfAHbzWE2NLRcCPIl5UOimYFeRRSBY+7DfDZFCu/jfmgh4mhrlJzDkny7dAJGtfSSM+ozppahuDB6uZjWHrbKZu2WG0qQwWojDAKsfY/TbsWlFm+TLhfALsFnSlVbTpUWqcG6/B/Jdd07ieccGFwTfLNCFdH6b64vDBwKBgQDD8rMHKQcvTEXdQTZGqJ1crTwqXuU3XMOau4h27VR7snDe4xpwl1rsP5W5SiraP5/9EtPwGKwjnEPUSWRHvj2wtBs/SB4A1VBdzKY+yM98eFzSf9HJZRra42SlU/Fw/jHKkRAUkp7gluc5DwZi7JfuxXS1RVgfMWbsTLKT8udQQQKBgQDlPUqBEu8aD5RYU0OhMkzf7WoDBICHwKQxD1q2eaf+wAI1Mko8VzqO+w/yzEV2laiAsbvd8SdBpzcatvh6qPWlaXRdEEhFVTPnml7+yronSpIrp9/I1nbUndhqquncJnngMDDF8WiZgGmAHEC74rOZwd5YQVKNLAfrcbMs1nbxJQKBgQC3SfOq8/bTiF4lq5VQnPKtuSH5ZFC265/QwjDRRgjruCuaYgbeYMXdDVFJRBY3lqJaAN2czgdfPBG6pngWH97mxmJiXFwsXVzSkNbFDeP/wzrYcFXVNCzdqS0A9Td4gV4j5HONOuVAogdhuSs5J6Sq5arY0Sev7e8fhFLaz7ENwQKBgQDjRRfpDUqxeZNjjx0Huu5EFe776ATLxhXHJT7xT7+NXKQK+SwzGMnYPK6fHlkoCOACIX0yqm2CqFNv/XT2rYJ2mFsXs4uDYV7QSDfqJ4fMyUQxsYzqQH/qBi2gP630+il7UmIA7Uddsfl6Wx8ugICX2frUTLNu6/B0a0hFiO++bw==" let encrypted = RSAUtil.publicKeyEncrypt(content, keyPair.publicKey); let dencrypted = RSAUtil.privateKeyDecrypt(encrypted, keyPair.privateKey); console.dir("密鑰對:" + "\n\t" + "公鑰:" + keyPair.publicKey + "\n\t" + "私鑰:" + keyPair.privateKey ) console.log("加密之后的密文(經 BASE64 處理):" + encrypted) console.log("解密之后的明文:" + dencrypted.toString('utf8') + "\n" + "原始明文:" + content + "\n" + "測試是否通過:" + ( dencrypted.toString('utf8') === content ) ) }
Java:
取上面紅圈內的內容到 Java 測試代碼中,使用相同的 密鑰對 其進行解密,解密成功就表示測試通過。
public static void main(String[] args){ // 待加密內容 String content = "abcdefg456789+-"; // 經 BASE64 處理之后的公鑰和私鑰 String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs02q0YdzYndJXqvr+DiFwfeQ+VSXSz3yj6vlCmvcorkKs0qIchDNd20/xY12a8hh2p8HkAt8lTL0qpC+dDahMJk8OIoeSltAHPwneUMu6EdeG5F5HNBQfuzFcFDEjZFI2mdUMsSFZqyw5HlNGF12YPNbOrR5FTiRcTRUgzlvcXM1gDDwbxpZY3rNZpoXvIwpsKMrlB+DTkn9802Qwrs07u+UCaCCqxnAQGmCiGwKbha/jQTa/1Y5aTtC9Zn+RPjvjZ+M03GZfin3u0rhLRGNJfcRsDd5zcdsZwsf8fId+TpcuHOOUvvkcLT/WEL5I5TQV0o2AR41BNW2cGOxFL+0xwIDAQAB"; String privateKeyStr = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzTarRh3Nid0leq+v4OIXB95D5VJdLPfKPq+UKa9yiuQqzSohyEM13bT/FjXZryGHanweQC3yVMvSqkL50NqEwmTw4ih5KW0Ac/Cd5Qy7oR14bkXkc0FB+7MVwUMSNkUjaZ1QyxIVmrLDkeU0YXXZg81s6tHkVOJFxNFSDOW9xczWAMPBvGlljes1mmhe8jCmwoyuUH4NOSf3zTZDCuzTu75QJoIKrGcBAaYKIbApuFr+NBNr/VjlpO0L1mf5E+O+Nn4zTcZl+Kfe7SuEtEY0l9xGwN3nNx2xnCx/x8h35Oly4c45S++RwtP9YQvkjlNBXSjYBHjUE1bZwY7EUv7THAgMBAAECggEAQHjD3DV1Ism7owP0hDtmtRkcktp80DxFFK39XGLuYcBhfZhmOYWbK78nuBQmqZjSvraCFKRctpUs7ou/P7BJA12GDtpzC8+F3SY511t16WWIDCehwd+RoiHm2HziP/kmlgmjd+G8CfA8ZtrLAuDQaQn4GsK76wp9GZR0cv7a+JKW0LyaTDidTx8fYGsKHV4odps/erR08SBBs5rUETRXcJpGktOUe7k7YlNHCJf+Y1W5yKKehWvTh71veayhjBzxv9RqR0fVgnJDUMVEPATaTj1pVKyZgA/9oia9m8yXAnCVR6GutH0P1FisFijnAN1CpVbHeKhUy1KLZeemGG0zgQKBgQDqQRg9HBwfAHbzWE2NLRcCPIl5UOimYFeRRSBY+7DfDZFCu/jfmgh4mhrlJzDkny7dAJGtfSSM+ozppahuDB6uZjWHrbKZu2WG0qQwWojDAKsfY/TbsWlFm+TLhfALsFnSlVbTpUWqcG6/B/Jdd07ieccGFwTfLNCFdH6b64vDBwKBgQDD8rMHKQcvTEXdQTZGqJ1crTwqXuU3XMOau4h27VR7snDe4xpwl1rsP5W5SiraP5/9EtPwGKwjnEPUSWRHvj2wtBs/SB4A1VBdzKY+yM98eFzSf9HJZRra42SlU/Fw/jHKkRAUkp7gluc5DwZi7JfuxXS1RVgfMWbsTLKT8udQQQKBgQDlPUqBEu8aD5RYU0OhMkzf7WoDBICHwKQxD1q2eaf+wAI1Mko8VzqO+w/yzEV2laiAsbvd8SdBpzcatvh6qPWlaXRdEEhFVTPnml7+yronSpIrp9/I1nbUndhqquncJnngMDDF8WiZgGmAHEC74rOZwd5YQVKNLAfrcbMs1nbxJQKBgQC3SfOq8/bTiF4lq5VQnPKtuSH5ZFC265/QwjDRRgjruCuaYgbeYMXdDVFJRBY3lqJaAN2czgdfPBG6pngWH97mxmJiXFwsXVzSkNbFDeP/wzrYcFXVNCzdqS0A9Td4gV4j5HONOuVAogdhuSs5J6Sq5arY0Sev7e8fhFLaz7ENwQKBgQDjRRfpDUqxeZNjjx0Huu5EFe776ATLxhXHJT7xT7+NXKQK+SwzGMnYPK6fHlkoCOACIX0yqm2CqFNv/XT2rYJ2mFsXs4uDYV7QSDfqJ4fMyUQxsYzqQH/qBi2gP630+il7UmIA7Uddsfl6Wx8ugICX2frUTLNu6/B0a0hFiO++bw=="; try{ PrivateKey privateKey = RSAUtil.string2Privatekey(privateKeyStr); PublicKey publicKey = RSAUtil.string2PublicKey(publicKeyStr); // 加密 // byte[] encrypteds = RSAUtil.publicEncrytype(content.getBytes(), publicKey); // String encryptedBase64Str = Base64.getEncoder().encodeToString(encrypteds); String encryptedBase64Str = "MPgbiM3u3ZnY0X9ilqO6+hzlxXEEKYGDMdxbZMQOXAMN149WBK/i1AB8/TnSmbclcqC6oy/Xe3UnqxWJmiybTz8hXdAdZJ6HmWWN8LXajv+1ElJJr+ZKDd3kg/cj39UKmwSmCEccX8Ntaj8BYXPxvzZfAuRqp9xjQ5SQtvcxJu4E4dZ1ecnvWvWiYI9qnJWLjMUhPaqWwJlX1fS4dKTp8GCJ+ZMdh6+oU9090famP0l4RN8nxnIiJPEKX3Z/si5Ad2MjoeFyZKjyuHihyGGZ5Ilpvb4DZzqcv55uRKvr0D4XZDcZN9eumx+rcqUGtV2RsIpBsqleqNJ4y1Z0jk5YMA=="; byte[] encrypteds = Base64.getDecoder().decode(encryptedBase64Str); // 解密 byte[] dencrypteds = RSAUtil.privateDecrypt(encrypteds, privateKey); // byte[] dencrypteds = RSAUtil.privateDecrypt(Base64.getDecoder().decode(encryptedBase64Str), privateKey); String dencrypted = new String(dencrypteds, "utf8"); // 信息打印 System.out.println( "公鑰為(BASE64 處理之后):" + publicKeyStr + "\n" + "私鑰為(BASE64 處理之后):" + privateKeyStr + "\n" + "公鑰加密密文(BASE64 處理之后):" + encryptedBase64Str + "\n" + "私鑰解密之后(UTF-8 處理之后):" + dencrypted + "\n" + "Java平台測試是否通過:" + (dencrypted.equals(content)) ); RSAUtil.getPublicKey(RSAUtil.getKeyPair()); }catch (Exception e){ e.printStackTrace(); } }
————————————————
版權聲明:本文為CSDN博主「「已注銷」」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/gulang03/article/details/82230408