常见加密算法05 - 分组密码AES

182 阅读13分钟

​各位眼光独到,学贯中西的读者们,你们好啊!今天我们讨论一下AES算法及其识别。

虽然DES算法的分组大小是64位,但是由于DES算法的密钥长度只有56位,因此DES算法存在着弱点,容易受到暴力破解和差分攻击等攻击手段的威胁。因此,在实际应用中,DES算法已经不再被广泛使用,而被更加安全的算法所取代,如AES算法等。

为了强调大于56位密钥对高强度安全的重要性,RAS数据安全(RSA Data Security)从1997年早期就发起了一系列的DES攻击竞赛。1998年电子前沿基金会(Electronic Frontier Foundation)在不到三天的时间里攻破了DES,赢得了RSA DES挑战II-2竞赛,EFF(电子前沿基金会缩写)用了一种专门开发的计算机,叫做DES破解者,它的造价不到25万美元。它的加密芯片能够使DES破解者有每秒钟产生880亿密钥的能力。更近一些,在1999年早期,分布式网络(Distributed. Net)创纪录的用22小时15分钟,利用DES破解者和全世界接近10万台电脑组成的网络赢得了RSA DES挑战III竞赛。DES破解者和联网电脑合起来每秒能够测试2450亿的密钥。另外,已经被证实,可以组建花费10万美元的硬件设施,以使其在3.5小时内攻破DES。这恰好验证了现在任何组织,只要利用现代化的资源就可以轻松破解DES。

不过,现在可以使用3DES的方式扩展密钥长度来进行加密。

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

加密图解

图里面的每个流程具体的讲解,看下面的视频链接,依然是UP主(可厉害的土豆)讲的:

www.bilibili.com/video/BV1i3…

里面说的很清楚,加密流程,密钥扩展等。总的来说,这个算法并不难,只是非常的繁琐。至于为什么要这样做,我就不懂了。

AES是取代DES的,其大致流程是差不多的,也有S盒,置换表等。关于S盒是否是固定的,我猜测不是的,但是应该也需要满足一定的条件。

Java算法识别

先看其使用:

    // 加密
    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("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/填充方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        String result = null;
        result = new String(Base64.encode(encrypted, Base64.DEFAULT));
        return result;//使用BASE64做转码功能
    }

    // 解密
    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("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = Base64.decode(sSrc.getBytes("UTF-8"), Base64.DEFAULT);
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, "utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

依然是使用 Cipher 类来完成,不过传递的参数是 AES/ECB/PKCS5Padding 。

所以我们可以按照之前的Hook逻辑来处理:

Java.use('javax.crypto.spec.SecretKeySpec').$init.overload('[B''java.lang.String').implementation = function (arg0, arg1) {

    //console.log('javax.crypto.spec.SecretKeySpec.init is called');
    if (arg1.indexOf("AES") != -1) {
        console.log('key:'JSON.stringify(arg0), ",algorithm:" + arg1);
    }

    var result = this.$init(arg0, arg1);
    return result;
};

//.init(Cipher.ENCRYPT_MODE, getRawKey(key), iv);
Java.use('javax.crypto.Cipher').init.overload('int''java.security.Key').implementation = function (arg0, arg1) {
    //console.log('javax.crypto.Cipher.init is called!', arg0, arg1, arg2);
    var mode = arg0;
    var key = arg1;
    var SecretKeySpecClass = Java.use('java.security.Key');
    var keyobj = Java.cast(key, SecretKeySpecClass);
    var key_bytes = keyobj.getEncoded();
    console.log('javax.crypto.Cipher.init is called!', mode, JSON.stringify(key_bytes));
    var result = this.init(arg0, arg1);
    return result;
};

Java.use('javax.crypto.Cipher').doFinal.overload('[B').implementation = function (arg0) {
    console.log('javax.crypto.Cipher.doFinal is called!'JSON.stringify(arg0));
    var data = arg0;
    var result = this.doFinal(arg0);
    console.log('javax.crypto.Cipher.doFinal is called!'JSON.stringify(data), "encrypt:"JSON.stringify(result));
    return result;
};

通过hook SecretKeySpec 的构造函数可以获得密钥字节。通过 hook doFinal 方法可以获取明文字节。

要是做的更完善一点,可以Hook Cipher 类的所有相关方法。我之前写了一个 Xposed 版的,就是做了这样的一个事情:

github.com/aprz512/Jav…

C算法识别

贴一些常量表:

// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM - 
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] =   {
  //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
  0x630x7c0x770x7b0xf20x6b0x6f0xc50x300x010x670x2b0xfe0xd70xab0x76,
  0xca0x820xc90x7d0xfa0x590x470xf00xad0xd40xa20xaf0x9c0xa40x720xc0,
  0xb70xfd0x930x260x360x3f0xf70xcc0x340xa50xe50xf10x710xd80x310x15,
  0x040xc70x230xc30x180x960x050x9a0x070x120x800xe20xeb0x270xb20x75,
  0x090x830x2c0x1a0x1b0x6e0x5a0xa00x520x3b0xd60xb30x290xe30x2f0x84,
  0x530xd10x000xed0x200xfc0xb10x5b0x6a0xcb0xbe0x390x4a0x4c0x580xcf,
  0xd00xef0xaa0xfb0x430x4d0x330x850x450xf90x020x7f0x500x3c0x9f0xa8,
  0x510xa30x400x8f0x920x9d0x380xf50xbc0xb60xda0x210x100xff0xf30xd2,
  0xcd0x0c0x130xec0x5f0x970x440x170xc40xa70x7e0x3d0x640x5d0x190x73,
  0x600x810x4f0xdc0x220x2a0x900x880x460xee0xb80x140xde0x5e0x0b0xdb,
  0xe00x320x3a0x0a0x490x060x240x5c0xc20xd30xac0x620x910x950xe40x79,
  0xe70xc80x370x6d0x8d0xd50x4e0xa90x6c0x560xf40xea0x650x7a0xae0x08,
  0xba0x780x250x2e0x1c0xa60xb40xc60xe80xdd0x740x1f0x4b0xbd0x8b0x8a,
  0x700x3e0xb50x660x480x030xf60x0e0x610x350x570xb90x860xc10x1d0x9e,
  0xe10xf80x980x110x690xd90x8e0x940x9b0x1e0x870xe90xce0x550x280xdf,
  0x8c0xa10x890x0d0xbf0xe60x420x680x410x990x2d0x0f0xb00x540xbb0x16 };

static const uint8_t rsbox[256] =
{ 0x520x090x6a0xd50x300x360xa50x380xbf0x400xa30x9e0x810xf30xd70xfb,
  0x7c0xe30x390x820x9b0x2f0xff0x870x340x8e0x430x440xc40xde0xe90xcb,
  0x540x7b0x940x320xa60xc20x230x3d0xee0x4c0x950x0b0x420xfa0xc30x4e,
  0x080x2e0xa10x660x280xd90x240xb20x760x5b0xa20x490x6d0x8b0xd10x25,
  0x720xf80xf60x640x860x680x980x160xd40xa40x5c0xcc0x5d0x650xb60x92,
  0x6c0x700x480x500xfd0xed0xb90xda0x5e0x150x460x570xa70x8d0x9d0x84,
  0x900xd80xab0x000x8c0xbc0xd30x0a0xf70xe40x580x050xb80xb30x450x06,
  0xd00x2c0x1e0x8f0xca0x3f0x0f0x020xc10xaf0xbd0x030x010x130x8a0x6b,
  0x3a0x910x110x410x4f0x670xdc0xea0x970xf20xcf0xce0xf00xb40xe60x73,
  0x960xac0x740x220xe70xad0x350x850xe20xf90x370xe80x1c0x750xdf0x6e,
  0x470xf10x1a0x710x1d0x290xc50x890x6f0xb70x620x0e0xaa0x180xbe0x1b,
  0xfc0x560x3e0x4b0xc60xd20x790x200x9a0xdb0xc00xfe0x780xcd0x5a0xf4,
  0x1f0xdd0xa80x330x880x070xc70x310xb10x120x100x590x270x800xec0x5f,
  0x600x510x7f0xa90x190xb50x4a0x0d0x2d0xe50x7a0x9f0x930xc90x9c0xef,
  0xa00xe00x3b0x4d0xae0x2a0xf50xb00xc80xeb0xbb0x3c0x830x530x990x61,
  0x170x2b0x040x7e0xba0x770xd60x260xe10x690x140x630x550x210x0c0x7d };

// The round constant word array, Rcon[i], contains the values given by 
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
// Note that i starts at 1, not 0).
static const uint8_t Rcon[255] = {
  0x8d0x010x020x040x080x100x200x400x800x1b0x360x6c0xd80xab0x4d0x9a, 
  0x2f0x5e0xbc0x630xc60x970x350x6a0xd40xb30x7d0xfa0xef0xc50x910x39, 
  0x720xe40xd30xbd0x610xc20x9f0x250x4a0x940x330x660xcc0x830x1d0x3a, 
  0x740xe80xcb0x8d0x010x020x040x080x100x200x400x800x1b0x360x6c0xd8, 
  0xab0x4d0x9a0x2f0x5e0xbc0x630xc60x970x350x6a0xd40xb30x7d0xfa0xef, 
  0xc50x910x390x720xe40xd30xbd0x610xc20x9f0x250x4a0x940x330x660xcc, 
  0x830x1d0x3a0x740xe80xcb0x8d0x010x020x040x080x100x200x400x800x1b, 
  0x360x6c0xd80xab0x4d0x9a0x2f0x5e0xbc0x630xc60x970x350x6a0xd40xb3, 
  0x7d0xfa0xef0xc50x910x390x720xe40xd30xbd0x610xc20x9f0x250x4a0x94, 
  0x330x660xcc0x830x1d0x3a0x740xe80xcb0x8d0x010x020x040x080x100x20, 
  0x400x800x1b0x360x6c0xd80xab0x4d0x9a0x2f0x5e0xbc0x630xc60x970x35, 
  0x6a0xd40xb30x7d0xfa0xef0xc50x910x390x720xe40xd30xbd0x610xc20x9f, 
  0x250x4a0x940x330x660xcc0x830x1d0x3a0x740xe80xcb0x8d0x010x020x04, 
  0x080x100x200x400x800x1b0x360x6c0xd80xab0x4d0x9a0x2f0x5e0xbc0x63, 
  0xc60x970x350x6a0xd40xb30x7d0xfa0xef0xc50x910x390x720xe40xd30xbd, 
  0x610xc20x9f0x250x4a0x940x330x660xcc0x830x1d0x3a0x740xe80xcb  };

根据常量表,就可以全局搜索了,依然使用 findcrypt3 插件来识别,当然我们可以自己加一些识别规则。