一、現代加密體系的概覽
二、加密算法介紹
關於編碼和解碼,正常的編碼是指將字符串轉化為特定的編碼的二進制,例如,java在內存中使用unicode碼,在向外發送數據時,使用不同的編碼會將同樣的字符串編碼成不同的二進制碼,如utf-8或gbk。二解碼,則是將二進制碼根據指定的編碼方式,轉化成字符串。
base64編碼,跟常用的編碼方式是相反的運作,它的編碼,是將二進制碼轉化成字符串;解碼則是將字符串轉化成二進制碼。
1、base64位加密
base64中64是指2的6次方,在編碼時,會使用6位二進制碼表示一個字符。
base64編碼表:
如圖實例:字符串one使用ascii編碼后的二進制碼,在base64編碼后是b25l。
base64編碼,使用6位二進制進行編碼,而一個字節有8位二進制。為了能對所有的字節進行編碼,且不造成更多的base64字符。采用8和6的最小公倍數,24位二進制位一個單位,也就是說base64編碼后的二進制個數是24的整倍數。
當有空出的字節數時:
對不能填滿6位的二進制補0,完全空出的6位二進制用=代替。如下圖:onex的base64編碼是b25lfA==
base64加密實現——java:
在java的加密體系中,常規加密有三種支持,jdk自帶的相關包,bouncy Castle對jdk的補充提供更強的加密強度,Commons Codec是apache提供的安全支持,它一般只是對jdk實現的擴展,比如jdk實現md2和md5算法,Commons Codec也只有md2和md5的實現。實際使用中可以根據需要選擇bouncy Castle或Commons Codec。
package com.tsinkai.ettp.practice; import java.io.IOException; import org.apache.commons.codec.binary.Base64; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class Base64Pra { private static String src = "hello world!"; public static void main(String[] args) { jdkBase64(); commonsCodesBase64(); bouncyCastleBase64(); } /** * jdk自帶的base64編碼,需要在eclipse里打開相應的包,jdk內部使用,不建議使用。 */ public static void jdkBase64() { try { //編碼 BASE64Encoder encoder = new BASE64Encoder(); String encodeStr = encoder.encode(src.getBytes()); System.out.println("jdk encode:" + encodeStr); //解碼 BASE64Decoder decoder = new BASE64Decoder(); System.out.println("jdk decode:"+new String(decoder.decodeBuffer(encodeStr))); } catch (IOException e) { e.printStackTrace(); } } /** * commonsCodes中的base64編碼 */ public static void commonsCodesBase64() { //編碼 byte[] encodeBytes = Base64.encodeBase64(src.getBytes()); String baseStr = Base64.encodeBase64String(src.getBytes()); System.out.println("cc encode:" + new String(encodeBytes)+"||"+baseStr); //解碼 byte[] decodeBytes = Base64.decodeBase64(encodeBytes); System.out.println("cc decode:" + new String(decodeBytes)); } /** * bouncyCastle中的base64編碼 */ public static void bouncyCastleBase64() { //編碼 byte[] encodeBatys = org.bouncycastle.util.encoders.Base64.encode(src.getBytes()); System.out.println("bc encode:" + new String(encodeBatys)); //解碼 System.out.println("bc decode:" + new String(org.bouncycastle.util.encoders.Base64.decode(encodeBatys))); } }
2、消息摘要算法
消息摘要算法是一種驗證消息完整性的算法,它通過一個單向hash加密函數,把字符串加密成固定長度的bit位,不同的字符串產生的輸出不同,同樣的字符串輸出一定相同。同時它是單向加密,不能解密。但是,消息摘要有一定的碰撞率,即不同的明文可能產生相同的密文。
在驗證消息完整性時,只需要將明文字符串使用相同的摘要算法加密,跟接收到的密文進行對比,即可驗證消息摘要的完整性。它是數字簽名的核心算法。
MD5和SHA1的算法已經被破解。
消息摘要的三類實現:
MD(Message Digest)
SHA(Secure Hash Algorithm)
MAC(Message Authentication Code)
a、MD算法
MD算法的實現——java:
package com.tsinkai.ettp.practice; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Security; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.MD2Digest; import org.bouncycastle.crypto.digests.MD4Digest; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class MDPra { private static String src = "hello world"; public static void main(String[] args) { jdkMD2(); jdkMD5(); bcMD2(); bcMD4(); bcMD5ForProvider(); bcMD4ForProvider(); ccMD2(); ccMD5(); } //JDK實現MD2 public static void jdkMD2() { try { MessageDigest md = MessageDigest.getInstance("MD2"); byte[] md2bytes = md.digest(src.getBytes()); //字節數組輸出要先轉成16進制,可以借助HEX類轉換 System.out.println("JDK MD2:" + Hex.encodeHexString(md2bytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } //JDK實現MD5 public static void jdkMD5() { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] md5bytes = md.digest(src.getBytes()); //字節數組輸出要先轉成16進制,可以借助HEX類轉換 System.out.println("JDK MD5:" + Hex.encodeHexString(md5bytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } //Bouncy Castle實現MD2 public static void bcMD2() { Digest digest = new MD2Digest(); digest.update(src.getBytes(), 0, src.getBytes().length); byte[] MD2Bytes = new byte[digest.getDigestSize()]; digest.doFinal(MD2Bytes, 0); System.out.println("BC MD2:" + org.bouncycastle.util.encoders.Hex.toHexString(MD2Bytes)); } //Bouncy Castle實現MD4 public static void bcMD4() { Digest digest = new MD4Digest(); digest.update(src.getBytes(), 0, src.getBytes().length); byte[] MD4Bytes = new byte[digest.getDigestSize()]; digest.doFinal(MD4Bytes, 0); System.out.println("BC MD4:" + org.bouncycastle.util.encoders.Hex.toHexString(MD4Bytes)); } //通過設置provider,使用jdk的包調用bouncyCastle的實現,MD5 //該種方式的provider是sun,若是使用該方式調用MD4,則實現則是BC public static void bcMD5ForProvider() { try { Security.addProvider(new BouncyCastleProvider()); MessageDigest md = MessageDigest.getInstance("MD5"); System.err.println("JDKforProvider MD5:"+md.getProvider()); byte[] md5bytes = md.digest(src.getBytes()); //字節數組輸出要先轉成16進制,可以借助HEX類轉換 System.out.println("BC MD5ForProvider:" + Hex.encodeHexString(md5bytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } //通過設置provider,使用jdk的包調用bouncyCastle的實現,MD4 public static void bcMD4ForProvider() { try { Security.addProvider(new BouncyCastleProvider()); MessageDigest md = MessageDigest.getInstance("MD4"); System.err.println("JDKforProvider MD4:"+md.getProvider()); byte[] md5bytes = md.digest(src.getBytes()); //字節數組輸出要先轉成16進制,可以借助HEX類轉換 System.out.println("BC MD4ForProvider:" + Hex.encodeHexString(md5bytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } //Commons Codes實現MD2 public static void ccMD2() { System.out.println("CC MD2:"+ DigestUtils.md2Hex(src.getBytes())); } //Commons Codes實現MD5 public static void ccMD5() { System.out.println("CC MD5:"+ DigestUtils.md5Hex(src.getBytes())); } }
b.SHA算法
相比於md5,相近的字符串加密后的密文區別很大。
sha共有5中算法,一般般sha-1外的算法稱為sha-2
算法 | 摘要長度 | 實現方 |
SHA-1 | 160 | JDK、CC、BC |
SHA-224 | 224 | BC |
SHA-256 | 256 | JDK、CC、BC |
SHA-384 | 384 | JDK、CC、BC |
SHA-512 | 512 | JDK、CC、BC |
package com.tsinkai.ettp.practice; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Security; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA224Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA3Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.jcajce.provider.digest.SHA3; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class SHAPra { private static String src = "hello world!"; public static void main(String[] args) { jdkSHA1(); bcSHA(); ccSha(); } /** * jdk實現方式 */ public static void jdkSHA1() { try { //getInstance("SHA-1")該參數可填列表:MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 //填寫SHA等同於SHA-1 MessageDigest md = MessageDigest.getInstance("SHA");//默認調用jdk實現:sun.security.provider.SHA // MessageDigest md = MessageDigest.getInstance("SHA224");//默認調用bc實現:org.bouncycastle.jcajce.provider.digest.SHA224 // MessageDigest md = MessageDigest.getInstance("SHA256");//默認調用bc實現:org.bouncycastle.jcajce.provider.digest.SHA256 // MessageDigest md = MessageDigest.getInstance("SHA384");//默認調用bc實現:org.bouncycastle.jcajce.provider.digest.SHA384 // MessageDigest md = MessageDigest.getInstance("SHA512");//默認調用bc實現:org.bouncycastle.jcajce.provider.digest.SHA512 // md.update(src.getBytes());//使用update后后續方法不需要再添加字節數組 System.out.println("jdk SHA-1:" + Hex.encodeHexString(md.digest(src.getBytes()))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } /** * Bouncy Castle 實現方式 */ public static void bcSHA() { Digest digest = new SHA1Digest(); // Digest digest = new SHA224Digest(); // Digest digest = new SHA256Digest(); // Digest digest = new SHA384Digest(); // Digest digest = new SHA512Digest(); digest.update(src.getBytes(),0,src.getBytes().length); System.out.println(digest.getDigestSize()); byte[] sha1Bytes = new byte[digest.getDigestSize()]; digest.doFinal(sha1Bytes, 0); System.out.println("bc sha-1:" + org.bouncycastle.util.encoders.Hex.toHexString(sha1Bytes)); } /** * Commons condc實現方式 */ public static void ccSha() { System.out.println("cc sha1:" + DigestUtils.sha1Hex(src));//改方法底層仍是調用MessageDigest.getInstance("SHA-1"); System.out.println("CC SHA256:"+DigestUtils.sha256Hex(src)); System.out.println("CC SHA384:"+DigestUtils.sha384Hex(src)); System.out.println("CC SHA512:"+DigestUtils.sha512Hex(src)); } }
c、mac——帶有秘鑰的摘要算法
package com.tsinkai.ettp.practice; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; public class HMacPra { public static String src = "hello world"; public static void main(String[] args) { jdkHMacMD5(); bcHMacMD5(); } public static void jdkHMacMD5() { try { //獲得jdk默認實現的秘鑰 KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5"); SecretKey secretKey = keyGenerator.generateKey(); byte[] key = secretKey.getEncoded(); //可以自定義生成,必須是偶數位 // byte[] key = Hex.decodeHex(new char[] {'a','a','a','a','a','a','a','a'}); SecretKey restreSecretKey = new SecretKeySpec(key, "HmacMD5");//該步驟是為了生成一個符合HMacMD5的秘鑰 Mac mac = Mac.getInstance(restreSecretKey.getAlgorithm()); mac.init(restreSecretKey); byte[] hmacMD5Bytes = mac.doFinal(src.getBytes()); System.out.println("jdk hmacMD5:"+Hex.encodeHexString(hmacMD5Bytes)); } catch (Exception e) { e.printStackTrace(); } } public static void bcHMacMD5() { HMac hmac = new HMac(new MD5Digest()); hmac.init(new KeyParameter(org.bouncycastle.util.encoders.Hex.decode("123456"))); hmac.update(src.getBytes(),0,src.getBytes().length); byte[] hMacMD5bytes = new byte[hmac.getMacSize()]; hmac.doFinal(hMacMD5bytes, 0); System.out.println("bc hmacMD5:" + Hex.encodeHexString(hMacMD5bytes)); } }
3、對稱加密算法
加密和解密使用相同的秘鑰。
a、DES加密
package com.tsinkai.ettp.practice; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class DESPra { public static String src = "hello"; public static void main(String[] args) { jdkDES(); bcDES(); } public static void jdkDES() { try { //生成key KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); keyGenerator.init(56); SecretKey secretKey = keyGenerator.generateKey(); byte[] key = secretKey.getEncoded(); //轉化key DESKeySpec desKeySpec = new DESKeySpec(key); SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); SecretKey convertSecretKey = factory.generateSecret(desKeySpec); //加密 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("jdk DES encrypt:" + Base64.encodeBase64String(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println("jdk des decrypy:" + new String(result)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void bcDES() { //2種方式1、使用setprovider方式,2、使用bc原生的方式 try { Security.addProvider(new BouncyCastleProvider()); //生成key KeyGenerator keyGenerator = KeyGenerator.getInstance("DES","BC");//通過參數指定使用provider System.out.println(keyGenerator.getProvider()); keyGenerator.init(56); SecretKey secretKey = keyGenerator.generateKey(); byte[] key = secretKey.getEncoded(); //轉化key DESKeySpec desKeySpec = new DESKeySpec(key); SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); SecretKey convertSecretKey = factory.generateSecret(desKeySpec); //加密 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("bc DES encrypt:" + Base64.encodeBase64String(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println("bc des decrypy:" + new String(result)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
b、AES加密
package com.tsinkai.ettp.practice; import java.security.Key; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; public class AESPra { public static String src = "hello"; public static void main(String[] args) { jdkAES(); } public static void jdkAES() { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); System.out.println("key0:" + Base64.encodeBase64String(keyBytes)); //key轉化 Key key = new SecretKeySpec(keyBytes, "AES"); System.out.println("key:" + Base64.encodeBase64String(key.getEncoded())); //加密 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("加:" + result.length); System.out.println("jdk AES encrypt:"+Base64.encodeBase64String(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, key); System.out.println("解:"+Base64.decodeBase64(Base64.encodeBase64String(result)).length); result = cipher.doFinal(Base64.decodeBase64(Base64.encodeBase64String(result))); System.out.println("jdk AES decrypt:" + new String(result)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }