常用的安全算法-数字摘要、对称加密、非对称加密详解
本文仅介绍摘要算法、对称加密算法、非对称加密算法的使用场景和使用方法。
1.数字摘要
说明:数字摘要是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单向Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。
1.1 MD5
MD5 即 Message Digest Algorithm5 (信息摘要算法5),一种数字摘要的实现,摘要长度为128位,由MD4,MD3,MD2改进而来,主要增强了算法复杂度和不可逆性。
基于Java 的MD5算法使用:
public static String md5(String content) throws Exception{ MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes =md.digest(content.getBytes("utf-8")); String md5Str = new BigInteger(1,bytes).toString(16);//16进制编码后 return md5Str; }
1.2 SHA
SHA 全称是Secure Hash Algorithm(安全散列算法),1995年又发布了一个修订版FIPS PUB 180-1 通常称之为SHA-1,是基于MD4算法。是现在公认的最安全的散列算法之一,被广泛使用。
比较MD5: SHA-1算法生成的摘要信息长度为160位,由于摘要信息更长,运算过程更加复杂,相同的硬件上,SHA-1比 MD5更慢,但是更为安全。
基于Java 的SHA算法使用:
public static String sha(String content) throws Exception{ MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] bytes =md.digest(content.getBytes("utf-8")); String shaStr = new BigInteger(1, bytes).toString(16);//16进制编码后 return shaStr; }
1.3 十六进制编码
附加:计算机的计算采用的是二进制的数据表示方法,而十六进制也是数据的一种表示方法,并且可以与二进制进行相互转化,每4位二进制数据对应一位十六进制数据。
同我们日常使用的十进制表示法不同的是,十六进制由0~9 和A~F来进行表示,与十进制对应关系是0~9 对应0~9, A~F对应 10~15。
基于Java 的十六进制编码:
public static String bytes2hex(byte[] bytes){ StringBuilder hex = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; boolean negative = false;// 是否为负数 if (b < 0) negative = true; int inte = Math.abs(b); if(negative) inte = inte | 0x80; //负数会转成正数(最高位的负号变成数值计算),再转16进制 String temp = Integer.toHexString(inte & 0xFF); if(temp.length() == 1){ hex.append("0"); } hex.append(temp.toLowerCase()); } return hex.toString(); }
1.4 Base64 编码
Base64是一种基于64个可打印字符来表示二进制数据的方法,由于2的6次方是64,所以每6位为一个单元,对应某个可打印字符,三个字节有24位,对应4个Base64单元,即3个字节需要用4个可打印字符来表示。
附加:多数人认为 Base64是一种加密算法,并且用做加密算法。而实际情况却并非这样,因为任何人只要得到Base64编码内容,便可通过固定的方法,逆向得出编码之前的信息,Base64算法仅仅只是一种编码算法而已。
基于Java 的Base64编码,需引入commons-codec-***.jar
public static String byte2base64(byte[] bytes){ Base64 base = new Base64(); return base.encodeAsString(bytes); } public static byte[] base642byte(String base64){ Base64 base = new Base64(); return base.decode(base64); }
1.5 彩虹表破解Hash算法
彩虹表(Rainbow Table)法是一种破解哈希算法的技术,从原理上来说能够对任何一种Hash算法进行攻击。简单说,彩虹表就是一张采用各种Hash算法生成的明文和密文的对照表。
本文不做赘述,详细内容可参考:http://www.aiezu.com/system/windows/rainbow-table_knowledge.html
1.6 运行示例
public static void main(String[] args) throws Exception { String content = "hello,world!"; System.out.println("原文:"+content); System.out.println("MD5:"+md5(content)); System.out.println("SHA:"+sha(content)); }
输出
原文:hello,world! MD5:c0e84e870874dd37ed0d164c7986f03a SHA:4518135c05e0706c0a34168996517bb3f28d94b5
2.对称加密算法
2.1 DES算法
DES算法属于对称加密算法,明文按64位进行分组,密钥长64位,事实上只有56位参与DES运算(第8,16,24,32,40,48,56,64位是校验位,使得每个密钥都有奇数个1),分组后的明文和56位的密钥按位替代或交换的方法形成密文。
3DES: 是DES向AES过渡的加密算法,使用3条56位的密钥对数据进行3次加密。
基于Java的DES 算法的使用
/** * 生成DES 密钥 */ public static SecretKey loadKeyDES() throws Exception{ KeyGenerator keyGen = KeyGenerator.getInstance("DES"); keyGen.init(56); SecretKey key = keyGen.generateKey(); String base64Key = byte2base64(key.getEncoded()); byte[] bytes = base642byte(base64Key); key = new SecretKeySpec(bytes,"DES"); return key; } //DES加密 public static byte[] encryptDES(byte[] source,SecretKey key) throws Exception{ Cipher cipher = Cipher.getInstance("DES"); // 用密匙初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, key); // 现在,获取数据并加密 byte[] bytes = cipher.doFinal(source); return bytes; } //DES解密 public static byte[] decryptDES(byte[] source,SecretKey key) throws Exception{ Cipher cipher = Cipher.getInstance("DES"); // 用密匙初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, key); // 真正开始解密操作 byte[] bytes = cipher.doFinal(source); return bytes; } public static void main(String[] args) throws Exception { String context = "Hello,World!";
// 密钥 SecretKey key = loadKeyDES(); byte[] encryptBytes = encryptDES(context.getBytes(),key); System.out.println("加密后:"+new String(encryptBytes)); byte[] decryptBytes = decryptDES(encryptBytes,key); System.out.println("解密后:"+new String(decryptBytes)); }
运行结果如下:
加密后:���9�iP�ﱹp 解密后:Hello,World!
2.2 AES算法
AES算法 全称是Advanced Encryption Standard,即高级加密标准,又称为Rijndael加密算法,是美国联邦政府采用的一种对称加密标准,这个标准用来替代原先的DES算法,目前已成为对称加密算法中最流行的算法之一。
比较DES算法,AES算法作为新一代的数据加密标准,汇聚了强安全性、高性能、高效率、易用和灵活等优点,设计有三个密钥长度(128,192,256位),比DES算法加密强度更高,更加安全。
基于Java的AES算法
/** * 生成AES 密钥 */ public static SecretKey loadKeyAES() throws Exception{ KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128);//DES为56 SecretKey key = keyGen.generateKey(); String base64Key = byte2base64(key.getEncoded()); byte[] bytes = base642byte(base64Key); key = new SecretKeySpec(bytes,"AES"); return key; } //AES加密 public static byte[] encryptAES(byte[] source,SecretKey key) throws Exception{ Cipher cipher = Cipher.getInstance("AES"); // 用密匙初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, key); // 现在,获取数据并加密 byte[] bytes = cipher.doFinal(source); return bytes; } //AES解密 public static byte[] decryptAES(byte[] source,SecretKey key) throws Exception{ Cipher cipher = Cipher.getInstance("AES"); // 用密匙初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, key); // 真正开始解密操作 byte[] bytes = cipher.doFinal(source); return bytes; } public static void main(String[] args) throws Exception { String context = "Hello,World!"; SecretKey key = loadKeyAES(); byte[] encryptBytes = encryptAES(context.getBytes(),key); System.out.println("加密后:"+new String(encryptBytes)); byte[] decryptBytes = decryptAES(encryptBytes,key); System.out.println("解密后:"+new String(decryptBytes)); }
运行结果如下
加密后:T�l�X���2�zEO� 解密后:Hello,World!
3.非对称加密算法+数字签名
非对称加密算法:又称为公开密钥加密算法,需要两个密钥,一个为公开密钥(PublicKey)即公钥,一个为私有密钥(PrivateKey)即私钥。两者需要配对使用。用其中一者加密,则必须用另一者解密。
3.1 RSA 算法 与 数字签名
RSA 是目前最有影响力的非对称加密算法,能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。
RSA 算法是基于一个十分简单的数论事实:将两个大素数想乘十分容易,但反过来想要对其乘积进行因式分解却极其困难。
常见的数字签名有两种:
1.MD5withRSA :采用MD5 算法生成需要发送正文的数字摘要,并且使用RSA算法来对摘要进行加密和解密。
2.SHA1withRSA: 采用SHA-1算法生成正文的数字摘要,并且使用RSA算法来对摘要进行加密和解密。
两者算法的流程完全一致,只是签名算法换成了对应的算法。这里只对MD5withRSA做出代码示例。
基于Java的RSA算法使用
private static final String RSA = "RSA"; private static final String MD5 = "MD5"; private static final String SHA1 = "SHA1"; private static final String MD5withRSA = "MD5withRSA"; private static final String SHA1withRSA = "SHA1withRSA"; public static String byte2base64(byte[] bytes){ Base64 base = new Base64(); return base.encodeAsString(bytes); } public static byte[] base642byte(String base64){ Base64 base = new Base64(); return base.decode(base64); } //初始化KeyPairGenerator ,生成KeyPair public static KeyPair getKeyPair() throws Exception{ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA); keyPairGenerator.initialize(512); KeyPair keypair = keyPairGenerator.generateKeyPair(); return keypair; } //获取公钥 public static String getPublicKey(KeyPair keyPair){ PublicKey publicKey = keyPair.getPublic(); byte[] bytes = publicKey.getEncoded(); return byte2base64(bytes); } //获取私钥 public static String getPrivateKey(KeyPair keyPair){ PrivateKey privateKey = keyPair.getPrivate(); byte[] bytes = privateKey.getEncoded(); return byte2base64(bytes); } //MD5withRSA 签名,可将MD5 替换成 SHA1 数字签名 public static byte[] sign4MD5(byte[] content,PrivateKey privateKey) throws Exception{ MessageDigest md = MessageDigest.getInstance(MD5); byte[] bytes = md.digest(content); Cipher cipher = Cipher.getInstance(RSA); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] encryptBytes = cipher.doFinal(bytes); return encryptBytes; } //MD5withRSA 验证签名,可将MD5 替换成 SHA1 数字签名 public static boolean verify4MD5(byte[] content,byte[] sign, PublicKey publicKey) throws Exception{ MessageDigest md = MessageDigest.getInstance(MD5); byte[] bytes = md.digest(content); Cipher cipher = Cipher.getInstance(RSA); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] decrypeBytes = cipher.doFinal(sign); return byte2base64(decrypeBytes).equals(byte2base64(bytes)); } //基于JAVA的Signature API的使用 将MD5withRSA 替换成 SHA1withRSA 数字签名 public static byte[] sign4Signature(byte[] content,PrivateKey privateKey) throws Exception{ Signature signature = Signature.getInstance(MD5withRSA); signature.initSign(privateKey);; signature.update(content); return signature.sign(); } public static boolean verify4Signature(byte[] content,byte[] sign, PublicKey publicKey) throws Exception{ Signature signature = Signature.getInstance(MD5withRSA); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } public static void main(String[] args) throws Exception { KeyPair keyPair = getKeyPair(); System.out.println("privateKey:"+getPrivateKey(keyPair)); System.out.println("publicKey:"+getPublicKey(keyPair)); String content = "hello,World"; byte[] signMd5 = sign4MD5(content.getBytes(), keyPair.getPrivate()); System.out.println("signMd5:"+new String(signMd5)); System.out.println("verifyMd5:"+verify4MD5(content.getBytes(), signMd5, keyPair.getPublic())); byte[] signSignature = sign4Signature(content.getBytes(), keyPair.getPrivate()); System.out.println("sign4Signature:"+new String(signSignature)); System.out.println("verify4Signature:"+verify4Signature(content.getBytes(), signSignature, keyPair.getPublic())); }
运行结果如下
privateKey:MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAiD/p5rsULwF3CNQNnh0Y3rUd5v7JSTWwt6RpIP6uoucwa1nI9pMU7IUF+PPgrMY3l6KJmfUdLSLx23/1286QYQIDAQABAkAk91pJvJY0TqxyG756GKDnw7ISOyx3jXadzKCtUAwnrDYXK3kyPpYW4Hnovw0bDF2GhFDs1l3DHk4dAfeFDXJxAiEA0FAesbxDSKaRePdlOTloMN6dm9cw3VJRK765TRVP8IUCIQCncKDGNA1OVSWZZicCaXSBuKlej+krjCavtJ/HWur1LQIhALPtsEV2xmsXLKnejnkaA8vKTTpRaeyQ8HmneGiDUxP9AiBTMzbgZfr6onqlZ8oYe7glwVF/qHjPZ7vNcS43zCiWHQIhAK0u7LLh2lzQNZ7eFGyHpsBQae5gfJMxzVY0/AyLvmtb publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIg/6ea7FC8BdwjUDZ4dGN61Heb+yUk1sLekaSD+rqLnMGtZyPaTFOyFBfjz4KzGN5eiiZn1HS0i8dt/9dvOkGECAwEAAQ== signMd5:f��ߜ��L>^�\���o��@���:E�±�X��z���p�d��+����{�?�hg�|Jk verifyMd5:true sign4Signature:��oa̠��eH>:4����W]����?@���?zh���j�4�˰�L��i �h, verify4Signature:true