一、现代加密体系的概览
二、加密算法介绍
关于编码和解码,正常的编码是指将字符串转化为特定的编码的二进制,例如,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(); } } }