加密和解密
目錄介紹
- 1.加密和解密
- 1.1 問答環節
- 1.2 加解密概況
- 2.對稱加密和非對稱加密
- 2.1 什么是對稱加密
- 2.2 什么是非對稱加密
- 2.3 常見對稱加密有什么
- 2.4 常見非對稱加密有什么
- 2.5 非對稱加密用途
- 3.關于單向加密
- 3.1 MD加密
- 3.2 什么叫做加鹽
- 4.加密和解密代碼展示
- 4.1 DES加密和解密
- 4.2 AES解密和解密
- 5.RSA非對稱加解密
- 5.1 公鑰加密,私鑰解密
- 5.2 私鑰加密,公鑰解密
- 5.3 完整工具類代碼
- 5.4 注意RSA加密填充方式
- 5.5 RSA加密內容長度限制問題
- 5.6 加解密效率測試
- 5.7 分段加密解密工具類代碼
好消息
- 博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!轉載請注明出處,謝謝!
- github.com/yangchong21…
1.加密和解密
1.1 問答環節
- 1.1.1 常見的加密和解密有哪些?
- 1.1.2 MD5加密是屬于什么類型?是否可逆,以及有哪些應用場景?
- 1.1.3 實際開發中有哪些加解密的應用場景?
1.2 加解密概況
- 1)在對稱加密算法中,雙方使用的密鑰相同,要求解密方事先必須知道加密密鑰。這類加密算法技術較為成熟,加密效率高。
- 2)在非對稱加密算法中,收發雙方使用不同的密鑰,發方使用公開密鑰對消息進行加密,收發使用私有密鑰機型解密,保密性更高,但效率更低。
- 3)單向加密算法在加密過程中不需要使用密鑰,輸入明文后由算法直接加密成密文,密文無法解密,只有重新輸入密文,并經過同樣的加密算法處理,得到形同的密文并被系統重新識別后,才能真正的解密,這種算法非常復雜,通常只在數據量有限的情形下使用,如廣泛應用在計算機系統中的口令加密等。
2.對稱加密和非對稱加密
2.1 什么是對稱加密
- 密鑰可以自己指定,只有一把密鑰,如果密鑰暴露,文件就會被暴露
- 特點是加密速度很快,但是缺點是安全性較低,因為只要密鑰暴漏,數據就可以被解密了。
- 一句話概括:加密和解密都是用相同密鑰
2.2 什么是非對稱加密
- 有兩把鑰匙(密鑰對),公鑰和私鑰,公鑰的話給別人,私鑰自己保存
- 把密鑰通常是通過程序生成,不能自己指定
- 特點是加密速度慢些,但是安全系數很高
- 加密和解密的規則是:公鑰加密只能私鑰解密,私鑰加密只能公鑰解密
- 應用場景舉例:在集成支付寶支付sdk時,需要生成私鑰和公鑰,公鑰需要設置到支付寶網站的管理后臺,在程序中調用支付接口的時候,使用我們自己的私鑰進行加密,這樣支付寶由于有公鑰可以解密,其他人即時劫持了數據,但是沒有公鑰,也無法解密。
2.3 常見對稱加密有什么
- DES算法(Data Encryption Standard)
- AES算法(Advanced Encryption Standard)
2.4 常見非對稱加密有什么
- RSA
2.5 非對稱加密用途
- 身份認證
- 一條加密信息若能用A 的公鑰能解開,則該信息一定是用A 的私鑰加密的,該能確定該用戶是A。
- 陌生人通信
- A 和B 兩個人互不認識,A把自己的公鑰發給B,B也把自己的公鑰發給A,則雙方可以通過對方的公鑰加密信息通信。C 雖然也能得到A、B 的公鑰,但是他解不開密文。
- 支付寶支付加密
- 具體可以參考支付寶sdk的官方demo
3.關于單向加密
- MD5
- 關于MD5加密的工具類代碼如下所示
4.加密和解密代碼展示
4.1 DES加密和解密【對稱加密】
-
加密過程
- 需要加密的內容,也就是明文;然后需要密鑰。最后通過工具類加密得到加密后的密文
-
解密過程
- 利用加密后的密文,與設置的key,也就是密鑰,則可以解密得到加密的內容
-
Android中實現DES加密和解密的核心代碼如下
4.2 AES解密和解密【對稱加密】
public class AES {private static final String Algorithm = "AES";private final static String HEX = "0123456789ABCDEF";//加密函數,key為密鑰public static String encrypt(String key, String src) throws Exception {byte[] rawKey = getRawKey(key.getBytes());byte[] result = encrypt(rawKey, src.getBytes());return toHex(result);}//解密函數。key值必須和加密時的key一致public static String decrypt(String key, String encrypted) throws Exception {byte[] rawKey = getRawKey(key.getBytes());byte[] enc = toByte(encrypted);byte[] result = decrypt(rawKey, enc);return new String(result);}private static void appendHex(StringBuffer sb, byte b) {sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));}private static byte[] getRawKey(byte[] seed) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance(Algorithm);// SHA1PRNG 強隨機種子算法, 要區別Android 4.2.2以上版本的調用方法SecureRandom sr = null;if (android.os.Build.VERSION.SDK_INT >= 17) {sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");} else {sr = SecureRandom.getInstance("SHA1PRNG");}sr.setSeed(seed);kgen.init(256, sr); // 256位或128位或192位SecretKey skey = kgen.generateKey();byte[] raw = skey.getEncoded();return raw;}private static byte[] encrypt(byte[] key, byte[] src) throws Exception {SecretKeySpec skeySpec = new SecretKeySpec(key, Algorithm);Cipher cipher = Cipher.getInstance(Algorithm);cipher.init(Cipher.ENCRYPT_MODE, skeySpec);byte[] encrypted = cipher.doFinal(src);return encrypted;}private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception {SecretKeySpec skeySpec = new SecretKeySpec(key, Algorithm);Cipher cipher = Cipher.getInstance(Algorithm);cipher.init(Cipher.DECRYPT_MODE, skeySpec);byte[] decrypted = cipher.doFinal(encrypted);return decrypted;}private static byte[] toByte(String hexString) {int len = hexString.length() / 2;byte[] result = new byte[len];for (int i = 0; i < len; i++) {result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();}return result;}private static String toHex(byte[] buf) {if (buf == null) {return "";}StringBuffer result = new StringBuffer(2 * buf.length);for (int i = 0; i < buf.length; i++) {appendHex(result, buf[i]);}return result.toString();}} 復制代碼5.RSA非對稱加解密
5.1 公鑰加密,私鑰解密
-
第一步:獲取隨機的公鑰和私鑰
- 代碼如下所示:
-
第二步:公鑰加密
- 代碼如下所示:
-
第三步:私鑰解密
//使用私鑰進行解密 try {byte[] bytes = RSA.decryptByPrivateKey(this.bytes, privateKey.getEncoded());String s = new String(bytes);Log.e("加密和解密", s);//解密后得到的數據:yangchong } catch (Exception e) {e.printStackTrace(); } 復制代碼
5.2 私鑰加密,公鑰解密
-
第一步:獲取隨機的公鑰和私鑰
- 代碼如下所示:
-
第二步:私鑰加密
- 代碼如下所示:
-
第三步:公鑰解密
//使用公鑰解密 try {byte[] bytes = RSA.decryptByPublicKey(this.bytes1, publicKey.getEncoded());String s = new String(bytes);Log.e("加密和解密", s);//解密后得到的數據:yangchong } catch (Exception e) {e.printStackTrace(); } 復制代碼
5.3 完整工具類代碼
- 代碼如下所示:public class RSA {public static final String RSA = "RSA";// 非對稱加密密鑰算法public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式/*** 隨機生成RSA密鑰對** @param keyLength 密鑰長度,范圍:512~2048* 一般1024* @return*/public static KeyPair generateRSAKeyPair(int keyLength) {try {KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);kpg.initialize(keyLength);return kpg.genKeyPair();} catch (NoSuchAlgorithmException e) {e.printStackTrace();return null;}}/*-------------------------------------------------------------------------------------------------*//*** 用公鑰對字符串進行加密* @param data 原文* @param publicKey 密鑰* @return byte[] 解密數據*/public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {// 得到公鑰X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);KeyFactory kf = KeyFactory.getInstance(RSA);PublicKey keyPublic = kf.generatePublic(keySpec);// 加密數據Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);cp.init(Cipher.ENCRYPT_MODE, keyPublic);return cp.doFinal(data);}/*** 私鑰加密** @param data 待加密數據* @param privateKey 密鑰* @return byte[] 解密數據*/public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {// 得到私鑰PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);KeyFactory kf = KeyFactory.getInstance(RSA);PrivateKey keyPrivate = kf.generatePrivate(keySpec);// 數據加密Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);return cipher.doFinal(data);}/*** 公鑰解密** @param data 待解密數據* @param publicKey 密鑰* @return byte[] 解密數據*/public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {// 得到公鑰X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);KeyFactory kf = KeyFactory.getInstance(RSA);PublicKey keyPublic = kf.generatePublic(keySpec);// 數據解密Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);cipher.init(Cipher.DECRYPT_MODE, keyPublic);return cipher.doFinal(data);}/*** 使用私鑰進行解密* @param encrypted 待解密數據* @param privateKey 密鑰* @return byte[] 解密數據* @throws Exception 異常*/public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {// 得到私鑰PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);KeyFactory kf = KeyFactory.getInstance(RSA);PrivateKey keyPrivate = kf.generatePrivate(keySpec);// 解密數據Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);cp.init(Cipher.DECRYPT_MODE, keyPrivate);byte[] arr = cp.doFinal(encrypted);return arr;}} 復制代碼
5.4 注意RSA加密填充方式
- 之前以為上面操作就能實現rsa加解密,以為可以呢,但是遇到Android這邊加密過的數據,服務器端死活解密不了,原來android系統的RSA實現是"RSA/None/NoPadding",而標準JDK實現是"RSA/None/PKCS1Padding",這造成了在android機上加密后無法在服務器上解密的原因,所以在實現的時候這個一定要注意這個問題。
5.5 RSA加密內容長度限制問題
- RSA非對稱加密內容長度有限制,1024位key的最多只能加密127位數據,否則就會報錯(javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes)
- RSA 算法規定:待加密的字節數不能超過密鑰的長度值除以 8 再減去 11(即:KeySize / 8 - 11),而加密后得到密文的字節數,正好是密鑰的長度值除以 8(即:KeySize / 8)。
5.6 加解密效率測試
-
第一步:獲取隨機的公鑰和私鑰
- 代碼如下所示:
-
第二步:用公鑰對對象進行加密
- 代碼如下所示:
-
第三步:使用私鑰進行解密
//使用私鑰進行解密 long start2 = System.currentTimeMillis(); byte[] decryptBytes= new byte[0]; try {decryptBytes = RSA.decryptByPrivateKeyForSpilt(encryptBytes,privateKey.getEncoded()); } catch (Exception e) {e.printStackTrace(); } String decryStr = new String(decryptBytes); long end2 =System.currentTimeMillis(); Log.e("YC","私鑰解密耗時 cost time---->"+(end2-start2)); Log.e("YC","解密后數據 --1-->"+decryStr); 復制代碼 -
第四步:加密和解密效率比較
- 如下所示
- 加密后數據大小的變化:數據量差不多是加密前的1.6倍
- 經過幾次測試可知,解密要比加密費時間多,所以一般建議在服務端做解密的邏輯操作
5.7 分段加密解密工具類代碼
- 代碼如下所示//秘鑰默認長度 private static final int DEFAULT_KEY_SIZE = 2048; // 當前秘鑰支持加密的最大字節數 private static final int DEFAULT_BUFFER_SIZE = (DEFAULT_KEY_SIZE / 8) - 11; // 當要加密的內容超過bufferSize,則采用partSplit進行分塊加密 private static final byte[] DEFAULT_SPLIT = "#PART#".getBytes();/*** 用公鑰對字符串進行分段加密* @param data 需要加密數據* @param publicKey 公鑰* @return byte[] 加密數據* @throws Exception 異常* https://github.com/yangchong211*/ public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception {int dataLen = data.length;if (dataLen <= DEFAULT_BUFFER_SIZE) {return encryptByPublicKey(data, publicKey);}List<Byte> allBytes = new ArrayList<>(2048);int bufIndex = 0;int subDataLoop = 0;byte[] buf = new byte[DEFAULT_BUFFER_SIZE];for (int i = 0; i < dataLen; i++) {if (buf != null) {buf[bufIndex] = data[i];}if (++bufIndex == DEFAULT_BUFFER_SIZE || i == dataLen - 1) {subDataLoop++;if (subDataLoop != 1) {for (byte b : DEFAULT_SPLIT) {allBytes.add(b);}}byte[] encryptBytes = encryptByPublicKey(buf, publicKey);for (byte b : encryptBytes) {allBytes.add(b);}bufIndex = 0;if (i == dataLen - 1) {buf = null;} else {buf = new byte[Math.min(DEFAULT_BUFFER_SIZE, dataLen - i - 1)];}}}byte[] bytes = new byte[allBytes.size()];int i = 0;for (Byte b : allBytes) {bytes[i++] = b;}return bytes; }/*** 用秘鑰對字符串進行分段加密** @param data 要加密的原始數據* @param privateKey 秘鑰* @return byte[] 加密數據* @throws Exception 異常* https://github.com/yangchong211*/ public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception {int dataLen = data.length;if (dataLen <= DEFAULT_BUFFER_SIZE) {return encryptByPrivateKey(data, privateKey);}List<Byte> allBytes = new ArrayList<Byte>(2048);int bufIndex = 0;int subDataLoop = 0;byte[] buf = new byte[DEFAULT_BUFFER_SIZE];for (int i = 0; i < dataLen; i++) {if (buf != null) {buf[bufIndex] = data[i];}if (++bufIndex == DEFAULT_BUFFER_SIZE || i == dataLen - 1) {subDataLoop++;if (subDataLoop != 1) {for (byte b : DEFAULT_SPLIT) {allBytes.add(b);}}byte[] encryptBytes = encryptByPrivateKey(buf, privateKey);for (byte b : encryptBytes) {allBytes.add(b);}bufIndex = 0;if (i == dataLen - 1) {buf = null;} else {buf = new byte[Math.min(DEFAULT_BUFFER_SIZE, dataLen - i - 1)];}}}byte[] bytes = new byte[allBytes.size()];int i = 0;for (Byte b : allBytes) {bytes[i++] = b;}return bytes; }/*** 用公鑰分段解密** @param encrypted 待解密數據* @param publicKey 公鑰* @return byte[] 解密數據* @throws Exception 異常* https://github.com/yangchong211*/ public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception {int splitLen = DEFAULT_SPLIT.length;if (splitLen <= 0) {return decryptByPublicKey(encrypted, publicKey);}int dataLen = encrypted.length;List<Byte> allBytes = new ArrayList<Byte>(1024);int latestStartIndex = 0;for (int i = 0; i < dataLen; i++) {byte bt = encrypted[i];boolean isMatchSplit = false;if (i == dataLen - 1) {// 到data的最后了byte[] part = new byte[dataLen - latestStartIndex];System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);byte[] decryptPart = decryptByPublicKey(part, publicKey);for (byte b : decryptPart) {allBytes.add(b);}latestStartIndex = i + splitLen;i = latestStartIndex - 1;} else if (bt == DEFAULT_SPLIT[0]) {// 這個是以split[0]開頭if (splitLen > 1) {if (i + splitLen < dataLen) {// 沒有超出data的范圍for (int j = 1; j < splitLen; j++) {if (DEFAULT_SPLIT[j] != encrypted[i + j]) {break;}if (j == splitLen - 1) {// 驗證到split的最后一位,都沒有break,則表明已經確認是split段isMatchSplit = true;}}}} else {// split只有一位,則已經匹配了isMatchSplit = true;}}if (isMatchSplit) {byte[] part = new byte[i - latestStartIndex];System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);byte[] decryptPart = decryptByPublicKey(part, publicKey);for (byte b : decryptPart) {allBytes.add(b);}latestStartIndex = i + splitLen;i = latestStartIndex - 1;}}byte[] bytes = new byte[allBytes.size()];int i = 0;for (Byte b : allBytes) {bytes[i++] = b;}return bytes; }/*** 使用私鑰分段解密** @param encrypted 待解密數據* @param privateKey 私鑰* @return byte[] 解密數據* @throws Exception 異常* https://github.com/yangchong211*/ public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception {int splitLen = DEFAULT_SPLIT.length;if (splitLen <= 0) {return decryptByPrivateKey(encrypted, privateKey);}int dataLen = encrypted.length;List<Byte> allBytes = new ArrayList<Byte>(1024);int latestStartIndex = 0;for (int i = 0; i < dataLen; i++) {byte bt = encrypted[i];boolean isMatchSplit = false;if (i == dataLen - 1) {// 到data的最后了byte[] part = new byte[dataLen - latestStartIndex];System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);byte[] decryptPart = decryptByPrivateKey(part, privateKey);for (byte b : decryptPart) {allBytes.add(b);}latestStartIndex = i + splitLen;i = latestStartIndex - 1;} else if (bt == DEFAULT_SPLIT[0]) {// 這個是以split[0]開頭if (splitLen > 1) {if (i + splitLen < dataLen) {// 沒有超出data的范圍for (int j = 1; j < splitLen; j++) {if (DEFAULT_SPLIT[j] != encrypted[i + j]) {break;}if (j == splitLen - 1) {// 驗證到split的最后一位,都沒有break,則表明已經確認是split段isMatchSplit = true;}}}} else {// split只有一位,則已經匹配了isMatchSplit = true;}}if (isMatchSplit) {byte[] part = new byte[i - latestStartIndex];System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);byte[] decryptPart = decryptByPrivateKey(part, privateKey);for (byte b : decryptPart) {allBytes.add(b);}latestStartIndex = i + splitLen;i = latestStartIndex - 1;}}byte[] bytes = new byte[allBytes.size()];int i = 0;for (Byte b : allBytes) {bytes[i++] = b;}return bytes; } 復制代碼
關于其他內容介紹
01.關于博客匯總鏈接
- 1.技術博客匯總
- 2.開源項目匯總
- 3.生活博客匯總
- 4.喜馬拉雅音頻匯總
- 5.其他匯總
02.關于我的博客
- 我的個人站點:www.yczbj.org,www.ycbjie.cn
- github:github.com/yangchong21…
- 知乎:www.zhihu.com/people/yang…
- 簡書:www.jianshu.com/u/b7b2c6ed9…
- csdn:my.csdn.net/m0_37700275
- 喜馬拉雅聽書:www.ximalaya.com/zhubo/71989…
- 開源中國:my.oschina.net/zbj1618/blo…
- 泡在網上的日子:www.jcodecraeer.com/member/cont…
- 郵箱:yangchong211@163.com
- 阿里云博客:yq.aliyun.com/users/artic… 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:segmentfault.com/u/xiangjian…
總結
- 上一篇: 《程序设计与数据结构》第四周学习总结
- 下一篇: 【洛谷 T47488】 D:希望 (点分