项目已经接近尾声,一个Web网页聊天室P-LinkC 。出于信息安全的考虑,准备为聊天室添加一套信息加密技术对聊天信息进行加密处理。这里我选用AES对称加密技术。
What is AES?
AES加密又称“高级加密标准”,在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准被用来替代原先的DES,已经被多方分析且广为全世界所使用。 — 维基百科
简单了解AES加密 对称加密与非对称加密
非对称加密:用户双方在信息交互开始前得到对方公钥,发送密文用对方公钥加密,对方用自己的私钥解密
AES算法是一种对称加密算法 AES加密的特点
AES的密钥 密钥是AES算法实现加密和解密的根本。对称加密算法之所以对称,是因为这类算法对明文的加密和解密需要使用同一个密钥。 AES支持三种长度的密钥:128位,192位,256位。其中:
填充加密 AES算法在对明文加密的时候,并不是把整个明文一股脑加密成一整段密文,而是把明文拆分成一个个独立的明文块,每一个明文块长度128bit进行独立加密再拼接。如果一个明文块长度不够128bit将会被填充至128bit。
工作模式 AES的工作模式,体现在把明文块加密成密文块的处理过程中。AES加密算法提供了五种不同的工作模式:ECB、CBC、CTR、CFB、OFB。加密解密的模式必须一致!
AES加密在WebSocket上的实现 后端代码 - AesUtil类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 public class AesUtil { private static final String KEY_ALGORITHM = "AES" ; private static final Integer KEY_LENGTH = 16 * 8 ; private static final String ALGORITHMS = "AES/ECB/PKCS5Padding" ; public static String key; static { key = getKey(); } public static String getKey () { StringBuilder uid = new StringBuilder(); Random rd = new SecureRandom(); for (int i = 0 ; i < KEY_LENGTH / 8 ; i++) { int type = rd.nextInt(3 ); switch (type) { case 0 : uid.append(rd.nextInt(10 )); break ; case 1 : uid.append((char ) (rd.nextInt(25 ) + 65 )); break ; case 2 : uid.append((char ) (rd.nextInt(25 ) + 97 )); break ; default : break ; } } return uid.toString(); } public static String encrypt (String content, String encryptKey) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHMS,new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM)); byte [] b = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); return Base64.encodeBase64String(b); } public static String decrypt (String encryptStr, String decryptKey) throws Exception { byte [] decodeBase64 = Base64.decodeBase64(encryptStr); Cipher cipher = Cipher.getInstance(ALGORITHMS,new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM)); byte [] decryptBytes = cipher.doFinal(decodeBase64); return new String(decryptBytes); } }
后端AES工具类简单测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { String key = "MIGfMA0GCSqGSIb3" ; String str = "唱、跳、Rap、篮球" ; try { String encrypt = AesUtil.encrypt(str, key); String decrypt = AesUtil.decrypt(encrypt, key); System.out.println("加密前:" + str); System.out.println("加密后:" + encrypt); System.out.println("解密后:" + decrypt); } catch (Exception e) { e.printStackTrace(); } }
测试结果
前端AES算法实现 CryptoJS AES我们采用CryptoJS,是一个标准和安全加密算法的JavaScript库,它的AES加密支持AES-128、AES-192和AES-256。下载或查看详情介绍请戳官网地址
GitHub地址:https://github.com/brix/crypto-js
官网地址:https://code.google.com/archive/p/crypto-js/
整合AES算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 var aesUtil = { genKey : function (length = 16 ) { let random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ; let str = "" ; for (let i = 0 ; i < length; i++) { str = str + random.charAt(Math .random() * random.length) } return str; }, encrypt : function (plaintext,key ) { if (plaintext instanceof Object ) { plaintext = JSON .stringify(plaintext) } let encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), CryptoJS.enc.Utf8.parse(key), {mode :CryptoJS.mode.ECB,padding : CryptoJS.pad.Pkcs7}); return encrypted.toString(); }, decrypt : function (ciphertext,key ) { let decrypt = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(key), {mode :CryptoJS.mode.ECB,padding : CryptoJS.pad.Pkcs7}); let decString = CryptoJS.enc.Utf8.stringify(decrypt).toString(); if (decString.charAt(0 ) === "{" || decString.charAt(0 ) === "[" ){ decString = JSON .parse(decString); } return decString; } }; aesUtil
前端AES工具类简单测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script> let text = "唱、跳、Rap、篮球" ; let genKey = aesUtil.genKey(); let ciphertext = aesUtil.encrypt(text,genKey); let plaintext = aesUtil.decrypt(ciphertext,genKey); console .log("key:" );console .log(genKey); console .log("加密前:" );console .log(text); console .log("key加密后:" + ciphertext); console .log("key解密后:" );console .log(plaintext); </script>
测试结果
前后端联调测试 主要目的是对用户聊天信息进行加密,故在前端打包加密用户发送的聊天信息。
前端部分 1 2 3 4 5 6 7 8 9 10 11 12 let aesKey = aesUtil.genKey(); let data = { data:aesUtil.encrypt(JSON .stringify({ message : { content : message, from : '${userid}' , to : to, time : getDateFull() }, type : "message" }),aesKey),aesKey }
WebSocket服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 String decrypt ="" ; try { ObjectMapper mapper = new ObjectMapper(); mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" )); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false ); HashMap map = mapper.readValue(_message, HashMap.class); String data = (String) map.get("data" ); String aesKey = (String) map.get("aesKey" ); System.out.println("加密后:" +data); System.out.println("密钥:" +aesKey); decrypt = AesUtil.decrypt(data, aesKey); System.out.println("解密后:" +decrypt); } catch (Exception e) { e.printStackTrace(); }
整体整合测试
1 2 3 4 5 加密后:JSV0// HjCAhPzTf/XQtau0/ 5 w6ucq++gVcSrODlUfQdHGvgyV9oilqlyeB/JxVa/ cCCxg6ML6XQ8+TYVwaF1qYHWfWc8BWRT7y2MovFGpgu+t+zuUCNIk/ExErp8ToACSXA1P+aF0NbK9TFn8aXcu9NGJl988A9Yn/ 74 l+ia9llcQjA2ocNVkXn1k/tRPEHJs7yslO6VtY5aql774MulL03Sq99XH1mWo52y2JygMS3RXf23G6lKZxzjhjbY5xi59pUt4wk1SWMeqUdyb7bGlEq+aUvnnKqn/ C3SRDbmrjg= 密钥:juLvarG4elTqa8zc 解密后:{"message" :{"content" :"<p>鸡你太美~</p><p><img src=\"/upload/6edbabcc-ab55-4195-a730-376b62dc50f4.jpeg\" style=\"max-width:100%;\"></p>" ,"from" :"admin" ,"to" :"" ,"time" :"2019-09-02 11:25:38" },"type" :"message" }
2019年9月2日 WebSocket结合AES加密算法实现加密聊天完成 参考 高级加密标准 -维基百科
漫画解读:什么是AES算法
一文读懂对称加密算法、非对称加密算法和 Hash 算法
WebSocket数据加密