原创作者: snowolf   阅读:84791次   评论:39条   更新时间:2011-05-26    
    本篇内容简要介绍BASE64MD5SHAHMAC几种加密算法。
    BASE64编码算法不算是真正的加密算法。
    MD5SHAHMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法,我们称之为单向加密算法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。

BASE64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。



通过java代码实现如下:
	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。

MD5
MD5 -- message-digest algorithm 5 (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。



通过java代码实现如下:
	/**
	 * MD5加密
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptMD5(byte[] data) throws Exception {

		MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
		md5.update(data);

		return md5.digest();

	}


通常我们不直接使用上述MD5加密。通常将MD5产生的字节数组交给BASE64再加密一把,得到相应的字符串。

SHA
SHA(Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。



通过java代码实现如下:
	/**
	 * SHA加密
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA(byte[] data) throws Exception {

		MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
		sha.update(data);

		return sha.digest();

	}
}


HMAC
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。



通过java代码实现如下:

	/**
	 * 初始化HMAC密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String initMacKey() throws Exception {
		KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

		SecretKey secretKey = keyGenerator.generateKey();
		return encryptBASE64(secretKey.getEncoded());
	}

	/**
	 * HMAC加密
	 * 
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptHMAC(byte[] data, String key) throws Exception {

		SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		mac.init(secretKey);

		return mac.doFinal(data);

	}


给出一个完整类,如下:
import java.security.MessageDigest;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 基础加密组件
 * 
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */
public abstract class Coder {
	public static final String KEY_SHA = "SHA";
	public static final String KEY_MD5 = "MD5";

	/**
	 * MAC算法可选以下多种算法
	 * 
	 * <pre>
	 * HmacMD5 
	 * HmacSHA1 
	 * HmacSHA256 
	 * HmacSHA384 
	 * HmacSHA512
	 * </pre>
	 */
	public static final String KEY_MAC = "HmacMD5";

	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

	/**
	 * MD5加密
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptMD5(byte[] data) throws Exception {

		MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
		md5.update(data);

		return md5.digest();

	}

	/**
	 * SHA加密
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptSHA(byte[] data) throws Exception {

		MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
		sha.update(data);

		return sha.digest();

	}

	/**
	 * 初始化HMAC密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String initMacKey() throws Exception {
		KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

		SecretKey secretKey = keyGenerator.generateKey();
		return encryptBASE64(secretKey.getEncoded());
	}

	/**
	 * HMAC加密
	 * 
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptHMAC(byte[] data, String key) throws Exception {

		SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		mac.init(secretKey);

		return mac.doFinal(data);

	}
}


再给出一个测试类:
import static org.junit.Assert.*;

import org.junit.Test;

/**
 * 
 * @author 梁栋
 * @version 1.0
 * @since 1.0
 */
public class CoderTest {

	@Test
	public void test() throws Exception {
		String inputStr = "简单加密";
		System.err.println("原文:\n" + inputStr);

		byte[] inputData = inputStr.getBytes();
		String code = Coder.encryptBASE64(inputData);

		System.err.println("BASE64加密后:\n" + code);

		byte[] output = Coder.decryptBASE64(code);

		String outputStr = new String(output);

		System.err.println("BASE64解密后:\n" + outputStr);

		// 验证BASE64加密解密一致性
		assertEquals(inputStr, outputStr);

		// 验证MD5对于同一内容加密是否一致
		assertArrayEquals(Coder.encryptMD5(inputData), Coder
				.encryptMD5(inputData));

		// 验证SHA对于同一内容加密是否一致
		assertArrayEquals(Coder.encryptSHA(inputData), Coder
				.encryptSHA(inputData));

		String key = Coder.initMacKey();
		System.err.println("Mac密钥:\n" + key);

		// 验证HMAC对于同一内容,同一密钥加密是否一致
		assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(
				inputData, key));

		BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));
		System.err.println("MD5:\n" + md5.toString(16));

		BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));
		System.err.println("SHA:\n" + sha.toString(32));

		BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));
		System.err.println("HMAC:\n" + mac.toString(16));
	}
}


控制台输出:
原文:
简单加密
BASE64加密后:
566A5Y2V5Yqg5a+G

BASE64解密后:
简单加密
Mac密钥:
uGxdHC+6ylRDaik++leFtGwiMbuYUJ6mqHWyhSgF4trVkVBBSQvY/a22xU8XT1RUemdCWW155Bke
pBIpkd7QHg==

MD5:
-550b4d90349ad4629462113e7934de56
SHA:
91k9vo7p400cjkgfhjh0ia9qthsjagfn
HMAC:
2287d192387e95694bdbba2fa941009a



    BASE64的加密解密是双向的,可以求反解。
    MD5、SHA以及HMAC是单向加密,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。
    单向加密的用途主要是为了校验数据在传输过程中是否被修改。
评论 共 39 条 请登录后发表评论
39 楼 huiyuanlujun 2013-08-25 16:47
  [color=orange][/color]
38 楼 xiebo1983 2012-04-16 17:20
hualang 写道
xiebo1983 写道
MD5,SHA是摘要算法,不是加密算法,并不能对信息加密;只是对信息做了摘要,从而在接收方验证信息的完整性。

从《JAVA加密与解密艺术》中,MD5,SHA属于单向加密算法,你把摘要跟加密分的太清了

如果是MD5是单向加密算法,那如何解密?单向加密算法会有一个可倒推的解密算法。
MD5 SHA 没有。他们只是做摘要。
37 楼 hualang 2012-04-16 11:36
xiebo1983 写道
MD5,SHA是摘要算法,不是加密算法,并不能对信息加密;只是对信息做了摘要,从而在接收方验证信息的完整性。

从《JAVA加密与解密艺术》中,MD5,SHA属于单向加密算法,你把摘要跟加密分的太清了
36 楼 xiebo1983 2012-03-25 22:03
MD5,SHA是摘要算法,不是加密算法,并不能对信息加密;只是对信息做了摘要,从而在接收方验证信息的完整性。
35 楼 dushanggaolou 2012-01-05 11:14
从最近的泄密事件来看,加密是必须的,双向加密等于没加密,只是绕了个弯,单向加密才有一定的作用。
34 楼 qin_eye 2011-11-27 12:58
一下子没接受,不过大致懂了,很详细。。
33 楼 marquis 2011-10-09 18:06
最后BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));  这里我没看明白,encryptHMAC这个方法第二个参数不是应该传Mac密钥么?怎么传了原文?
32 楼 zhanghh321 2011-08-09 17:46
看不懂呀
31 楼 tan4836128 2011-08-08 15:53
很详细,通俗易懂,好文章!
30 楼 zssggg 2011-07-11 16:29
介绍的这么详细,感谢楼主。
29 楼 enternalttyy 2011-06-22 13:51
,不错,值得学习~
28 楼 yunnysunny 2011-03-22 16:14
snowolf 写道
lanxiazhi 写道
谢谢,让我知道了还有Mac这样的算法。
名称无所谓,叫哈希 或者 单向加密 都无所谓。
只是提醒一下:
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 
sun.*里面的类不是标准java的一部分,慎用啊。

呵呵,你提到的东西我明白,同时我在我的书中《Java加密与解密的艺术》中给出了相应的替代方案。

您的书我看了,本来想深入研究一下,后来发现,还是当手册用比较好。
27 楼 frankiegao123 2010-12-11 13:16
应用中不应出现 Sun JRE 中以 sun 开头包的类库,详见:

Why Developers Should Not Write Programs That Call 'sun' Packages
http://java.sun.com/products/jdk/faq/faq-sun-packages.html

Sun 已被 Oracle 收购,作为一家很强势的公司是极有可能将 sun.* 和 com.sun.* 改为 oracle.* 和 com.oracle.* 的。
26 楼 frankiegao123 2010-12-11 13:12
Base64 与加密是无关的。其主要用于将二进制数据转换成为可见的文本数据,这样的话就可以在 XML 等基于文本的协议中传送二进制数据了,比如传送个图像什么的,呵呵。

不过我认为,把 MD5、SHA、HMAC 等认为是加密算法欠妥,因为有加密就得有解密。这些算法都归于散列函数之类,在应用上主要是用于验证数据的完整性或者有效性。
25 楼 snowolf 2010-05-06 17:59
littleJava 写道
snowolf 写道
lanxiazhi 写道
谢谢,让我知道了还有Mac这样的算法。
名称无所谓,叫哈希 或者 单向加密 都无所谓。
只是提醒一下:
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 
sun.*里面的类不是标准java的一部分,慎用啊。

呵呵,你提到的东西我明白,同时我在我的书中《Java加密与解密的艺术》中给出了相应的替代方案。

Bouncy Castle 很好的代替方案,而且是Apache licence,功能更强大,使用也很方便,可以代替sun提供的方案

个人认为,单说Base64,Commons Codec更具优势!
24 楼 littleJava 2010-05-06 17:30
snowolf 写道
lanxiazhi 写道
谢谢,让我知道了还有Mac这样的算法。
名称无所谓,叫哈希 或者 单向加密 都无所谓。
只是提醒一下:
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 
sun.*里面的类不是标准java的一部分,慎用啊。

呵呵,你提到的东西我明白,同时我在我的书中《Java加密与解密的艺术》中给出了相应的替代方案。

Bouncy Castle 很好的代替方案,而且是Apache licence,功能更强大,使用也很方便,可以代替sun提供的方案
23 楼 snowolf 2010-04-11 14:28
lanxiazhi 写道
谢谢,让我知道了还有Mac这样的算法。
名称无所谓,叫哈希 或者 单向加密 都无所谓。
只是提醒一下:
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 
sun.*里面的类不是标准java的一部分,慎用啊。

呵呵,你提到的东西我明白,同时我在我的书中《Java加密与解密的艺术》中给出了相应的替代方案。
22 楼 lanxiazhi 2010-04-10 21:50
谢谢,让我知道了还有Mac这样的算法。
名称无所谓,叫哈希 或者 单向加密 都无所谓。
只是提醒一下:
import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 
sun.*里面的类不是标准java的一部分,慎用啊。
21 楼 tjgamejx2 2010-04-01 10:45
这是哈希算法,不是加密算法,加密算法必能解密,作为专业的软件人员不应该弄混他们之间的概念。哈希算法,不可逆,不存在解密的说法,只存在哈希冲突而已,是哈希算法必定有哈希冲突,最出名的MD5也一样。
20 楼 huang4953 2010-02-08 15:43
这些加密跟RSA比小巫见大巫了
19 楼 shrpcn 2010-01-27 12:45
这些不能当加密算法使用吧?  HASH算法, HMAC是密钥交换算法
18 楼 weihairui 2010-01-12 09:42
王小云 在当代密码学上有很高的威望。在山东读大学的时候,曾去过她的实验室,参观了下曾经为她处理大量数学运算的大型机。呵呵
17 楼 snowolf 2009-08-20 14:42
onray 写道
原文:
简单加密
BASE64加密后:
vPK1pbzTw9w=

BASE64解密后:
简单加密
Mac密钥:
34e4yyZ5u5eSFp/ACJthtOcFXlKFNy4aczXRAY6DZA72FhwsyfwgI4efhFuWFbd6WWXKnlG5Q0rA
PPka5PKIoQ==

MD5:
-12d1456f5d947be6eac80fc07900be99
SHA:
-bbbd559051qu2b5pl635l3jj8jrvodsn
HMAC:
358250966fbd6166b94b384105f6894b


一样的结果~~~奇怪

这是中文编码问题!楼上几位使用的是GBK编码,我使用的是UTF-8编码。用GBK编码1个汉字就是2个字节,用UTF-8编码则1个汉字3个字节。转换为二进制再经过base64编码后当然会有所不同了!
16 楼 lfrick 2009-08-14 10:17
谢谢,一直对Java的加解密有疑问!
15 楼 onray 2009-08-10 14:02
原文:
简单加密
BASE64加密后:
vPK1pbzTw9w=

BASE64解密后:
简单加密
Mac密钥:
34e4yyZ5u5eSFp/ACJthtOcFXlKFNy4aczXRAY6DZA72FhwsyfwgI4efhFuWFbd6WWXKnlG5Q0rA
PPka5PKIoQ==

MD5:
-12d1456f5d947be6eac80fc07900be99
SHA:
-bbbd559051qu2b5pl635l3jj8jrvodsn
HMAC:
358250966fbd6166b94b384105f6894b


一样的结果~~~奇怪
14 楼 lingangw 2009-06-29 22:06
我用的也是JDK1.6的,呵呵,环境的问题是不存在的,因为别的代码都是可以顺利的执行的。还有另外的原因的吗?
我是第一次使用JUnit,是因为JUnit的原因吗?



13 楼 snowolf 2009-06-28 21:03
lingangw 写道
我在我本机运行的结果在MAC密钥前和1楼kinkding的运行结果一致。
另外在我的机器上运行的结果中只显示到MAC密钥结果一行,后面的都没有显示出来。
还有运行后:JUnit前面有个错号,test前面也有个错号,也没有什么提示。
不知道是什么原因?

和环境有关吗?我使用的是jdk1.6
12 楼 lingangw 2009-06-28 02:59
我在我本机运行的结果在MAC密钥前和1楼kinkding的运行结果一致。
另外在我的机器上运行的结果中只显示到MAC密钥结果一行,后面的都没有显示出来。
还有运行后:JUnit前面有个错号,test前面也有个错号,也没有什么提示。
不知道是什么原因?
11 楼 chenlixun 2009-06-26 11:54
kinkding 写道
辛苦,在我本机程序运行的结果如下:
原文:
简单加密
BASE64加密后:
vPK1pbzTw9w=

BASE64解密后:
简单加密
Mac密钥:
QIdiQufh2AYOjOVW7TW++PzDFqYKogLzZqB9wxJLMS+JQSAffZeFLJ9TCH23yZwuqBh5aQdAWP7e
LpH/D99kSA==

MD5:
-12d1456f5d947be6eac80fc07900be99
SHA:
-bbbd559051qu2b5pl635l3jj8jrvodsn
HMAC:
358250966fbd6166b94b384105f6894b

可能是JVM的默认编码方式和博主的不一样。


我也有同样的问题,目前没找到原因.
10 楼 snowolf 2009-06-25 22:55
密码学杂凑函数(有时称作消息摘要函数,杂凑函数又称散列函数或哈希函数)陷门函数
trydofor 写道
当看到楼主的系列文章和"加密解密,曾经是我一个毕业设计的重要组件。在工作了多年以后回想当时那个加密、解密算法,实在是太单纯了。"后.
知道碰到行家了,之前的理解,的确有些想当然,认为有加便有解,不知学术上的事,见笑见笑.

虽然我已承认单项加密,并更新了自己的知识库,但我会叫他们散列,并刻意和对称/非对称加密加以区分.

殊途同归,呵呵!
密码学杂凑函数(有时称作消息摘要函数,杂凑函数又称散列函数或哈希函数),陷门函数。叫那个都无所谓!

发表评论

您还没有登录,请您登录后再发表评论

文章信息

  • snowolf在2009-05-25创建
  • snowolf在2011-05-26更新
  • 标签: 单向加密算法
Global site tag (gtag.js) - Google Analytics