详细解析DES系列加密技术(二)

我们在上一篇《详细解析DES系列加密技术(一)》中提到说DES在1999年1月被破解,并且有分析报告提出DES算法在理论上存在的一些漏洞,另外,2001年,DES作为一个标准已经被取代了.一旦一种加密技术被破解,那么,被取代也就是必然的事情了,对于DES来说,取代他的又是谁呢?今天我们来讨论一下DES的后辈,也就是3DES和AES.

3DES(triple-DES)是为了增加DES的强度,将DES重复3次所得到的一种密码算法,也被称为TDEA(Triple Data Encryption Algorithm),缩写为3DES,其过程如下图所示:


1.png

明文经过3次DES处理才能变成最后的密文,由于DES密钥的长度实质上是56bit,因此3DES的密钥长度就是168bit.

虽然3DES是将DES重复了3次,但是透过上图就能够发现3DES并不是进行了三次DES的加密(加密—加密—加密),而是加密—解密—加密.这个是由IMB设计出来的,目的是为了兼容普通的DES,比如说当3DES的三个密钥都相同时,3DES也就相当于是普通的DES了.也就是说3DES具有向下兼容性.

在上一篇博文中我们谈到DES时说,DES的加密和解密过程只是改变了子密钥的顺序,而实际上处理都是相同的.所以说,如果所有密钥都使用相同的比特序列,那么其结果与普通的DES就是等价的.

我们看刚刚那幅图,如果密钥1和密钥3使用相同的密钥,而密钥2使用不同的密钥(也就是只使用两个DES密钥),这种三重DES就成为DES-EDE2.EDE表示的是加密(Encryption)—解密(Decryption)—加密(Encryption),如下图所示:

2.png

3DES的解密过程与加密过程正好相反,是以密钥3,密钥2,密钥1的顺序执行解密—加密—解密的操作.以下是Java实现3DES加/解密的过程: 附带代码1.javapackage org.shangzeng.cipher;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class ThreeDESTest {

public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
    String content="Hello,ThreeDES Test!";
    String key="shangzengxueyuan2018";
    byte[] encryptResult=encryThreeDES(content,key);
    String result=convertByteToHexString(encryptResult);
    System.out.println("3DES加密后为:"+result);
    byte[] decryptResult=decryptThreeDES(encryptResult,key);
    System.out.println("3DES解密后的内容为:"+new String(decryptResult));
}


/***
 *  将byte数组转换成16进制输出
 */
public static String convertByteToHexString(byte[] byteArray){
    String result="";
    for (int i = 0; i < byteArray.length; i++) {
        int temp=byteArray[i]& 0xff;
        String tempHex= Integer.toHexString(temp);
        if(tempHex.length()<2){
            result+="0"+tempHex;
        }else{
            result+=tempHex;
        }
    }
    return result;
}

public static byte[] get3DESKey(String pass){
    byte[] key=new byte[24];//3DES里的密钥key为24位
    byte[] temp;
    try {
        temp=pass.getBytes("UTF-8");//将字符串转成字节数组
        if(key.length>temp.length){
            //如果temp不够24位,则拷贝temp数组整个长度的内容到key数组中
            System.arraycopy(temp,0,key,0,temp.length);
        }else{
            //如果temp大于24位,则拷贝temp24个长度的内容到key数组中
            System.arraycopy(temp,0,key,0,key.length);
        }
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return key;
}

private static final String ALGORITHM="DESede";
public static byte[] encryThreeDES(String message,String key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    return threeDES(message.getBytes(),key,Cipher.ENCRYPT_MODE);
}
public static byte[] decryptThreeDES(byte[] contentArray,String key) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
    return threeDES(contentArray,key,Cipher.DECRYPT_MODE);
}

private static byte[] threeDES(byte[] contentArray,String key,int mode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    SecretKey desKey=new SecretKeySpec(get3DESKey(key), ALGORITHM);
    Cipher cipher= null;
    cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(mode,desKey);
    return cipher.doFinal(contentArray);
}

}

3DES虽然比DES要安全一些,但是他的处理速度不高.所以除了特别重视向下兼容性的情况以外,很少被用于新的用途.接下来我们来谈谈AES.

AES(Advanced Encryption Standard)也就是高级加密标准,他是为取代DES而成为新标准的一种对称加密算法.谈到这里我们发现,AES和DES一样,只是加密的标准,而不负责具体的加密算法,DES是由Feistel实现的,而AES的实现,则由NIST(National Institute of Standards and Technology,国家标准技术研究所)组织一个公开竞选活动去竞选.这个研究所选拔的密码算法,会成为美国的国家标准,也就是我们在上一篇文章中提到的FIPS.因为参加这个竞选的条件是:被选择AES的密码算法必须无条件地免费供全世界使用,所以AES虽然是美国的标准,却也和DES一样,成为了世界性的标准.

AES竞选的参与者还必须提交密码算法的详细规格书、以ANSI C和Java编写的实现代码以及抗密码破译强度的评估等材料.所以,参加者所提交的密码算法,必须在详细设计和程序代码完全公开的情况下,依然保证较高的强度,这样呢,就杜绝了隐蔽式安全性.

AES竞选的活动虽然是由NIST组织的,但密码算法的评审却不是由NIST完成的,这个评审是由全世界的企业和密码学家共同完成的.所以参加者都会努力从各个角度寻找其他密码算法的弱点,并向其他参与评审的人进行证明.这种方式就是通过竞争来实现标准化.

在前面我们提到过,3DES虽然从安全性上来说优于DES,但是,他的处理速度并不高.因此,我们必须要考虑实现AES的算法的处理速度,除此之外,算法本身是否存在弱点、实现的容易性,密钥准备的速度,能否在各种平台,比如智能卡,8位CPU等低性能平台以及工作站等高性能平台上有效工作,这些均是AES选拔参与者需要考虑的范围.

从1997年,NIST开始募集,到1999年,15个算法有5个算法入围,最终2000年10月2日,Rijndael力压群雄,被NIST选定为AES标准.Rijndael是由比利时密码学家Joan Daemen和Vincent Rijmen设计的分组密码算法.不知道Rijndal是否和这两位密码学家的名字有关系.

下面我们来详细说明一下Rijndael的加密和解密:

首先Rijndael的分组长度和密钥长度可以分别以32bit为单位在128bit到256bit的范围内进行选择.不过,在AES的规格中,分组长度固定位128bit,密钥长度只有128,192和256三种.

和DES一样,Rijndael算法也是由多个轮构成的,只不过,DES使用Feistel网络作为其基本结构,而Rijndael则是使用了SPN结构.其一个单位的加解密过程如下图所示:

3.png

现在我们对这四种处理进行详细解析:

通过上图我们知道Rijndael的单位输入分组为128bit,也就是16字节.首先需要对这16个字节进行SubBytes处理.所谓SubBytes处理可以简单理解为输入的每个字节的值都在另一张替换表中拥有其对应的值,然后按着这种对应关系进行一一替换.如下图所示:

4.png

在SubBytes之后,就是ShiftRows处理,这一步是将以4字节为一个单位的行按着一定的规则向左平移,并且每一行平移的字节数不相同,如下图所示:

5.png

之后我们开始进行第三步处理,即MixColumns处理,这一步仍然是对以4字节为单位的值进行比特运算,将其变为另外一个4字节值,类似于第一步的SubBytes,如下图所示:


6.png

最后我们要将MixColumns的输出与轮密钥进行XOR,即AddRoundKey处理,到这里,Rijndael的一轮就结束了.

因为在Rijndael中所有输入的bit都会在一轮中进行加密,因此与Feistel网络相比,加密所需要的轮数就减少了.并且以上这四种方式可以并行进行.至于解密过程,除了最后一步AddRoundKey与加密时相同之外,其他三种处理都是与原步骤相对应的的逆运算.以下是AES的Java实现: 附加代码2.java
package org.shangzeng.testsignature;

import javax.crypto.*;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AESTest {

private static String convertByteToHexString(byte[] byteArray){
    String result="";
    for (int i = 0; i < byteArray.length; i++) {
        int temp=byteArray[i]& 0xff;
        String tempHex= Integer.toHexString(temp);
        if(tempHex.length()<2){
            result+="0"+tempHex;
        }else{
            result+=tempHex;
        }
    }
    return result;
}

static final String ALGORITHM="AES";
static SecretKey secretKey=null;
public static SecretKey generateKey() throws NoSuchAlgorithmException {
    if(null==secretKey){
        KeyGenerator keyGenerator=KeyGenerator.getInstance(ALGORITHM);
        SecureRandom secureRandom=new SecureRandom();
        keyGenerator.init(secureRandom);
        secretKey=keyGenerator.generateKey();
    }
    return secretKey;
}

public static byte[] encrypt(String content) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
    SecretKey secretKey=generateKey();
    return aes(Cipher.ENCRYPT_MODE,secretKey,content.getBytes(Charset.forName("UTF-8")));
}

public static String decrypt(byte[] contentArray) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, UnsupportedEncodingException {
    SecretKey secretKey=generateKey();
    byte[] resultByte= aes(Cipher.DECRYPT_MODE,secretKey,contentArray);
    return new String(resultByte,"UTF-8");
}


private static byte[] aes(int mode,Key key,byte[] contentArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    Cipher cipher=Cipher.getInstance(ALGORITHM);
    cipher.init(mode,key);
    byte[] result=cipher.doFinal(contentArray);
    return result;
}

public static void main(String[] args) throws InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, UnsupportedEncodingException {
    String content="你好,我是熵熷学院Jacob老师.";
    byte[] encryptResult=encrypt(content);
    String resultByHex=convertByteToHexString(encryptResult);
    String resultByUnicode=new String(encryptResult,"UTF-8");
    System.out.println("加密后的结果16进制表示为:"+resultByHex+"\n 加密后的结果转换成字符串为:"+resultByUnicode);
    String decryptResult=decrypt(encryptResult);
    System.out.println("解密后的结果为:"+decryptResult);
}

}

对于Rijndael来说,因为它具有非常严谨的数学结构,也就是说从明文到密文的计算过程可以全部用公式来表达,这是之前其他算法所不具备的性质,所以,严谨的数学结构这个性质也就意味着Rijndael能够通过数学方法进行破译,当然这目前只是一种假设.目前来看,还没有出现针对Rijndael的有效攻击,因此以Rijndael为实现的AES还是比较安全的.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容