一、一点历史
1976年以前,所有的加密方法都是同一种模式:
(1)甲方选择某一种加密规则,对信息进行加密;
(2)乙方使用同一种规则,对信息进行解密。
由于加密和解密使用同样规则(简称"密钥"),这被称为"对称加密算法"(Symmetric-key algorithm)。
这种加密模式有一个最大弱点:甲方必须把加密规则告诉乙方,否则无法解密。保存和传递密钥,就成了最头疼的问题。
1976年,两位美国计算机学家Whitfield Diffie 和 Martin Hellman,提出了一种崭新构思,可以在不直接传递密钥的情况下,完成解密。这被称为"Diffie-Hellman密钥交换算法"。这个算法启发了其他科学家。人们认识到,加密和解密可以使用不同的规则,只要这两种规则之间存在某种对应关系即可,这样就避免了直接传递密钥。
这种新的加密模式被称为"非对称加密算法"。
(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
(2)甲方获取乙方的公钥,然后用它对信息加密。
(3)乙方得到加密后的信息,用私钥解密。
如果公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的。
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。
二、对称加密算法(AES算法)
1. AES算法
对称加密算法中,数据加密和解密采用的都是同一个密钥,因而其安全性依赖于所持有密钥的安全性。
对称加密算法的主要优点是加密和解密速度快,加密强度高,且算法公开。
缺点是实现密钥的秘密分发困难,在大量用户的情况下密钥管理复杂,而且无法完成身份认证等功能,不便于应用在网络开放的环境中。
由于密钥是对称加密解密的核心,密钥本身的安全性要求较高,以AES算法为例,它的密钥长度至少为128位(因此也称
AES-128
。AES-192
和AES-256
自然也知道是什么意思了吧)
一般来说,对于对称加密体制,通常也会包含3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法)
(1) 密钥生成算法
(Key) <— KeyGen(λ) // 密钥生成算法
对于对称加密体制来说(以AES为例),本身并没有定义密钥生成算法,仅仅是要求密钥长度达到要求即可(AES-128),也就是说,通常密钥生成算法是由我们自己实现,只要最终生成的密钥长度达到算法要求的即可。
我们通常会随机生成密钥,如下代码为例:
/**
* 自动生成AES128位密钥
*/
public static void getA221(){
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);//要生成多少位,只需要修改这里即可128, 192或256
SecretKey sk = kg.generateKey();
byte[] b = sk.getEncoded();
String s = byteToHexString(b);
System.out.println(s);
System.out.println("十六进制密钥长度为"+s.length());
System.out.println("二进制密钥的长度为"+s.length()*4);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
System.out.println("没有此算法。");
}
}
/**
* 二进制byte[]转十六进制string
*/
public static String byteToHexString(byte[] bytes){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String strHex=Integer.toHexString(bytes[i]);
if(strHex.length() > 3){
sb.append(strHex.substring(6));
} else {
if(strHex.length() < 2){
sb.append("0" + strHex);
} else {
sb.append(strHex);
}
}
}
return sb.toString();
}
/**
* 十六进制string转二进制byte[]
*/
public static byte[] hexStringToByte(String s) {
byte[] baKeyword = new byte[s.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
System.out.println("十六进制转byte发生错误!!!");
e.printStackTrace();
}
}
return baKeyword;
}
/**
* 使用对称密钥进行加密
*/
public static void getA231() throws Exception{
String keys = "c0e9fcff59ecc3b8b92939a1a2724a44"; //密钥
byte[] keyb = hexStringToByte(keys);
String mingwen = "hello word!"; //明文
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] bjiamihou = cipher.doFinal(mingwen.getBytes());
System.out.println(byteToHexString(bjiamihou)); //加密后数据为ecf0a6bc80dbaf657eac9b06ecd92962
}
/**
* 使用对称密钥进行解密
*/
public static void getA232() throws Exception{
String keys = "c0e9fcff59ecc3b8b92939a1a2724a44"; //密钥
byte[] keyb = hexStringToByte(keys);
String sjiami = "ecf0a6bc80dbaf657eac9b06ecd92962"; //密文
byte[] miwen = hexStringToByte(sjiami);
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
byte[] bjiemihou = cipher.doFinal(miwen);
System.out.println(new String(bjiemihou));
}
(2) 加密算法
(CT) <— Encrypt(Key,M) // 加密算法
加密算法以明文(我们要加密的内容)和密钥作为参数输入,输出密文CT。
这里解释一下什么是可逆:当我们拿到密文,又拿到了密钥,那么是可以推算出原文的,这就是对称加密体制中的解密算法
再多思考一下假如我们拿到了明文,又拿到了密文,我们可以推算出密钥吗?很直白的告诉你,没有办法直接算出密钥,唯一的方式就是穷举,俗称暴力破解。举个生活中的例子:我们知道矿泉水的原材料是水,但是矿泉水并不是简单的将水装到瓶子包装一下就成了矿泉水(农夫三拳除外),是需要经过工厂的很多工序之后,才会生产出一瓶矿泉水,我们能看到原材料-水(原文),我们也能看到产品-矿泉水(密文),但是我们看不到中间的工序(密钥,不为外人所知,只有工厂自己知道)
突然发现矿泉水的这个栗子非常贴切:
矿泉水 <— 工厂工序(水) // 加密算法
水<—倒掉(矿泉水) // 解密算法,我们将矿泉水倒在地上(或者喝掉),最终又变回了水,
// 这里只是想说明矿泉水是能够!!!变回水的,不必太较真矿泉水是怎么!!!变回水的...
(3) 解密算法
(M) <— Decrypt(Key,CT) // 解密算法
解密算法以密钥和密文CT作为参数输入,输出明文M。
这里的栗子可以参照上面矿泉水变为水一例。
那么在这里提个疑问:我们在实际应用中,比如手机和后台交互,数据采用对称加密(对称加密速度快啊),那么这个密钥该如何传递呢?明文传递密钥肯定不合适咯,那么我们该如何安全的传递密钥呢?
Follow Me!Let's go!
三、非对称加密算法(RSA算法)
注意:非对称加密由于非对称体制,可能难于理解,但是只要记住我下面的笔记,你就能很清晰的记住后面我所讲的一些名词。
首先给大家画一张思维导图,这里我推荐一家在线制图网站https://www.processon.com
1. RSA的加密算法(PK加密,SK解密)
我们来回顾一下RSA的加密算法。我们从公钥加密算法和签名算法的定义出发,用比较规范的语言来描述这一算法。
RSA公钥加密体制包含如下3个算法:KeyGen(密钥生成算法),Encrypt(加密算法)以及Decrypt(解密算法)。
(1) 密钥生成算法
[图片上传失败...(image-e1c0cc-1513844367128)]
密钥生成算法以安全常数λ作为输入,输出一个公钥PK,和一个私钥SK。安全常数用于确定这个加密算法的安全性有多高,一般以加密算法使用的质数p的大小有关。λ越大,质数p一般越大,保证体制有更高的安全性。在RSA中,密钥生成算法如下:算法首先随机产生两个不同大质数p和q,计算N=pq。随后,算法计算欧拉函数φ(N)=(p-1)(q-1)。接下来,算法随机选择一个小于φ(N)的整数e,并计算e关于φ(N)的模反元素d。最后,公钥为PK=(N, e),私钥为SK=(N, d)。
(2) 加密算法
加密算法以公钥PK和待加密的消息M作为输入,输出密文CT。在RSA中,加密算法如下:算法直接输出密文为CT = M^e mod N
(3) 解密算法
解密算法以私钥SK和密文CT作为输入,输出消息M。在RSA中,解密算法如下:算法直接输出明文为M = CT^d mod N。由于e和d在φ(N)下互逆,因此我们有:CT^d = M^ed = M mod N
所以,从算法描述中我们也可以看出:公钥用于对数据进行加密,私钥用于对数据进行解密。当然了,这个也可以很直观的理解:公钥就是公开的密钥,其公开了大家才能用它来加密数据。私钥是私有的密钥,谁有这个密钥才能够解密密文。否则大家都能看到私钥,就都能解密,那不就乱套了。
2. RSA的签名算法(SK加密,PK解密)
我们再来回顾一下RSA签名体制。签名体制同样包含3个算法:KeyGen(密钥生成算法),Sign(签名算法),Verify(验证算法)。
(1) 密钥生成算法
密钥生成算法同样以安全常数λ作为输入,输出一个公钥PK和一个私钥SK。在RSA签名中,密钥生成算法与加密算法完全相同。
(2) 签名算法
签名算法以私钥SK和待签名的消息M作为输入,输出签名α。在RSA签名中,签名算法直接输出签名为α = M^d mod N。注意,签名算法和RSA加密体制中的解密算法非常像。
(3) 验证算法
验证算法以公钥PK,签名α以及消息M作为输入,输出一个比特值b。b=1意味着验证通过。b=0意味着验证不通过。在RSA签名中,验证算法首先计算M' = α^e mod N,随后对比M'与M,如果相等,则输出b=1,否则输出b=0。注意:验证算法和RSA加密体制中的加密算法非常像。
所以,在签名算法中,私钥用于对数据进行签名,公钥用于对签名进行验证。这也可以直观地进行理解:对一个文件签名,当然要用私钥,因为我们希望只有自己才能完成签字。验证过程当然希望所有人都能够执行,大家看到签名都能通过验证证明确实是我自己签的。
3. 应用场景
其实公钥和私钥都可以用来加密或解密——只要能保证用A加密,就用B解密就行。至于A是公钥还是私钥,其实可以根据不同的用途而定。例如说,如果你想把某个消息秘密的发给某人,那你就可以用他的公钥加密。因为只有他知道他的私钥,所以这消息也就只有他本人能解开,于是你就达到了你的目的。
但是如果你想发布一个公告,需要一个手段来证明这确实是你本人发的,而不是其他人冒名顶替的。那你可以在你的公告开头或者结尾附上一段用你的私钥加密的内容(例如说就是你公告正文的一段话),那所有其他人都可以用你的公钥来解密,看看解出来的内容是不是相符的。如果是的话,那就说明这公告确实是你发的---因为只有你的公钥才能解开你的私钥加密的内容,而其他人是拿不到你的私钥的。
最后再说一下数字签名。数字签名无非就两个目的:证明这消息是你发的;证明这消息内容确实是完整的——也就是没有经过任何形式的篡改(包括替换、缺少、新增)。其实,上面关于“公告”那段内容,已经证明了第一点:证明这消息是你发的。那么要做到第二点,也很简单,就是把你公告的原文做一次哈希(md5或者sha1都行),然后用你的私钥加密这段哈希作为签名,并一起公布出去。当别人收到你的公告时,他可以用你的公钥解密你的签名,如果解密成功,并且解密出来的哈希值确实和你的公告原文一致,那么他就证明了两点:这消息确实是你发的,而且内容是完整的。
四、散列算法(也称HASH,消息摘要,不可逆算法)
Hash的应用场景非常之多,常见的算法有MD5、SHA-1,只要涉及到通讯传输,都离不开hash(说的有些绝对,只为突出其重要性),因为hash最大的用途是用于校验信息完整性,保证服务端传送的信息被客户端完整的收到,防止丢包后客户端无法判定文件是否完整。
1. MD5算法
hash值 <— MD5(M); // 算法表达式
哈希运算的输入M可以是任意长度的字节码(长度不限,但是过长会导致hash运算很慢,因此一般我们会发送的消息截取部分进行hash),输出固定长度的hash值(通常为128位)
由于hash的不可逆性,我们会经常使用它来生成唯一标示,只要M不同,那么输出的hash值绝对不会相同,我们的UUID生成就使用到了hash。
2. Hash的碰撞攻击
我们一上来也提到了hash是一种不可逆运算,那么想根据hash值反推明文的方式就只有一种,暴力破解!但是,暴力破解并不是无脑破解(就像你猜一个人的银行密码一样,你肯定会从这个人的身份信息开始猜测)。由于Hash算法本身是公开的,算法本身可能存在一些漏洞,2004年8月17日的美国加州圣巴巴拉的国际密码学会议(Crypto’2004)上,来自中国山东大学的王小云教授做了破译MD5、HAVAL-128、 MD4和RIPEMD算法的报告,公布了MD系列算法的破解结果。宣告了固若金汤的世界通行密码标准MD5的堡垒轰然倒塌,引发了密码学界的轩然大波。(注意:并非是真正的破解,只是加速了杂凑冲撞)
关于王小云博士的故事有兴趣的人可以自己百度(还是蛮有意思的)。
而目前大数据云计算的兴起,越多越多的MD5计算网站开始兴起,他们有大量的机器(分布式云计算)不停地计算不同的文本对应的md5值,并将结果存入数据库或Hadoop(大数据)。如图所示:
五、常见名词解释
1. 数字签名
主要功能
保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。
数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。
数字签名是个加密的过程,数字签名验证是个解密的过程。
狭义上的数字签名(也被叫做数字指纹)过程
发送报文时,发送方用一个哈希函数从报文文本中生成报文摘要,然后用自己的私人密钥对这个摘要进行加密(签名),这个加密(签名)后的摘要将作为报文的数字签名和报文一起发送给接收方,接收方首先用与发送方一样的哈希函数从接收到的原始报文中计算出报文摘要,接着再用发送方的公用密钥来对报文附加的数字签名进行解密,如果这两个摘要相同、那么接收方就能确认该数字签名是发送方的。
数字签名有两种功效:一是能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。二是数字签名能确定消息的完整性。因为数字签名的特点是它代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。不同的文件将得到不同的数字摘要。 一次数字签名涉及到一个哈希函数、发送者的公钥、发送者的私钥。
广义上的数字签名过程(仅仅是签名与校验):
发送方用自己的密钥对报文X进行Encrypt(编码)运算,生成不可读取的密文Dsk,然后将Dsk传送给接收方,接收方为了核实签名,用发送方的公用密钥进行Decrypt(解码)运算,还原报文。
2. 数字证书
狭义上的数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。数字证书还有一个重要的特征就是只在特定的时间段内有效。
广义上的数字证书指包含了公钥以及证书有效期,加密算法信息,颁发机构等信息的文件