RSA 加密、解密、簽名、驗籤、摘要,前後端java+vue聯調測試通過
直接上程式碼
// 注意:加密密文與簽名都是唯一的,不會變化。
// 注意:vue 端金鑰都要帶pem格式。java 不要帶pem格式
// 注意:vue端及java端函式引數、返回值要求是什麼型別及何進位制。搞明白哪裡用base64,哪裡2進位制,哪裡16進位制。
// 重點還是要了解點原理,比如sha1withrsa,先經過sha1演算法,知道aaa,雜湊後的密文16進位制是:7e240de74fb1ed08fa08d38063f6a6a91462a815,對比自己的程式有沒有算錯。
// 利用一些線上測試工具幫忙驗證自己的程式過程。http://www.metools.info/code/c82.html ; 同時知道如何查引入的類庫各api的官網,瞭解如何使用各函式。
// 不然遇到莫名奇妙的錯誤無從解決。報錯的地方不一定是程式實際錯誤的地方。瞭解查錯的方式有:打莊、debug.
伺服器端java:
工具類,rsa加解密,RsaUtil.java
1 package com.ruoyi.common.utils.enDeCrypt; 2 3 import com.ruoyi.common.core.text.Convert; 4 import com.ruoyi.common.exception.UtilException; 5 6 import java.io.ByteArrayOutputStream; 7 import java.io.UnsupportedEncodingException; 8 import java.security.*; 9 import java.util.Base64; 10 import javax.crypto.Cipher; 11 import java.security.interfaces.RSAPrivateKey; 12 import java.security.interfaces.RSAPublicKey; 13 import java.security.spec.PKCS8EncodedKeySpec; 14 import java.security.spec.X509EncodedKeySpec; 15 import java.util.HashMap; 16 import java.util.Map; 17 18 import static io.netty.util.CharsetUtil.UTF_8; 19 20 21 /** 加密和解密花費時間長、速度慢,故使用RSA只能加密少量資料,大量的資料加密還要靠對稱密碼演算法。 22 * 第一種用法是公鑰加密、私鑰解密——用於加密;第二種是私鑰簽名、公鑰驗籤——用於簽名 23 * @author EvianZou 24 */ 25 public class RsaUtil { 26 27 /** 28 * 金鑰長度與原文長度對應,越長速度越慢 29 * 1024bit 金鑰 能加密明文最大的長度是 1024/8 -11 = 117 byte 30 * 2048bit 金鑰 能加密明文最大的長度是 2048/8 -11 = 245 byte 31 */ 32 private final static int KEY_SIZE = 1024; 33 /* 34 * RSA演算法 RSA-1024位 RSA2-2048位 35 */ 36 private final static String ALGORITHM = "RSA"; 37 //java預設"RSA"="RSA/ECB/PKCS1Padding" 38 private final static String PADDING_MODE = "RSA"; 39 40 /* 41 * 簽名演算法 42 * RSA: 常用 SHA1WithRSA,有 MD2withRSA、MD5withRSA、SHA1withRSA; 43 * RSA2: 常用:SHA256WithRSA,有 SHA224withRSA、SHA256withRSA、SHA384withRSA、SHA512withRSA 、RIPEMD128withRSA、RIPEMD160withRSA; 44 */ 45 public static final String SIGN_ALGORITHM = "SHA1withRSA"; 46 47 /* 48 * 字元編碼 49 */ 50 private final static String ENCODING = "UTF-8"; 51 52 /* 53 * RSA最大加密明文大小 54 * RSA:117 55 * RSA2:245 56 */ 57 private static final int MAX_ENCRYPT_BLOCK = 117; 58 59 /* 60 * RSA最大解密密文大小 61 * RSA:128 62 * RSA2:256 63 */ 64 private static final int MAX_DECRYPT_BLOCK = 128; 65 66 public Map<String, String> getKeyMap() { 67 return keyMap; 68 } 69 70 public void setKeyMap(Map<String, String> keyMap) { 71 this.keyMap = keyMap; 72 } 73 74 /** 75 * 用於封裝隨機產生的公鑰與私鑰 76 */ 77 private Map<String, String> keyMap; 78 79 80 public RsaUtil(boolean isKeyBase64) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchProviderException { 81 setKeyMap(generateKeyPair(isKeyBase64)); 82 } 83 84 /** 85 * 隨機生成金鑰對 86 * @param isKeyBase64 金鑰base64加密為true,hex為false 87 */ 88 private Map<String, String> generateKeyPair(boolean isKeyBase64) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchProviderException { 89 // KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA演算法生成物件 90 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM); 91 // 初始化金鑰對生成器 92 keyPairGen.initialize(KEY_SIZE, new SecureRandom()); 93 // 生成一個金鑰對,儲存在keyPair中 94 KeyPair keyPair = keyPairGen.generateKeyPair(); 95 // 得到私鑰 96 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); 97 // 得到公鑰 98 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); 99 String publicKeyString = isKeyBase64 ? Base64.getEncoder().encodeToString(publicKey.getEncoded()) : Convert.bytesToHex(publicKey.getEncoded()); 100 101 // 得到私鑰字串 102 String privateKeyString = isKeyBase64 ? Base64.getEncoder().encodeToString(privateKey.getEncoded()) : Convert.bytesToHex(privateKey.getEncoded()); 103 // 將公鑰和私鑰儲存到Map 104 Map<String, String> map = new HashMap<>(32); 105 //表示公鑰 106 map.put("publicKey", publicKeyString); 107 //表示私鑰 108 map.put("privateKey", privateKeyString); 109 110 return map; 111 } 112 113 /** 114 * 流程1:RSA公鑰加密,再BASE64加密,再base64解密,再RSA私鑰解密 115 * 流程2:RSA私鑰加密,再BASE64加密,再base64解密,再RSA公鑰解密 116 * 117 * @param text 加密字串/解密字串 118 * @param secretKey 加密祕鑰 119 * @param isPublicKey 公鑰/私鑰 120 * @param isEncrypt 加密/解密 121 * @param isKeyBase64 金鑰base64加密為true,hex為false 122 * @param isTextBase64 密文base64加密為true,hex為false 123 * @param isUrlCode 密文urlencode為true,否則為false 124 * @return 密文/明文 125 * @throws Exception 加密過程中的異常資訊 126 */ 127 private String enDecrypt(String text, String secretKey, boolean isPublicKey, boolean isEncrypt, boolean isKeyBase64, boolean isTextBase64, boolean isUrlCode) throws Exception { 128 //base64編碼的祕鑰 129 byte[] decodedKey = isKeyBase64 ? Base64.getDecoder().decode(secretKey.getBytes(ENCODING)) : Convert.hexToByteArray(secretKey); 130 byte[] decodeText; 131 int encodeMode = 0; 132 int max_block = 0; 133 int textLength = 0; 134 Cipher cipher = Cipher.getInstance(PADDING_MODE); 135 UrlCode urlCode = new UrlCode(); 136 137 138 if (isEncrypt) { 139 encodeMode = Cipher.ENCRYPT_MODE; 140 max_block = MAX_ENCRYPT_BLOCK; 141 //待加密字串 142 decodeText = text.getBytes(ENCODING); 143 textLength = text.getBytes(ENCODING).length; 144 } else { 145 encodeMode = Cipher.DECRYPT_MODE; 146 max_block = MAX_DECRYPT_BLOCK; 147 decodeText = isUrlCode ? urlCode.decodeURL(text, UTF_8).getBytes(ENCODING) : text.getBytes(ENCODING); 148 // 注意:byte[]轉String,不能 byte[].toString(); 可用new String(byte[]) 149 decodeText = isTextBase64 ? Base64.getDecoder().decode(decodeText) : Convert.hexToByteArray(new String(decodeText, ENCODING)); 150 textLength = decodeText.length; 151 } 152 153 if (isPublicKey) { 154 cipher.init(encodeMode, (RSAPublicKey) KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(decodedKey))); 155 } else { 156 cipher.init(encodeMode, (RSAPrivateKey) KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decodedKey))); 157 } 158 159 ByteArrayOutputStream out = new ByteArrayOutputStream(); 160 int offSet = 0; 161 byte[] cache; 162 int i = 0; 163 // 對資料分段加密 164 while (textLength - offSet > 0) { 165 if (textLength - offSet > max_block) { 166 cache = cipher.doFinal(decodeText, offSet, max_block); 167 } else { 168 cache = cipher.doFinal(decodeText, offSet, textLength - offSet); 169 } 170 out.write(cache, 0, cache.length); 171 i++; 172 offSet = i * max_block; 173 } 174 byte[] byteText = out.toByteArray(); 175 out.close(); 176 177 String resultText = out.toString(); 178 if (isEncrypt) { 179 resultText = isTextBase64 ? Base64.getEncoder().encodeToString(byteText) : Convert.bytesToHex(byteText); 180 resultText = isUrlCode ? urlCode.encodeURL(resultText, UTF_8) : resultText; 181 } 182 183 return resultText; 184 } 185 186 public String encryptByPublicKey(String plainText, String publicKey, boolean isKeyBase64, boolean isTextBase64, boolean isUrlCode) throws Exception { 187 return enDecrypt(plainText, publicKey, true, true, isKeyBase64, isTextBase64, isUrlCode); 188 } 189 190 public String decryptByPrivateKey(String cipherText, String privateKey, boolean isKeyBase64, boolean isTextBase64, boolean isUrlCode) throws Exception { 191 return enDecrypt(cipherText, privateKey, false, false, isKeyBase64, isTextBase64, isUrlCode); 192 } 193 194 /** 195 * RSA簽名 196 * 197 * @param signText 待簽名資料 198 * @param privateKey 私鑰 199 * @param isKeyBase64 金鑰base64加密為true,hex為false 200 * @param isTextBase64 密文base64加密為true,hex為false 201 * @param isUrlCode 密文urlencode為true,否則為false 202 * @return 簽名值 203 */ 204 public String signByPrivateKey(String signText, String privateKey, boolean isKeyBase64, boolean isTextBase64, boolean isUrlCode) throws Exception { 205 try { 206 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); 207 byte[] decodeKey = isKeyBase64 ? Base64.getDecoder().decode(privateKey) : Convert.hexToByteArray(privateKey); 208 209 PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decodeKey); 210 //DER input, Integer tag error ,原因金鑰錯誤 211 PrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec); 212 Signature signature = Signature.getInstance(SIGN_ALGORITHM); 213 signature.initSign(private_key); 214 215 String sign_text = isTextBase64?Convert.bytesToHexString(Base64.getDecoder().decode(signText.getBytes(ENCODING))):signText; 216 // System.out.println("伺服器端:待簽名資料:"+sign_text); 217 218 assert sign_text != null; 219 byte[] digestText = genDigest(sign_text,SIGN_ALGORITHM ); 220 221 // 生成訊息摘要 因客戶端簽名時引數為16進位制, 可以通過加密演算法驗證是否正確 222 signature.update(Convert.bytesToHexString(digestText).getBytes(ENCODING)); 223 // System.out.println("伺服器端:摘要資料:"+new String(digestText)); 224 225 byte[] signedByte = signature.sign(); 226 // System.out.println("伺服器端:簽名資料:"+Convert.bytesToHexString(signedByte)); 227 228 UrlCode urlCode = new UrlCode(); 229 230 String signedText = isTextBase64 ? Base64.getEncoder().encodeToString(signedByte) : Convert.bytesToHex(signedByte); 231 signedText = isUrlCode ? urlCode.encodeURL(signedText, UTF_8) : signedText; 232 233 return signedText; 234 235 } catch (Exception e) { 236 throw new UtilException(e.getMessage()); 237 } 238 } 239 240 241 /* 242 * 生成訊息摘要 243 */ 244 public byte[] genDigest(String plainText, String algorithm) throws NoSuchAlgorithmException, UnsupportedEncodingException { 245 246 MessageDigest messageDigest = MessageDigest.getInstance(algorithm.split("w")[0]); 247 // System.out.println("伺服器端,雜湊演算法:"+algorithm.split("w")[0]); 248 byte [] digestText = messageDigest.digest(plainText.getBytes(ENCODING)); 249 // System.out.println("伺服器端,摘要:"+bytesToHexString(digestText)); 250 return digestText; 251 } 252 253 /** 254 * RSA驗簽名檢查 255 * 256 * @param signText 待簽名資料 257 * @param signedText 已簽名資料 258 * @param publicKey 公鑰 259 * @param isKeyBase64 金鑰base64加密為true,hex為false 260 * @param isTextBase64 密文base64加密為true,hex為false 261 * @param isUrlCode 密文urlencode為true,否則為false 262 * @return 布林值 263 */ 264 public boolean verifySignPublicKey(String signText, String signedText, String publicKey, boolean isKeyBase64, boolean isTextBase64, boolean isUrlCode) throws Exception { 265 boolean isVerified; 266 try { 267 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); 268 byte[] decodedKey = isKeyBase64 ? Base64.getDecoder().decode(publicKey) : Convert.hexToByteArray(publicKey); 269 PublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); 270 Signature signature = Signature.getInstance(SIGN_ALGORITHM); 271 272 signature.initVerify(pubKey); 273 274 signText = isTextBase64?Convert.bytesToHexString( Base64.getDecoder().decode(signText.getBytes(ENCODING))) :signText; 275 // 生成訊息摘要 276 assert signText != null; 277 // System.out.println("驗證,待簽名資料:"+signText); 278 279 byte[] digestText = genDigest(signText,SIGN_ALGORITHM ); 280 // 生成訊息摘要 281 signature.update(Convert.bytesToHexString(digestText).getBytes(ENCODING)); 282 283 UrlCode urlCode = new UrlCode(); 284 String decodeText = isUrlCode ? urlCode.decodeURL(signedText, UTF_8) : signedText; 285 byte[] bytesText = isTextBase64 ? Base64.getDecoder().decode(decodeText) : Convert.hexToByteArray(decodeText); 286 287 //DER input, Integer tag error ,或 DerInputStream.getLength(): lengthTag=111, too big.原因金鑰錯誤 288 isVerified = signature.verify(bytesText); 289 290 } catch (Exception e) { 291 throw new UtilException(e.getMessage()); 292 } 293 return isVerified; 294 } 295 296 297 // PKCS#1 與PKCS#8 格式轉換 298 // 帶頭和尾的金鑰,去掉頭和尾後,再按指定格式增加頭和尾 299 public Map<String,String> convertKeyFormat(Map<String,String> mapSource,String keyMode) 300 { 301 Map<String,String> map = removeHeaderAndBottom(mapSource,keyMode); 302 map = formatKey (map,keyMode); 303 304 return map; 305 } 306 307 public Map<String,String> removeHeaderAndBottom (Map<String,String> mapSource,String keyMode) 308 { 309 Map<String,String> map = new HashMap<>(); 310 Map<String,String> mapHeaderAndBottom_publicKey = genKeyHeaderAndBottom(keyMode,false); 311 Map<String,String> mapHeaderAndBottom_privateKey = genKeyHeaderAndBottom(keyMode,true); 312 313 String privateKey = mapSource.get("privateKey"); 314 String publicKey = mapSource.get("publicKey"); 315 316 publicKey = publicKey.replaceAll(mapHeaderAndBottom_publicKey.get("header"),""); 317 publicKey = publicKey.replaceAll(mapHeaderAndBottom_publicKey.get("bottom"),""); 318 319 privateKey=privateKey.replaceAll(mapHeaderAndBottom_privateKey.get("header"),""); 320 privateKey= privateKey.replaceAll(mapHeaderAndBottom_privateKey.get("bottom"),""); 321 322 map.put("publicKey",publicKey); 323 map.put("privateKey",privateKey); 324 325 return map; 326 } 327 328 // 不帶頭和尾的金鑰,按指定格式增加頭和尾 329 public Map<String,String> formatKey( Map<String,String> mapSource, String keyMode) 330 { 331 Map<String,String> map = new HashMap<>(); 332 333 map.put("publicKey", formatKey(mapSource.get("publicKey"),keyMode,false)); 334 map.put("privateKey", formatKey(mapSource.get("privateKey"),keyMode,true)); 335 336 return map; 337 } 338 339 // 格式化key,增加頭和尾 340 private String formatKey( String keyBody, String keyMode,boolean isPrivateKey ) 341 { 342 Map<String,String> map = genKeyHeaderAndBottom(keyMode,isPrivateKey); 343 String keyHeader = map.get("header"); 344 String keyBottom =map.get("bottom"); 345 String strKey = keyHeader + "\n"; 346 347 int nPrivateKeyLen = keyBody.length(); 348 char[] status = keyBody.toCharArray(); 349 for(int i = 64; i < nPrivateKeyLen; i+=64) 350 { 351 if(status[i] != '\n') 352 { 353 status[i]= '\n'; 354 } 355 i++; 356 } 357 strKey += String.valueOf(status); 358 strKey += "\n"; 359 strKey += keyBottom; 360 strKey += "\n"; 361 362 return strKey; 363 } 364 365 // 按指定格式生成頭和尾 366 public Map<String,String> genKeyHeaderAndBottom(String keyMode,boolean isPrivateKey) 367 { 368 Map<String,String> map = new HashMap<>(); 369 if (keyMode == "PKCS#1") { 370 if (isPrivateKey) { 371 map.put("header", "-----BEGIN RSA PRIVATE KEY-----"); 372 } else { 373 map.put("header", "-----BEGIN RSA PUBLIC KEY-----"); 374 } 375 if (isPrivateKey) { 376 map.put("bottom", "-----END RSA PRIVATE KEY-----"); 377 } else { 378 map.put("bottom", "-----END RSA PUBLIC KEY-----"); 379 } 380 } 381 else if (keyMode == "PKCS#8") { 382 if (isPrivateKey) { 383 map.put("header", "-----BEGIN PRIVATE KEY-----"); 384 } else { 385 map.put("header", "-----BEGIN PUBLIC KEY-----"); 386 } 387 if (isPrivateKey) { 388 map.put("bottom", "-----END PRIVATE KEY-----"); 389 } else { 390 map.put("bottom", "-----END PUBLIC KEY-----"); 391 } 392 } 393 return map; 394 } 395 }
測試使用流程方法
1 public static void testRSAflow() throws Exception { 2 3 // 測試前後端使用流程 4 // 1、後端生成金鑰對,公鑰分享給前端,前端獲取到伺服器給的公鑰。 5 // 2、前端用伺服器端給的公鑰 及演算法:RSA/ECB/PKCS1Padding 加密"xhy 我愛你 中國 依芸Yiyun !!!" 生成加密資料。 6 // 3、前端然後生成自己的金鑰對,用自己的私鑰及演算法SHA1withRSA生成摘要簽名。 7 // 4、前端把加密資料、自己的公鑰與簽名一併發給後端。 8 // 5、後端收到加密資料、簽名資料、前端的公鑰,先用前端公鑰、簽名資料、演算法SHA1withRSA 驗籤,驗籤通過後再用後端的私鑰、演算法:RSA/ECB/PKCS1Padding 解密資料。 9 // 注意:金鑰與密文全部base64編碼 10 11 RsaUtil rsaUtil = new RsaUtil(true); 12 13 String privateKey_s = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIfKRPRzS0a9Rg1LQizkfIL1ciAMEFs45tl49ERuwIA1kcUrtB1Cjj3bKMLO0Sp7992ECOWVZsE6DZPle3kVYFufIBT4pjR1oJqRs4Z9g5bkwY6p743eGnT1pxri5LNqBdevlKsjqwcfIdOhIqz2BaeM3PT1O52PI9e+U40XEri7AgMBAAECgYAmNYNLqbmP0SiKCxg226AxlXEklWBw2sUSgpdxPhzKtsgqzA5lgVnXC/kfP+TZaIKpgUKjn3OHgZdae2NQAfTXxTcvhNGYSOeJ8VgslQueoJW7ypgQ/IoNy2DeglObAJ3uCgA4F566j6H7IvcllKGmDT/6PUlljxZJpBMfslspgQJBAP19EMRxmV4vYL7o55oR397UEUXn3vO88SPo2gxaPZ/ltzgaHM5R1zALPE1EfPIPqVdGf2hcowr22pC1BG+nlXsCQQCJIq4USfgNmjGwquo5PyksQ9vsYc/OxGBxEqTpVez24eJb7tvoqvbYfpleeEyWgtvzHqnlY24QdONhVVm5zOXBAkAxt7PwM6+3D2fUSe4TA+p60/FHWsEZ4TcSqfsKbTClCfMzp7t6pAamv61mIka3W2cFXShkGbdI0T3xH+/szlu9AkBi3SSgrd7td39hPSaU1MsLBXT0SmO1Te+1NNq8+VxXc+trmZzidPZ2h3ZsG9AjJf4JnM6g9/iuVoZiclS4VVZBAkEAsPkIGRvX4Nj3ljiBjgdJ68JRZC3gK/kXLNeefIeHg6F/4eyg729PlfdD2mvPb8hiszvsT1zvF8gvxGi4lT6B/w==" ; 14 String publicKey_s = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHykT0c0tGvUYNS0Is5HyC9XIgDBBbOObZePREbsCANZHFK7QdQo492yjCztEqe/fdhAjllWbBOg2T5Xt5FWBbnyAU+KY0daCakbOGfYOW5MGOqe+N3hp09aca4uSzagXXr5SrI6sHHyHToSKs9gWnjNz09TudjyPXvlONFxK4uwIDAQAB"; 15 String publicKey_c ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQPgskvyi9D/IuD0x73M2UOxBH3daAGbxLfUiSraG3cEgZCp7/o1RKM/Uckoplw/DDD665je4wVc0R2zZ8E9LQrrHvvVgddaCvaFZkcIno4EVtHCLldKOFzAIr8ucxCHelV9oGhrcCmeGwYnVTeXOerY9iFi2KDWwF30e2PHRpRQIDAQAB"; 16 17 // publicKey_s = rsaUtil.getKeyMap().get("publicKey"); 18 // privateKey_s =rsaUtil.getKeyMap().get("privateKey"); 19 System.out.println("<--- 1 --->伺服器端生成公鑰:" +publicKey_s ); 20 // System.out.println("<--- 1 --->伺服器端生成私鑰:"+ privateKey_s); 21 System.out.println("<--- 1 --->伺服器端生成私鑰,不公開"); 22 System.out.println("<--- 2 --->客戶端獲取到伺服器端給定的公鑰:"+ publicKey_s); 23 System.out.println("<--- 3 --->客戶端用此公鑰執行加密,加密原始資料:xhy 我愛你 中國 依芸Yiyun !!!"); 24 25 // 客戶端用伺服器端的公鑰生成的密文 26 String cipherText_c ="eCOu/WkaQ8tZHk2u+Y9bh6RKOVMQGsssjnQB5DVlUeDPhjiIybeQSe7JH7fG5FgsucCi6uFwdU7yWzmkJFmMKGnE1pGLReqSaWgecviSTl1P4jjrq84VJvreoeCmcNUCoqxQvmYuMxB/D4rZ+PTuv0B2sQ4Q5fOH6fbqoj3uD5w="; 27 cipherText_c ="MoYui9R3RZ+OYamuTvwAGWy2u8rllBQpjGzTXzje9aUM/KiD5nt8DYf1wna5a7DkBYpZ0CRVNHtOHiHgmrheBkpLuAXdh58eVn2hFLn0xCXqIRk+yEZSrF8fHJ7EBhE00WcyfqOoAM+uum57DFxtvATcEnFiEdkRb32JWkumdIY="; 28 System.out.println("<--- 3 --->客戶端生成的加密資料:"+cipherText_c); 29 System.out.println("<--- 4 --->客戶端生成自己的公鑰:"+ publicKey_c); 30 // System.out.println("<--- 4 --->客戶端生成自己的私鑰:"+ privateKey_c); 31 System.out.println("<--- 4 --->客戶端生成自己的私鑰,不公開"); 32 33 // 客戶端利用自己的私鑰生成摘要後進行的簽名 34 String signedText_c = "uFy+PqjxdxusV5+a9VR0cvk1XY0+Th8jWBT581irWVEDyzq00xGphQ8KIyApgvPw5+KP1DB/M7tMfd0viUT4w8i4VcyhGmRlk0XNkuRhQDgcWeZ5XKIoJ1ORQ0ecxcAAAAlPwMe2wCbPClXFmhJzypJtS7nKFzE/oeZg7nr91zg="; 35 signedText_c ="reCyRzzxeo1i269BdV5TBrYqiYNyWEmk+i6Oxq/0MJz581tThBikh6/Z+hTZ3vY03UngoXwxB7pZBPW7FUxtwdqS8FcKtlVShaz0ZwU22BHbqFBvCvr4C224HQAdNeVoeHb3o8O/DEjNruUzM1NkweLqayI0unieRYxebvCweTE="; 36 System.out.println("<--- 4 --->客戶端利用自己的私鑰生成摘要而後簽名資料:"+signedText_c); 37 System.out.println("<--- 5 --->客戶端傳送資料至伺服器端,1自己的公鑰,2自己的加密資料,3自己的簽名資料"); 38 System.out.println("<--- 6 --->伺服器端接收到來自客戶端的資料:1客戶端的公鑰,2客戶端的加密資料,3客戶端的簽名資料"); 39 System.out.println("<--- 6 --->1客戶端的公鑰:"+publicKey_c); 40 System.out.println("<--- 6 --->2客戶端的加密資料:"+cipherText_c); 41 System.out.println("<--- 6 --->2客戶端的簽名資料:"+signedText_c); 42 43 boolean isPassed = rsaUtil.verifySignPublicKey(cipherText_c, signedText_c, publicKey_c, true,true,false); 44 System.out.println("<--- 7 --->伺服器端用自己的公鑰進行簽名驗證,結果:"+isPassed); 45 if (isPassed) 46 { 47 System.out.println("<--- 8 --->伺服器端驗籤成功,開始解密資料..."); 48 49 String plainText = ""; 50 plainText = rsaUtil.decryptByPrivateKey(cipherText_c,privateKey_s,true,true,false); 51 52 System.out.println("<--- 9 --->伺服器端解密資料成功:"+ plainText); 53 } 54 55 /* 測試程式碼 56 // 伺服器端用伺服器端的公鑰生成的密文 57 String cipherText_s =""; 58 String plainText = "xhy 我愛你 中國 依芸Yiyun !!!"; 59 cipherText_s = rsaUtil.encryptByPublicKey(plainText, publicKey_s,true, true,false); 60 System.out.println("伺服器端執行加密,公鑰加密:"+cipherText_s); 61 62 // 伺服器端利用客戶端的私鑰進行簽名 63 String signedText_s = rsaUtil.signByPrivateKey(cipherText_c, privateKey_c,true, true,false); 64 System.out.println("伺服器端執行簽名,私鑰簽名:"+signedText_s); 65 */ 66 67 /* String privateKey_c = "MIICXAIBAAKBgQDQPgskvyi9D/IuD0x73M2UOxBH3daAGbxLfUiSraG3cEgZCp7/o1RKM/Uckoplw/DDD665je4wVc0R2zZ8E9LQrrHvvVgddaCvaFZkcIno4EVtHCLldKOFzAIr8ucxCHelV9oGhrcCmeGwYnVTeXOerY9iFi2KDWwF30e2PHRpRQIDAQABAoGARuvaf7la9ojnwigTtFuO6Fz1PoSe+SHKrysL/GiGGyNyapTjccz+eAcaA5Ek8WO6K7S7nRZpeKzAGsS92aQmt66BpOqI+JJ2uM+K1HzH5K5rQ4rnaC/Hbd+4zsltVzuLbsICDGSlkpTSKK5YdIkA5YPMXoQek4zoYpUnKT2AxEECQQDoDrjIJ4MllIpc" + 68 "gAWjahga1YrcTIcQPBwG9rfX7zk2nKFZF5rOB6iDHjE9mo9EOD/s7j3Z5eefwVkp" + 69 "hRnbXJp3AkEA5bpMSf8zyBKfMZll3vdtDTDqnsVzOu89RxQYgceyWZ/OcFgvc9hg" + 70 "NYoV/EkGQXcHWL1gPQwWpMRfS8L/DjbNIwJBAL3NBL/Y6YB8TOq5X2M4bHzOOiRT" + 71 "h4j00Su08ctxA8eyNpnrH5fyVZbgw/+SAioXI9oDRp2JWHinKOk3z11HEaMCQDI/" + 72 "qLY60xm9MQMJWaYGmtzayUcHS2glslKcy6t/gbxm3yHluCNvvcOYO6zeUDb7kSjQ" + 73 "638O6NkLdwi8U0vJot8CQHEfumEFZ0LYbz914TZOWe2q0UKOUZaHgQIwoJ3n2yxJ" + 74 "p7Ps3k9t2Of8Tm+HqZYCkSz8henOM8aFCS2GPD8Pkf4=" ; 75 */ 76 }
客戶端vue:
工具類,常用加解密演算法,enDecrypt.js
1 /* base64 加解密 2 */ 3 export let Base64 = require('js-base64').Base64 4 5 /* md5 加解密 6 */ 7 export let crypto = require('crypto'); 8 export let md5 = require('js-md5'); 9 export let CryptoJS = require('crypto-js'); 10 export let MD5 = CryptoJS.MD5; 11 /* 12 *引入jsencrypt實現資料RSA加密 13 */ 14 import JSEncrypt from 'jsencrypt'; 15 // jsencrypt.js處理長文字資料時報錯 Message too long for RSA 16 // encryptlong是基於jsencrypt擴充套件的長文字分段加解密功能。 17 import Encrypt from "encryptlong"; 18 // rsa sign 19 import jsrsasign from 'jsrsasign' 20 21 // Message Digest algorithm 5,資訊摘要演算法 22 // alglorithm:md5、sha1、sha256 23 export function Md5(plainText, alglorithm, encoding){ 24 const hash = crypto.createHash(alglorithm) 25 hash.update(plainText);//加密內容 26 return hash.digest(encoding);//密文 27 } 28 29 //Hash Message Authentication Code,雜湊訊息鑑別碼 30 //Secure Hash Algorithm,安全雜湊演算法 31 //alglorithm:md5、sha256、sha1 32 export function HMac(plainText, secretKey,alglorithm, encoding){ 33 const hmac= crypto.createHmac(alglorithm, secretKey); 34 const cipherText= hmac.update(plainText);//加密內容 35 return cipherText.digest(encoding);//密文 36 } 37 38 // Data Encryption Standard,資料加密演算法 39 // DES/DES3/AES 加密, key與iv長度必須是8的倍數 40 // mode:CryptoJS.mode.CBC、CryptoJS.mode.ECB、CryptoJS.mode.CFB 41 // padding:CryptoJS.pad.ZeroPadding、CryptoJS.pad.Pkcs7、CryptoJS.pad.NoPadding 42 export function encrypt ( algorithm, plainText,key, iv, mode, padding, isTextBase64) { 43 key = key ? key : "abcdefghijklmnop"; 44 iv = iv ? iv : "0102030405060708"; 45 46 const keyHex = CryptoJS.enc.Utf8.parse(key); 47 const ivHex = CryptoJS.enc.Utf8.parse(iv); 48 const option = { iv:keyHex,mode: mode, padding: padding } 49 let encrypted = null ; 50 if(algorithm === "TripleDES"){ 51 encrypted = CryptoJS.TripleDES.encrypt(plainText, keyHex, option) 52 }else if(algorithm === "DES"){ 53 encrypted = CryptoJS.DES.encrypt(plainText, keyHex, option) 54 } 55 else if(algorithm === "AES"){ 56 encrypted = CryptoJS.AES.encrypt(plainText, keyHex, option) 57 } 58 return isTextBase64?CryptoJS.enc.Base64.stringify(encrypted.ciphertext):encrypted.ciphertext.toString(); 59 } 60 61 // DES/DES3/AES解密,key與iv長度必須是8的倍數 62 export function decrypt (algorithm,cipherText,key, iv, mode, padding, isTextBase64) { 63 key = key ? key : "abcdefghijklmnop"; 64 iv = iv ? iv : "0102030405060708"; 65 66 const keyHex = CryptoJS.enc.Utf8.parse(key); 67 const ivHex = CryptoJS.enc.Utf8.parse(iv); 68 const decryptText = isTextBase64? CryptoJS.enc.Base64.parse(cipherText):cipherText; 69 const textHex = { ciphertext: isTextBase64?decryptText:CryptoJS.enc.Hex.parse(decryptText) } 70 const option = { iv:ivHex,mode: mode, padding: padding } 71 let decrypted = null; 72 if(algorithm === "TripleDES"){ 73 decrypted = CryptoJS.TripleDES.decrypt(textHex, keyHex, option); 74 }else if(algorithm === "DES"){ 75 decrypted = CryptoJS.DES.decrypt(textHex, keyHex, option); 76 } 77 else if(algorithm === "AES"){ 78 decrypted = CryptoJS.AES.decrypt(textHex, keyHex, option); 79 } 80 return decrypted.toString(CryptoJS.enc.Utf8); 81 } 82 83 export function stringToHex(strSource) { 84 if(strSource === "") 85 return ""; 86 var hexCharCode = []; 87 for(var i = 0; i < strSource.length; i++) { 88 hexCharCode.push((strSource.charCodeAt(i)).toString(16)); 89 } 90 return hexCharCode.join(""); 91 } 92 93 export function hexToString(hexCharCodeStr) { 94 var trimedStr = hexCharCodeStr.trim(); 95 var len = trimedStr.length; 96 if(len % 2 !== 0) { 97 alert("Illegal Format ASCII Code!"); 98 return ""; 99 } 100 var curCharCode; 101 var resultStr = []; 102 for(var i = 0; i < len;i = i + 2) { 103 curCharCode = parseInt(trimedStr.substr(i, 2), 16); // ASCII Code Value 104 resultStr.push(String.fromCharCode(curCharCode)); 105 } 106 return resultStr.join(""); 107 } 108 109 /** RSA 加密過程 110 * (1)A生成一對金鑰(公鑰和私鑰),私鑰不公開,A自己保留。公鑰為公開的,任何人可以獲取。 111 * (2)A傳遞自己的公鑰給B,B用A的公鑰對訊息進行加密。 112 * (3)A接收到B加密的訊息,利用A自己的私鑰對訊息進行解密。 113 * 在這個過程中,只有2次傳遞過程,第一次是A傳遞公鑰給B,第二次是B傳遞加密訊息給A,即使都被敵方截獲,也沒有危險性。 114 * 因為只有A的私鑰才能對訊息進行解密,防止了訊息內容的洩露。 115 * 使用方法 116 * 客戶端初始化訪問伺服器端時,伺服器端會生成一對RSA對,及公鑰和金鑰。 117 * 如果前端只需要將要傳給後端的資料進行加密後傳輸,那麼前端可以只要公鑰,通過公鑰對要傳輸的引數進行加密後把加密的字串發給後端,後端取出儲存的密碼種子或者直接儲存的私鑰,採用私鑰對加密字串進行解密,得到明文。 118 * 如果前端要獲取後端傳過來的已經加密後的字串,並且解密使用,那麼前端就需要拿到RSA對立面的私鑰進行解密後使用了。 119 * */ 120 /* JSEncrypt 公鑰加密 padding:pkcs1pad2 */ 121 export function RsaJSEncrypt(plainText,publicKey,isKeyBase64,isTextBase64,isURLCode) { 122 const jsencrypt = new JSEncrypt({ 123 default_key_size: 1024 124 }); 125 // setPublicKey 引數預設需要base64,如果是十六進位制編碼則需要轉換為base64,jsrsasign.b64tohex,jsrsasign.hextob64 126 isKeyBase64?jsencrypt.setPublicKey(publicKey):jsencrypt.setPublicKey( jsrsasign.hextob64(publicKey)); 127 // 如果是物件/陣列的話,需要先JSON.stringify轉換成字串 128 // 處理中文亂碼,伺服器端:String result = java.net.URLDecoder.decode(cipherText ,"UTF-8"); 129 let cipherText = jsencrypt.encrypt(plainText); 130 131 // 預設加密結果為base64編碼 132 cipherText = isTextBase64?cipherText:jsrsasign.b64tohex(cipherText); 133 // +號伺服器端不識別,url編碼 134 cipherText = isURLCode? encodeURIComponent(cipherText):cipherText; 135 136 return cipherText; 137 } 138 139 /* JSEncrypt 私鑰解密 padding:pkcs1pad2 */ 140 export function RsaJSDecrypt(cipherText,privateKey,isKeyBase64,isTextBase64,isURLCode) { 141 const jsencrypt = new JSEncrypt({ 142 default_key_size: 1024, 143 padding: crypto.constants.RSA_PKCS1_PADDING 144 }); 145 146 isKeyBase64?jsencrypt.setPrivateKey(privateKey):jsencrypt.setPrivateKey(jsrsasign.hextob64(privateKey)); 147 148 cipherText = isURLCode?decodeURIComponent(cipherText):cipherText; 149 cipherText = isTextBase64?cipherText:jsrsasign.b64tohex(cipherText); 150 151 return jsencrypt.decrypt(cipherText); 152 } 153 154 /* 長文字分段加密 */ 155 export function RsaEncrypt(plainText,publicKey,isKeyBase64,isTextBase64,isURLCode) { 156 const encryptor = new Encrypt({ 157 default_key_size: 1024, 158 padding: crypto.constants.RSA_PKCS1_PADDING 159 }); 160 if (isKeyBase64) { 161 encryptor.setPublicKey(publicKey) 162 } 163 else { 164 encryptor.setPublicKey(jsrsasign.hextob64(publicKey)); 165 } 166 167 // 處理中文亂碼,伺服器端:String result = java.net.URLDecoder.decode(cipherText ,"UTF-8"); 168 169 let cipherText = encryptor.encryptLong(plainText); 170 171 cipherText = isTextBase64?cipherText:jsrsasign.hextob64(cipherText); 172 // +號伺服器端不識別,url編碼 173 cipherText = isURLCode? encodeURIComponent(cipherText):cipherText; 174 175 return cipherText; 176 } 177 178 /* 長文字分段解密 */ 179 export function RsaDecrypt(cipherText,privateKey,isKeyBase64,isTextBase64,isURLCode) { 180 const encryptor = new Encrypt({ 181 default_key_size: 1024, 182 padding: crypto.constants.RSA_PKCS1_PADDING 183 }) 184 185 if (isKeyBase64){ 186 encryptor.setPrivateKey(privateKey) 187 } 188 else{ 189 encryptor.setPrivateKey(jsrsasign.hextob64(privateKey)); 190 } 191 cipherText = isURLCode?decodeURIComponent(cipherText):cipherText; 192 cipherText = isTextBase64?cipherText:jsrsasign.b64tohex(cipherText); 193 194 return encryptor.decryptLong(cipherText); 195 } 196 197 // 獲取簽名 privateKey 198 export function RsaSign(plainText,privateKey,format_key, algorithm,isKeyBase64,isTextBase64,isURLCode) 199 { 200 // 生成簽名物件 201 let sign = genSign(isKeyBase64?privateKey:jsrsasign.hextob64(privateKey),format_key, algorithm); 202 plainText = isTextBase64?jsrsasign.b64tohex(plainText):plainText; 203 // console.log("待簽名前資料:"+plainText); 204 let plain_Text = genDigest(plainText,algorithm); 205 206 // console.log("待簽名摘要資料:"+plain_Text); 207 sign.updateString(plain_Text); 208 209 // 預設簽名資料為十六進位制資料 210 let signedText = isTextBase64?jsrsasign.hextob64(sign.sign()):sign.sign(); 211 212 // console.log("生成簽名資料:"+sign.sign()); 213 // +號伺服器端不識別,url編碼 214 signedText = isURLCode? encodeURIComponent(signedText):signedText; 215 216 return signedText; 217 } 218 219 // 驗證簽名 publicKey_s 伺服器端的公鑰 220 // alglorithm: SHA1withRSA、MD5withRSA、SHA256withRSA、 SHA384withRSA、SHA512withRSA、RIPEMD160withRSA 221 // format_key: PKCS#1、PKCS#5、PKCS#8 222 /* 223 * PKCS#1:定義RSA公開金鑰演算法加密和簽名機制,主要用於組織PKCS#7中所描述的數字簽名和數字信封。 224 * PKCS#3:定義Diffie-Hellman金鑰交換協議。 225 * PKCS#5:描述一種利用從口令派生出來的安全金鑰加密字串的方法。使用MD2或MD5 從口令中派生金鑰,並採用DES-CBC模式加密。主要用於加密從一個計算機傳送到另一個計算機的私人金鑰,不能用於加密訊息[24]。 226 * PKCS#6:描述了公鑰證照的標準語法,主要描述X.509證照的擴充套件格式。 227 * PKCS#7:定義一種通用的訊息語法,包括數字簽名和加密等用於增強的加密機制,PKCS#7與PEM相容,所以不需其他密碼操作,就可以將加密的訊息轉換成PEM訊息[26]。 228 * PKCS#8:描述私有金鑰資訊格式,該資訊包括公開金鑰演算法的私有金鑰以及可選的屬性集等。 229 * PKCS#9:定義一些用於PKCS#6證照擴充套件、PKCS#7數字簽名和PKCS#8私鑰加密資訊的屬性型別。 230 * PKCS#10:描述證照請求語法。 231 * PKCS#11:稱為Cyptoki,定義了一套獨立於技術的程式設計介面,用於智慧卡和PCMCIA卡之類的加密裝置。 232 * PKCS#12:描述個人資訊交換語法標準。描述了將使用者公鑰、私鑰、證照和其他相關資訊打包的語法。 233 * PKCS#13:橢圓曲線密碼體制標準。 234 * PKCS#14:偽隨機數生成標準。 235 * PKCS#15:密碼令牌資訊格式標準。 236 */ 237 export function RsaVerifySign(plainText,signedText,publicKey,format_key, algorithm,isKeyBase64,isTextBase64,isURLCode) 238 { 239 // 生成簽名 240 let verifySign = genSign(isKeyBase64?publicKey:jsrsasign.hextob64(publicKey),format_key, algorithm); 241 plainText = isTextBase64?jsrsasign.b64tohex(plainText):plainText; 242 // 根據明文生成摘要 243 let digestText = genDigest(plainText,algorithm); 244 245 verifySign.updateString(digestText); 246 247 signedText = isURLCode?decodeURIComponent(signedText):signedText; 248 signedText = isTextBase64?jsrsasign.b64tohex(signedText):signedText; 249 250 return verifySign.verify(signedText); 251 } 252 253 // 根據明文生成摘要 254 //SHA1withRSA、MD5withRSA、SHA256withRSA、 SHA384withRSA、SHA512withRSA、RIPEMD160withRSA 255 export function genDigest(plainText,algorithm ){ 256 let option = { "alg": algorithm.split('w')[0], "prov":"cryptojs/jsrsa", } 257 // console.log("演算法:"+algorithm.split('w')[0]); 258 let text = new jsrsasign.KJUR.crypto.MessageDigest(option); // 摘要 259 text.updateString(plainText); 260 261 let digestText = text.digest(); 262 // console.log("摘要:"+digestText); 263 return digestText; 264 } 265 266 /* 生成rsa簽名物件 267 * */ 268 export function genSign(RsaKey,format_key, algorithm) 269 { 270 // 金鑰要寫開頭和結束 271 // var private_key = '-----BEGIN PRIVATE KEY-----' + privateKey_s + '-----END PRIVATE KEY-----' 272 // 讀取解析pem格式的祕鑰, 生成祕鑰例項 (RSAKey) 273 let rsaKey = new jsrsasign.RSAKey(); 274 if (format_key === "PKCS#1" || format_key === "PKCS#5"|| format_key === "PKCS#7"|| format_key === "PKCS#8") { 275 rsaKey = jsrsasign.KEYUTIL.getKey(RsaKey); 276 // rsaSign.readPrivateKeyFromPEMString(privateKey_s); 277 } 278 279 let option= { 280 "alg":algorithm, 281 "prov":"cryptojs/jsrsa", 282 "prvkeypem": rsaKey 283 }; 284 285 let sign = new jsrsasign.KJUR.crypto.Signature(option); 286 sign.init(rsaKey); 287 288 return sign; 289 }
前端使用程式碼流程:
1 // 測試前後端使用流程 2 // 1、後端生成金鑰對,公鑰分享給前端,前端獲取到伺服器給的公鑰。 3 // 2、前端用伺服器端給的公鑰 及演算法:RSA/ECB/PKCS1Padding 加密"xhy 我愛你 中國 依芸Yiyun !!!" 生成加密資料。 4 // 3、前端然後生成自己的金鑰對,用自己的私鑰及演算法SHA1withRSA生成摘要簽名。 5 // 4、前端把加密資料、自己的公鑰與簽名一併發給後端。 6 // 5、後端收到加密資料、簽名資料、前端的公鑰,先用前端公鑰、簽名資料、演算法SHA1withRSA 驗籤,驗籤通過後再用後端的私鑰、演算法:RSA/ECB/PKCS1Padding 解密資料。 7 // 注意:金鑰與密文全部base64編碼 8 9 let plainText = "xhy 我愛你 中國 依芸Yiyun !!!"; 10 11 let privateKey_c = "-----BEGIN RSA PRIVATE KEY-----" + 12 "MIICXAIBAAKBgQDQPgskvyi9D/IuD0x73M2UOxBH3daAGbxLfUiSraG3cEgZCp7/" + 13 "o1RKM/Uckoplw/DDD665je4wVc0R2zZ8E9LQrrHvvVgddaCvaFZkcIno4EVtHCLl" + 14 "dKOFzAIr8ucxCHelV9oGhrcCmeGwYnVTeXOerY9iFi2KDWwF30e2PHRpRQIDAQAB" + 15 "AoGARuvaf7la9ojnwigTtFuO6Fz1PoSe+SHKrysL/GiGGyNyapTjccz+eAcaA5Ek" + 16 "8WO6K7S7nRZpeKzAGsS92aQmt66BpOqI+JJ2uM+K1HzH5K5rQ4rnaC/Hbd+4zslt" + 17 "VzuLbsICDGSlkpTSKK5YdIkA5YPMXoQek4zoYpUnKT2AxEECQQDoDrjIJ4MllIpc" + 18 "gAWjahga1YrcTIcQPBwG9rfX7zk2nKFZF5rOB6iDHjE9mo9EOD/s7j3Z5eefwVkp" + 19 "hRnbXJp3AkEA5bpMSf8zyBKfMZll3vdtDTDqnsVzOu89RxQYgceyWZ/OcFgvc9hg" + 20 "NYoV/EkGQXcHWL1gPQwWpMRfS8L/DjbNIwJBAL3NBL/Y6YB8TOq5X2M4bHzOOiRT" + 21 "h4j00Su08ctxA8eyNpnrH5fyVZbgw/+SAioXI9oDRp2JWHinKOk3z11HEaMCQDI/" + 22 "qLY60xm9MQMJWaYGmtzayUcHS2glslKcy6t/gbxm3yHluCNvvcOYO6zeUDb7kSjQ" + 23 "638O6NkLdwi8U0vJot8CQHEfumEFZ0LYbz914TZOWe2q0UKOUZaHgQIwoJ3n2yxJ" + 24 "p7Ps3k9t2Of8Tm+HqZYCkSz8henOM8aFCS2GPD8Pkf4=" + 25 "-----END RSA PRIVATE KEY-----"; 26 27 let publicKey_c ="-----BEGIN PUBLIC KEY-----" + 28 "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQPgskvyi9D/IuD0x73M2UOxBH" + 29 "3daAGbxLfUiSraG3cEgZCp7/o1RKM/Uckoplw/DDD665je4wVc0R2zZ8E9LQrrHv" + 30 "vVgddaCvaFZkcIno4EVtHCLldKOFzAIr8ucxCHelV9oGhrcCmeGwYnVTeXOerY9i" + 31 "Fi2KDWwF30e2PHRpRQIDAQAB" + 32 "-----END PUBLIC KEY-----"; 33 34 let publicKey_s = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHykT0c0tGvUYNS0Is5HyC9XIgDBBbOObZePREbsCANZHFK7QdQo492yjCztEqe/fdhAjllWbBOg2T5Xt5FWBbnyAU+KY0daCakbOGfYOW5MGOqe+N3hp09aca4uSzagXXr5SrI6sHHyHToSKs9gWnjNz09TudjyPXvlONFxK4uwIDAQAB"; 35 36 console.log("<--- 2 --->客戶端獲取到伺服器端給定的公鑰:"+ publicKey_s); 37 38 let cipherText_c =this.$RsaEncrypt(plainText,publicKey_s,true,true,false); 39 console.log("<--- 3 --->客戶端利用伺服器端的公鑰加密資料,生成密文,base64編碼輸出\n"+cipherText_c); 40 // 記錄 cipherText_c = "eCOu/WkaQ8tZHk2u+Y9bh6RKOVMQGsssjnQB5DVlUeDPhjiIybeQSe7JH7fG5FgsucCi6uFwdU7yWzmkJFmMKGnE1pGLReqSaWgecviSTl1P4jjrq84VJvreoeCmcNUCoqxQvmYuMxB/D4rZ+PTuv0B2sQ4Q5fOH6fbqoj3uD5w="; 41 42 let signedText_c = this.$RsaSign(cipherText_c,privateKey_c,"PKCS#8","SHA1withRSA",true, 43 true,false); 44 console.log("<--- 4 --->客戶端利用自己生成的私鑰簽名資料,生成摘要與簽名\n"+signedText_c); 45 console.log("<--- 4 --->客戶端生成自己的公鑰\n"+publicKey_c); 46 console.log("<--- 5 --->客戶端傳送資料至伺服器端,1客戶端的公鑰,2客戶端的加密資料,3客戶端的簽名資料\n"); 47 // 記錄 signedText_c = "uFy+PqjxdxusV5+a9VR0cvk1XY0+Th8jWBT581irWVEDyzq00xGphQ8KIyApgvPw5+KP1DB/M7tMfd0viUT4w8i4VcyhGmRlk0XNkuRhQDgcWeZ5XKIoJ1ORQ0ecxcAAAAlPwMe2wCbPClXFmhJzypJtS7nKFzE/oeZg7nr91zg="; 48 // 注意:加密密文與簽名都是唯一的,不會變化。 49 // 注意:vue 端金鑰都要帶頭。 50 // 注意:vue端及java端函式引數要求是什麼型別及何進位制。搞明白哪裡用base64,哪裡2進位制,哪裡16進位制。 51 // 重點還是要了解點原理,比如sha1withrsa,先經過sha1演算法,知道aaa,雜湊後的密文16進位制是:7e240de74fb1ed08fa08d38063f6a6a91462a815,對比自己的程式有沒有算錯。 52 // 利用一些線上測試工具幫忙驗證自己的程式過程。http://www.metools.info/code/c82.html ; 同時知道如何查引入的類庫各api的官網,瞭解如何使用各函式。 53 // 不然遇到莫名奇妙的錯誤無從解決。報錯的地方不一定是程式實際錯誤的地方。瞭解查錯的方式有:打莊、debug. 54 55 /* 僅供測試使用,加密資料及驗籤 56 let privateKey_s = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIfKRPRzS0a9Rg1LQizkfIL1ciAMEFs45tl49ERuwIA1kcUrtB1Cjj3bKMLO0Sp7992ECOWVZsE6DZPle3kVYFufIBT4pjR1oJqRs4Z9g5bkwY6p743eGnT1pxri5LNqBdevlKsjqwcfIdOhIqz2BaeM3PT1O52PI9e+U40XEri7AgMBAAECgYAmNYNLqbmP0SiKCxg226AxlXEklWBw2sUSgpdxPhzKtsgqzA5lgVnXC/kfP+TZaIKpgUKjn3OHgZdae2NQAfTXxTcvhNGYSOeJ8VgslQueoJW7ypgQ/IoNy2DeglObAJ3uCgA4F566j6H7IvcllKGmDT/6PUlljxZJpBMfslspgQJBAP19EMRxmV4vYL7o55oR397UEUXn3vO88SPo2gxaPZ/ltzgaHM5R1zALPE1EfPIPqVdGf2hcowr22pC1BG+nlXsCQQCJIq4USfgNmjGwquo5PyksQ9vsYc/OxGBxEqTpVez24eJb7tvoqvbYfpleeEyWgtvzHqnlY24QdONhVVm5zOXBAkAxt7PwM6+3D2fUSe4TA+p60/FHWsEZ4TcSqfsKbTClCfMzp7t6pAamv61mIka3W2cFXShkGbdI0T3xH+/szlu9AkBi3SSgrd7td39hPSaU1MsLBXT0SmO1Te+1NNq8+VxXc+trmZzidPZ2h3ZsG9AjJf4JnM6g9/iuVoZiclS4VVZBAkEAsPkIGRvX4Nj3ljiBjgdJ68JRZC3gK/kXLNeefIeHg6F/4eyg729PlfdD2mvPb8hiszvsT1zvF8gvxGi4lT6B/w==" ; 57 58 console.log("RSAVerifySign_pubK="+this.$RsaVerifySign(cipherText_c, signedText_c, 59 publicKey_c,"PKCS#8","SHA1withRSA",true,true,false)); 60 61 plainText = ""; 62 plainText = this.$RsaDecrypt(cipherText_c, privateKey_s,true,true,false) 63 console.log("RSADe_priK="+plainText); 64 */
測試結果截圖說明
伺服器:
客戶端: