也学AES加密算法主要步骤以及具体实例

来源:互联网 发布:网络小胖的老婆 编辑:程序博客网 时间:2024/06/06 01:54
 

1 AES加密算法主要步骤
1.1 AES算法整体描述
l 给定一个明文x,将State初始化为x,并进行AddRoundKey操作,将RoundKey与State异或。
l 对前Nr-1轮中的每一轮,用S盒对进行一次代换操作,称为SubBytes;对State做一置换ShiftRows;再对State做一次操作MixColumns;然后进行AddRoundKey操作。
l 依次进行SubBytes、ShiftRows和AddRoundKey操作。
l 将State定义为密文y。
1.2 伪代码
Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, w[0, Nb-1])
for round = 1 step 1 to Nr-1
SubBytes(state)
ShiftRows(state)
MixColumns(state)
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
end for
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
out = state
end
2 KeyExpansion()实现
2.1要求
将128 bit的密钥扩展至加密过程中的9轮循环,再上初始及最后2轮,需构造11轮密钥。每一轮密钥由4个字组成。每个字由4个byte组成。
2.2 算法设计
输入:byte[] key, byte[] w //key为密钥 w为扩展的密钥
输出:byte[] w //扩展密钥 长度为4 * 4 * 11
处理:
 1)建立一个4 byte的一维数组,存放一个字。Byte[] temp;
 2)将密钥key[0..15]送至w[0..15];//已赋值4个字给w。
 3) for I = 4 to 43
  //以下每次处理一个字(32 bit)
  temp = w[I-1];
  if (I = 0 mod 4) //处理一个字 then
   for j = 1 to 4 //字的4 byte处理
在此循环中取temp数组下标的次序为1,2,3,0 //RotWord 操作
     如果是字的首byte,取Rcon常数Rcon(I/4);
     temp[j] = Sbox(temp[ (j + 1) /4]^Rcon常数
            end for
   temp = SubWord(RotWord(temp))⊕Rcon[i/4]
        end if
  w[I] = w[I-4]⊕temp;
    end for
 4) 输出w
3多项式乘法mod GF(28)运算
3.1要求
将两个byte在有限域GF(28)按多项式乘法,并mod 不可约多项式m(x)=x8+x4+x3+x+1。
3.2 算法设计
输入:byte a ,byte b
输出:byte r
数学基础:
 GF(28)有限域性质:两个元素的加法与两个字节按位模2加是一致的;乘法满足结合律;
 考虑多项式中的一项aixi(i∈0-7),用一次x乘以多项式:
  b(x) = b7x7 + b6x6 + b5x5 + b4x4 + b3x3 + b2x2 + b1x + b0,
 得到
  b7x8 + b6x7 + b5x6 + b4x5 + b3x4 + b2x3 + b1x2 + b0x  (式1)
 将结果模m(x)求余得到x*b(x)。
如果b7 = 0,则式1就是x*b(x)。
如果b7 不等于0,则必须从式1中减去m(x)后结果为x*b(x)。用x乘一个多项式简称x乘。
由此得出,aixi 乘以b(x),可以作i次x乘。x(十六进制表示为0x02)乘可以用字节内左移一位和紧接着的一个与0x1b的按位模2加来实现,该运算暂记为xtime()。X的更高次的乘法可以通过重复应用xtime()来实现。通过将中间结果相加,任意乘法都可以利用xtime()来实现。例如:
57 * 13 = fe ,这是因为:
  57 * 02 = xtime(57) = ae
  57 * 04 = xtime(ae) = 47
  57 * 08 = xtime(47) = 8e
  57 * 10 = xtime(8e) = 07
所以
  57 * 13 = 57 * ( 01⊕ 02 ⊕10 )
     = 57⊕ ae⊕ 07
     = fe
 

4 Sbox生成
4.1要求
一个字节byte看作为一个在有限域GF(28)的多项式,求出它关于模m(x)的乘法逆,之后将该乘法逆在GF(2)上作仿射变换。
4.2 算法设计
输入:byte a
输出:byte[] S
数学逻辑:
 由有限域GF(28)性质。某个生成元(也是本原元)a,其a^(28-1) ≡ 1 mod m(x)。或a255 ≡ 1 mod m(x)。另外,a的从1到28-1的幂的值是构成了有限域GF(28)。
 由乘法逆的性质b * b -1 ≡ 1。求乘法逆可简化如下
 设 x = am ,设y是x的乘法逆,则y = a255-m
处理:
 建立三个一组数组,分别为:byte S[255],byte L[255],byte E[255]。
 取本原元为a = 0x03,
 将a的0,1,2…255次方mod m(x)分另送至数组L中。a的运算参考前面的多项式乘法运算。如下伪码:
 For i = 0 to 255
  L[i] = ai  (式2)
 End for
 为方便计算乘法逆的指数,数组E存放ai的幂指数i。将式2中ai值为数组E的下标,而将ai在数组L中的下标i作为数组E中对应的值。对应(式2)每一项有E[ai] = i。
 由上面两个数组L,E,可得到GF(28)域中的任一byte的乘法逆。
 设字节c它由ai生成的。其中a是GF(28)域中的生成元。欲求c的乘法逆。只需要找到a255-i即可。在数组E中可以由c查出生成元a的幂指数i。c-1的幂指数255-i。所以c-1 = L[255-i]。
 对每一个字节byte根据以上内容得到乘法逆,作仿射变换得到数组S。即为Sbox。

 

下面是AES加解密算法,使用Base64做转码以及辅助加密实例代码:

package com.wintv.common;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;/******************************************************************************* * AES加解密算法 *  * @author arix04 *  */public class AES {    // 加密    public static String Encrypt(String sSrc, String sKey) throws Exception {        if (sKey == null) {            System.out.print("Key为空null");            return null;        }        // 判断Key是否为16位        if (sKey.length() != 16) {            System.out.print("Key长度不是16位");            return null;        }        byte[] raw = sKey.getBytes();        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"        IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);        byte[] encrypted = cipher.doFinal(sSrc.getBytes());        return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。    }    // 解密    public static String Decrypt(String sSrc, String sKey) throws Exception {        try {            // 判断Key是否正确            if (sKey == null) {                System.out.print("Key为空null");                return null;            }            // 判断Key是否为16位            if (sKey.length() != 16) {                System.out.print("Key长度不是16位");                return null;            }            byte[] raw = sKey.getBytes("ASCII");            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");            IvParameterSpec iv = new IvParameterSpec("0102030405060708"                    .getBytes());            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密            try {                byte[] original = cipher.doFinal(encrypted1);                String originalString = new String(original);                return originalString;            } catch (Exception e) {                System.out.println(e.toString());                return null;            }        } catch (Exception ex) {            System.out.println(ex.toString());            return null;        }    }    public static void main(String[] args) throws Exception {        /*         * 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符,虽然不会错,至于怎么裁决,个人看情况而定         * 此处使用AES-128-CBC加密模式,key需要为16位。         */        String cKey = "1234567890123456";        // 需要加密的字串        String cSrc = "Email : arix04@xxx.com";        System.out.println(cSrc);        // 加密        long lStart = System.currentTimeMillis();        String enString = AES.Encrypt(cSrc, cKey);        System.out.println("加密后的字串是:" + enString);        long lUseTime = System.currentTimeMillis() - lStart;        System.out.println("加密耗时:" + lUseTime + "毫秒");        // 解密        lStart = System.currentTimeMillis();        String DeString = AES.Decrypt(enString, cKey);        System.out.println("解密后的字串是:" + DeString);        lUseTime = System.currentTimeMillis() - lStart;        System.out.println("解密耗时:" + lUseTime + "毫秒");    }}

AES加解密算法,使用byte2hex做转码:

package com.wintv.common;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;/******************************************************************************* * AES加解密算法 *  * @author arix04 *  */public class AES {    // 加密    public static String Encrypt(String sSrc, String sKey) throws Exception {        if (sKey == null) {            System.out.print("Key为空null");            return null;        }        // 判断Key是否为16位        if (sKey.length() != 16) {            System.out.print("Key长度不是16位");            return null;        }        byte[] raw = sKey.getBytes();        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");        IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);        byte[] encrypted = cipher.doFinal(sSrc.getBytes());        return byte2hex(encrypted).toLowerCase();    }    // 解密    public static String Decrypt(String sSrc, String sKey) throws Exception {        try {            // 判断Key是否正确            if (sKey == null) {                System.out.print("Key为空null");                return null;            }            // 判断Key是否为16位            if (sKey.length() != 16) {                System.out.print("Key长度不是16位");                return null;            }            byte[] raw = sKey.getBytes("ASCII");            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");            IvParameterSpec iv = new IvParameterSpec("0102030405060708"                    .getBytes());            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);            byte[] encrypted1 = hex2byte(sSrc);            try {                byte[] original = cipher.doFinal(encrypted1);                String originalString = new String(original);                return originalString;            } catch (Exception e) {                System.out.println(e.toString());                return null;            }        } catch (Exception ex) {            System.out.println(ex.toString());            return null;        }    }    public static byte[] hex2byte(String strhex) {        if (strhex == null) {            return null;        }        int l = strhex.length();        if (l % 2 == 1) {            return null;        }        byte[] b = new byte[l / 2];        for (int i = 0; i != l / 2; i++) {            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2),                    16);        }        return b;    }    public static String byte2hex(byte[] b) {        String hs = "";        String stmp = "";        for (int n = 0; n < b.length; n++) {            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));            if (stmp.length() == 1) {                hs = hs + "0" + stmp;            } else {                hs = hs + stmp;            }        }        return hs.toUpperCase();    }    public static void main(String[] args) throws Exception {        /*         * 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符,虽然不会错,至于怎么裁决,个人看情况而定         */        String cKey = "1234567890123456";        // 需要加密的字串        String cSrc = "Email : arix04@xxx.com";        System.out.println(cSrc);        // 加密        long lStart = System.currentTimeMillis();        String enString = AES.Encrypt(cSrc, cKey);        System.out.println("加密后的字串是:" + enString);        long lUseTime = System.currentTimeMillis() - lStart;        System.out.println("加密耗时:" + lUseTime + "毫秒");        // 解密        lStart = System.currentTimeMillis();        String DeString = AES.Decrypt(enString, cKey);        System.out.println("解密后的字串是:" + DeString);        lUseTime = System.currentTimeMillis() - lStart;        System.out.println("解密耗时:" + lUseTime + "毫秒");    }}



原创粉丝点击