一文读懂DES加密算法和实现

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: DES是一种使用56位秘钥对64位长分组进行加密的加密算法。

DES算法


YD2]HF8N3S}E5{8FTLR~NYT.jpg

DES加密算法


简介


DES是一种使用56位秘钥对64位长分组进行加密的加密算法。

3LEQ2ZT5)R8N3R})}_V(EMK.png


DES分组密码

DES是一种对称密码,加密和解密使用的是相同的密钥。DES对明文当中的每个分组加密过程都包含了16轮,每一轮的操作相同,且每一轮加密使用的子密钥为主密钥推导而来。


image.gifDES迭代结构

在DES加密当中使用了Feistal结构,下面给出DES的Feistal结构图。

@}0{@$[SX`OT9R(Y7[LFZRE.pngDES的Fesital结构


算法描述

下面在这里具体描述一下DES的算法结构。

初始置换和逆初始置换

初始置换和逆初始置换都是按位置换,这里通过初始置换表和逆初始置换表来实现,做了一个映射。

image.gif初始置换和逆初始置换

@1]QCE9)TU3JYLVARQQ)H2H.png

f函数

f函数是DES安全性的核心,首先通过扩展算法把32位的块扩展到48位,然后和每一轮的密钥(48bit)进行异或,之后通过SBox替换得到32位的输出,最后通过置换得到最终的加密结果。

OY9_DIES5{QC)%0Z1@A%D1D.pngF函数

密钥编排

对于DES来说,实际密钥有效的只有56位,然后通过这56位的密钥生成16轮的子密钥,每个子密钥长度为48位,其规则如下。

  • 将原始的密钥分成两部分
  • 在第1, 2, 9, 16轮当中,左右两部分分别向左移动一位
  • 其他轮中,左右两部分向左移动两位

这样做之后会使得C0 = C16, D0 = D16

解密过程

解密过程和加密过程类似,倒过来安排一遍就好了。


完整算法实现

// region constant
pub static IP: [u8; 64] = [
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9, 1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7
];
pub static IP_REVERSE: [u8; 64] = [
    40, 8, 48, 16, 56, 24, 64, 32,
    39, 7, 47, 15, 55, 23, 63, 31,
    38, 6, 46, 14, 54, 22, 62, 30,
    37, 5, 45, 13, 53, 21, 61, 29,
    36, 4, 44, 12, 52, 20, 60, 28,
    35, 3, 43, 11, 51, 19, 59, 27,
    34, 2, 42, 10, 50, 18, 58, 26,
    33, 1, 41, 9, 49, 17, 57, 25
];
pub static E: [u8; 48] = [
    32, 1, 2, 3, 4, 5,
    4, 5, 6, 7, 8, 9,
    8, 9, 10, 11, 12, 13,
    12, 13, 14, 15, 16, 17,
    16, 17, 18, 19, 20, 21,
    20, 21, 22, 23, 24, 25,
    24, 25, 26, 27, 28, 29,
    28, 29, 30, 31, 32, 1
];
pub static S1: [u8; 64] = [
    14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
    0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
    4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
    15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
];
pub static S2: [u8; 64] = [
    15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
    3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
    0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
    13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
];
pub static S3: [u8; 64] = [
    10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
    13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
    13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
    1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
];
pub static S4: [u8; 64] = [
    7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
    13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
    10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
    3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
];
pub static S5: [u8; 64] = [
    2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
    14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
    4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
    11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
];
pub static S6: [u8; 64] = [
    12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
    10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
    9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
    4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
];
pub static S7: [u8; 64] = [
    4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
    13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
    1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
    6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
];
pub static S8: [u8; 64] = [
    13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
    1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
    7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
    2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
];
pub static BOX_LOOKUP: [usize; 64] = [
    0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23,
    8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31,
    32, 48, 33, 49, 34, 50, 35, 51, 36, 52, 37, 53, 38, 54, 39, 55,
    40, 56, 41, 57, 42, 58, 43, 59, 44, 60, 45, 61, 46, 62, 47, 63
];
pub static BOXES: [[u8; 64]; 8] = [
    S1, S2, S3, S4, S5, S6, S7, S8
];
pub static P: [u8; 32] = [
    16, 7, 20, 21,
    29, 12, 28, 17,
    1, 15, 23, 26,
    5, 18, 31, 10,
    2, 8, 24, 14,
    32, 27, 3, 9,
    19, 13, 30, 6,
    22, 11, 4, 25
];
pub static PC1: [u8; 56] = [
    57, 49, 41, 33, 25, 17, 9,
    1, 58, 50, 42, 34, 26, 18,
    10, 2, 59, 51, 43, 35, 27,
    19, 11, 3, 60, 52, 44, 36,
    63, 55, 47, 39, 31, 23, 15,
    7, 62, 54, 46, 38, 30, 22,
    14, 6, 61, 53, 45, 37, 29,
    21, 13, 5, 28, 20, 12, 4
];
pub static PC2: [u8; 48] = [
    14, 17, 11, 24, 1, 5,
    3, 28, 15, 6, 21, 10,
    23, 19, 12, 4, 26, 8,
    16, 7, 27, 20, 13, 2,
    41, 52, 31, 37, 47, 55,
    30, 40, 51, 45, 33, 48,
    44, 49, 39, 56, 34, 53,
    46, 42, 50, 36, 29, 32
];
pub static LSHIFTS: [u8; 16] = [
    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
];
// endregion
pub struct DES {
}
impl DES {
    fn permute(block: u64, block_size: u8, permutation: &[u8]) -> u64 {
        let mut result = 0u64;
        for &i in permutation.iter() {
            result <<= 1;
            let p = block_size - i;
            result |= (block & (1 << p)) >> p;
        }
        return result;
    }
    fn generate_round_keys(key: u64) -> [u64; 16] {
        let mut keys = [0u64; 16];
        let mut cd = DES::permute(key, 64, &PC1);
        let data_mask = (1 << 55) | (1 << 27);
        let zero_mask = (0xFF << 56) | (1 << 28);
        for (i, &j) in LSHIFTS.iter().enumerate() {
            for _ in 0..j {
                let data = (cd & data_mask) >> 27;
                cd = (cd << 1) & !zero_mask | data;
            }
            keys[i] = DES::permute(cd, 56, &PC2);
        }
        return keys;
    }
    fn f(block: u64, key: u64) -> u64 {
        let mut result = 0u64;
        let tmp = DES::permute(block, 32, &E) ^ key;
        let mask = 0b111111u64;
        for (i, s_box) in BOXES.iter().enumerate() {
            let val: u64 = (tmp & (mask << (42 - (i * 6)))) >> (42 - (i * 6));
            result = (result << 4) | s_box[BOX_LOOKUP[val as usize]] as u64;
        }
        return DES::permute(result, 32, &P);
    }
    fn feistel(block: u64, keys: [u64; 16]) -> u64 {
        let lr = DES::permute(block, 64, &IP);
        let mut l: u64 = (lr & 0xFF_FF_FF_FF_00_00_00_00) >> 32;
        let mut r: u64 = lr & 0x00_00_00_00_FF_FF_FF_FF;
        for &key in keys.iter() {
            let tmp: u64 = l;
            l = r;
            r = tmp ^ DES::f(r, key);
        }
        let switched: u64 = (r << 32) | l;
        return DES::permute(switched, 64, &IP_REVERSE);
    }
    fn encrypt_block(block: u64, keys: [u64; 16]) -> u64 {
        return DES::feistel(block, keys);
    }
    fn decrypt_block(block: u64, keys: [u64; 16]) -> u64 {
        let mut rks = [0u64; 16];
        for (i, &key) in keys.iter().rev().enumerate() {
            rks[i] = key;
        }
        return DES::feistel(block, rks);
    }
    fn encrypt(input: Vec<u64>, key: u64) -> Vec<u64> {
        let keys = DES::generate_round_keys(key);
        let mut result = vec![0u64; input.len()];
        for (i, &block) in input.iter().enumerate() {
            result[i] = DES::encrypt_block(block, keys);
        }
        return result;
    }
    fn decrypt(input: Vec<u64>, key: u64) -> Vec<u64> {
        let keys = DES::generate_round_keys(key);
        let mut result = vec![0u64; input.len()];
        for (i, &block) in input.iter().enumerate() {
            result[i] = DES::decrypt_block(block, keys);
        }
        return result;
    }
}
#[cfg(test)]
mod test {
    use crate::des::DES;
    #[test]
    fn test() {
        let result = DES::encrypt(vec![100], 50);
        println!("{:?}", result);
        let result = DES::decrypt(result, 50);
        println!("{:
        ?}", result);
    }
}

代码仅供参考学习使用(简单的实现),未经过严格测试,请不要直接用于生产用途,避免造成损失


算法实战

还是以Android作为例子,如果仅仅使用Java层,还是非常容易通过hook从而拿到加密算法和密钥,先来看一个简单的Java实现的例子:

private String encrypt(String plaintext, String key) {
    String ciphertext = "";
    try {
        DESKeySpec keySpec = new DESKeySpec(key.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        @SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, keyFactory.generateSecret(keySpec));
        ciphertext = Base64.encodeToString(cipher.doFinal(plaintext.getBytes()), Base64.DEFAULT);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ciphertext;
}

这里我采用了一个不安全的模式ECB, 并且不设置Padding), 默认输入的内容是满足分组要求的,这个模式是不需要初始向量的,并且各个分组之间也没有什么关系,有关模式的详细知识,给我自己挖一个坑,后续有时间单独写一篇文章来说明模式。

注意: 生产环境大家要采取安全的模式,不要用ECB!!! 这里仅做研究学习使用!!!

这里采取Java原生的写法实际上是很容易被看出来的,如果不加字符串混淆的话,直接搜索字符串即可拿到算法,添加字符串混淆也可以非常容易的通过hook来直接确定算法。

简单用frida写一个测试脚本,用xposed也可以实现,为了方便测试直接用frida写了。

function hook () {
  Java.perform(function () {
    const javax_crypto_Cipher_class = Java.use('javax.crypto.Cipher')
    javax_crypto_Cipher_class['getInstance'].overload('java.lang.String').implementation = function (alg) {
      console.log(alg)
      return this.getInstance(alg)
    }
    javax_crypto_Cipher_class['init'].overload('int', 'java.security.Key').implementation = function (mode, key) {
      console.log(mode, key.getEncoded())
      return this.init(mode, key)
    }
    javax_crypto_Cipher_class['doFinal'].overload('[B').implementation = function (content) {
      console.log(content)
      return this.doFinal(content)
    }
  })
}

这样,轻松的拿到了明文和密钥,如果想加强安全性,核心算法还是放到native层吧。下面用c++实现一下算法, 依然采用ECB/NoPadding的模式。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <android/log.h>
const unsigned char IP[] = {
        58, 50, 42, 34, 26, 18, 10, 2,
        60, 52, 44, 36, 28, 20, 12, 4,
        62, 54, 46, 38, 30, 22, 14, 6,
        64, 56, 48, 40, 32, 24, 16, 8,
        57, 49, 41, 33, 25, 17, 9, 1,
        59, 51, 43, 35, 27, 19, 11, 3,
        61, 53, 45, 37, 29, 21, 13, 5,
        63, 55, 47, 39, 31, 23, 15, 7
};
const unsigned char IP_REVERSE[] = {
        40, 8, 48, 16, 56, 24, 64, 32,
        39, 7, 47, 15, 55, 23, 63, 31,
        38, 6, 46, 14, 54, 22, 62, 30,
        37, 5, 45, 13, 53, 21, 61, 29,
        36, 4, 44, 12, 52, 20, 60, 28,
        35, 3, 43, 11, 51, 19, 59, 27,
        34, 2, 42, 10, 50, 18, 58, 26,
        33, 1, 41, 9, 49, 17, 57, 25
};
const unsigned char E[] = {
        32, 1, 2, 3, 4, 5,
        4, 5, 6, 7, 8, 9,
        8, 9, 10, 11, 12, 13,
        12, 13, 14, 15, 16, 17,
        16, 17, 18, 19, 20, 21,
        20, 21, 22, 23, 24, 25,
        24, 25, 26, 27, 28, 29,
        28, 29, 30, 31, 32, 1
};
const unsigned char BOX_LOOKUP[] = {
        0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23,
        8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31,
        32, 48, 33, 49, 34, 50, 35, 51, 36, 52, 37, 53, 38, 54, 39, 55,
        40, 56, 41, 57, 42, 58, 43, 59, 44, 60, 45, 61, 46, 62, 47, 63
};
const unsigned char BOXES[8][64] = {
        {
                14, 4,  13, 1,  2,  15, 11, 8,  3,  10, 6,  12, 5,  9,  0,  7,
                0,  15, 7,  4,  14, 2,  13, 1,  10, 6, 12, 11, 9,  5,  3,  8,
                4,  1,  14, 8,  13, 6,  2,  11, 15, 12, 9,  7,  3,  10, 5,  0,
                15, 12, 8,  2,  4,  9,  1,  7,  5,  11, 3,  14, 10, 0, 6,  13
        },
        {
                15, 1,  8,  14, 6,  11, 3,  4,  9,  7,  2,  13, 12, 0,  5,  10,
                3,  13, 4,  7,  15, 2,  8,  14, 12, 0, 1,  10, 6,  9,  11, 5,
                0,  14, 7,  11, 10, 4,  13, 1,  5,  8,  12, 6,  9,  3,  2,  15,
                13, 8,  10, 1,  3,  15, 4,  2,  11, 6,  7,  12, 0,  5, 14, 9
        },
        {
                10, 0,  9,  14, 6,  3,  15, 5,  1,  13, 12, 7,  11, 4,  2,  8,
                13, 7,  0,  9,  3,  4,  6,  10, 2,  8, 5,  14, 12, 11, 15, 1,
                13, 6,  4,  9,  8,  15, 3,  0,  11, 1,  2,  12, 5,  10, 14, 7,
                1,  10, 13, 0,  6,  9,  8,  7,  4,  15, 14, 3,  11, 5, 2,  12
        },
        {
                7,  13, 14, 3,  0,  6,  9,  10, 1,  2,  8,  5,  11, 12, 4,  15,
                13, 8,  11, 5,  6,  15, 0,  3,  4,  7, 2,  12, 1,  10, 14, 9,
                10, 6,  9,  0,  12, 11, 7,  13, 15, 1,  3,  14, 5,  2,  8,  4,
                3,  15, 0,  6,  10, 1,  13, 8,  9,  4,  5,  11, 12, 7, 2,  14
        },
        {
                2,  12, 4,  1,  7,  10, 11, 6,  8,  5,  3,  15, 13, 0,  14, 9,
                14, 11, 2,  12, 4,  7,  13, 1,  5,  0, 15, 10, 3,  9,  8,  6,
                4,  2,  1,  11, 10, 13, 7,  8,  15, 9,  12, 5,  6,  3,  0,  14,
                11, 8,  12, 7,  1,  14, 2,  13, 6,  15, 0,  9,  10, 4, 5,  3
        },
        {
                12, 1,  10, 15, 9,  2,  6,  8,  0,  13, 3,  4,  14, 7,  5,  11,
                10, 15, 4,  2,  7,  12, 9,  5,  6,  1, 13, 14, 0,  11, 3,  8,
                9,  14, 15, 5,  2,  8,  12, 3,  7,  0,  4,  10, 1,  13, 11, 6,
                4,  3,  2,  12, 9,  5,  15, 10, 11, 14, 1,  7,  6,  0, 8,  13
        },
        {
                4,  11, 2,  14, 15, 0,  8,  13, 3,  12, 9,  7,  5,  10, 6,  1,
                13, 0,  11, 7,  4,  9,  1,  10, 14, 3, 5,  12, 2,  15, 8,  6,
                1,  4,  11, 13, 12, 3,  7,  14, 10, 15, 6,  8,  0,  5,  9,  2,
                6,  11, 13, 8,  1,  4,  10, 7,  9,  5,  0,  15, 14, 2, 3,  12
        },
        {
                13, 2,  8,  4,  6,  15, 11, 1,  10, 9,  3,  14, 5,  0,  12, 7,
                1,  15, 13, 8,  10, 3,  7,  4,  12, 5, 6,  11, 0,  14, 9,  2,
                7,  11, 4,  1,  9,  12, 14, 2,  0,  6,  10, 13, 15, 3,  5,  8,
                2,  1,  14, 7,  4,  10, 8,  13, 15, 12, 9,  0,  3,  5, 6,  11
        },
};
const unsigned char P[] = {
        16, 7, 20, 21,
        29, 12, 28, 17,
        1, 15, 23, 26,
        5, 18, 31, 10,
        2, 8, 24, 14,
        32, 27, 3, 9,
        19, 13, 30, 6,
        22, 11, 4, 25
};
const unsigned char PC1[] = {
        57, 49, 41, 33, 25, 17, 9,
        1, 58, 50, 42, 34, 26, 18,
        10, 2, 59, 51, 43, 35, 27,
        19, 11, 3, 60, 52, 44, 36,
        63, 55, 47, 39, 31, 23, 15,
        7, 62, 54, 46, 38, 30, 22,
        14, 6, 61, 53, 45, 37, 29,
        21, 13, 5, 28, 20, 12, 4
};
const unsigned char PC2[] = {
        14, 17, 11, 24, 1, 5,
        3, 28, 15, 6, 21, 10,
        23, 19, 12, 4, 26, 8,
        16, 7, 27, 20, 13, 2,
        41, 52, 31, 37, 47, 55,
        30, 40, 51, 45, 33, 48,
        44, 49, 39, 56, 34, 53,
        46, 42, 50, 36, 29, 32
};
const unsigned char LSHIFTS[] = {
        1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
void u64to8(char msg[], uint64_t num) {
    for (int i = 0; i < 8; i++) {
        msg[i] = num >> (8 - 1 - i) * 8;
    }
}
uint64_t u8to64(const char a[]) {
    uint64_t n;
    n = (((uint64_t) a[0] << 56) & 0xFF00000000000000LLU)
        | (((uint64_t) a[1] << 48) & 0x00FF000000000000LLU)
        | (((uint64_t) a[2] << 40) & 0x0000FF0000000000LLU)
        | (((uint64_t) a[3] << 32) & 0x000000FF00000000LLU)
        | ((a[4] << 24) & 0x00000000FF000000LLU)
        | ((a[5] << 16) & 0x0000000000FF0000LLU)
        | ((a[6] << 8) & 0x000000000000FF00LLU)
        | (a[7] & 0x00000000000000FFLLU);
    return n;
}
uint64_t permute(uint64_t block, int block_size, const uint8_t permutation[], size_t size) {
    uint64_t result = 0;
    for (int i = 0; i < size; ++i) {
        result <<= 1;
        int p = block_size - permutation[i];
        result |= (block & (1ULL << p)) >> p;
    }
    return result;
}
void generate_round_keys(uint64_t key, uint64_t *keys) {
    uint64_t cd = permute(key, 64, PC1, 56);
    uint64_t data_mask = (1ULL << 55) | (1ULL << 27);
    // 这里如果用!有问题,写死固定值了
    uint64_t non_zero_mask = 72057593769492479;
    for (int i = 0; i < 16; ++i) {
        for (int j = 0; j < LSHIFTS[i]; ++j) {
            uint64_t data = (cd & data_mask) >> 27;
            cd = (cd << 1) & non_zero_mask | data;
        }
        keys[i] = permute(cd, 56, PC2, 48);
    }
}
uint64_t f(uint64_t block, uint64_t key) {
    uint64_t result = 0;
    uint64_t tmp = permute(block, 32, E, 48) ^key;
    uint64_t mask = 0b111111;
    for (int i = 0; i < 8; ++i) {
        uint64_t val = (tmp & (mask << (42 - (i * 6)))) >> (42 - (i * 6));
        result = (result << 4) | BOXES[i][BOX_LOOKUP[val]];
    }
    return permute(result, 32, P, 32);
}
uint64_t feistel(uint64_t block, const uint64_t *keys) {
    uint64_t lr = permute(block, 64, IP, 64);
    uint64_t l = (lr & 0xFFFFFFFF00000000LLU) >> 32;
    uint64_t r = lr & 0x00000000FFFFFFFFLLU;
    for (int i = 0; i < 16; ++i) {
        uint64_t tmp = l;
        l = r;
        r = tmp ^ f(r, keys[i]);
    }
    uint64_t switched = (r << 32) | l;
    return permute(switched, 64, IP_REVERSE, 64);
}
uint64_t encrypt_block(uint64_t block, uint64_t *keys) {
    return feistel(block, keys);
}
uint64_t decrypt_block(uint64_t block, const uint64_t *keys) {
    uint64_t rks[16] = {};
    for (int i = 15; i >= 0; --i) {
        rks[15 - i] = keys[i];
    }
    return feistel(block, rks);
}
/**
 * DES(NoPadding/ECB)
 * @param input
 * @param key
 * @param output
 */
void encrypt(const char *input, uint64_t key, char output[], size_t input_size) {
    size_t count = ceil(input_size / 8.0);
    uint64_t keys[16] = {0};
    generate_round_keys(key, keys);
    for (int i = 0; i < count; ++i) {
        uint64_t input_64 = u8to64(input);
        uint64_t stream = encrypt_block(input_64, keys);
        u64to8(output, stream);
        input += 8;
        output += 8;
    }
}

简单的做一个native层的调用,就直接贴代码了。

extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_littleq_cryptography_DESDemoActivity_nativeEncrypt(
        JNIEnv *env, jobject thiz,
        jbyteArray plaintext,
        jbyteArray key) {
    size_t plaintext_size = env->GetArrayLength(plaintext);
    auto *plaintext_dst = (jbyte *) malloc(plaintext_size * sizeof(jbyte));
    auto *key_dst = (jbyte *) malloc(8 * sizeof(jbyte));
    env->GetByteArrayRegion(plaintext, 0, plaintext_size, plaintext_dst);
    env->GetByteArrayRegion(key, 0, 8, key_dst);
    char *output = (char *) malloc(plaintext_size * sizeof(char));
    encrypt(reinterpret_cast<const char *>(plaintext_dst),
            u8to64(reinterpret_cast<const char *>(key_dst)), output,
            plaintext_size);
    jbyteArray byteArray = env->NewByteArray(plaintext_size);
    env->SetByteArrayRegion(byteArray, 0, plaintext_size, (jbyte *) output);
    return byteArray;
}

放到native层,分析出这个算法的难度就比较高了,直接通过hook系统函数的方法就失效了,如果这算法没魔改过的话,采用原始的DES实现,找S-Box是一个不错的切入点,比如直接用010Editor打开so, 然后搜索相关的字面量,由于我知道我这里用的是unsigned char, 所以没有其他补位,直接搜索0E 04 0D 01, 如果存储类型不为unsigned char, 搜索内容可能不一样,比如如果使用int进行存储,那么需要搜索0E 00 00 00 04 00 00 00 0D 00 00 00 01 00 00 00

image.gif

DES-S-Box

不过这个方案有一定的局限性,有这个不一定能确定他一定是DES算法,如果我把所有的算法的常量都给扔进去,然后随机用用,添加点无效指令,感觉这误导性还是蛮强的,实际分析so在这里我就不展开分析了,自己写的算法,分析起来,目的性还是挺强的,有兴趣的大佬可以自行尝试一下吧。


总结


如果加密算法完整放在Java层,对于逆向分析而言,我个人感觉分析起来还是比较容易的,直接hook Java层的函数,便可以快速的分析出算法。而对于放在native层的加密算法来说,可以通过S-Box的特征来大概确定一下算法(如果只凭借S-Box, 无法最终确定是不是DES),后续还需要一定的分析。最后,本文采用了不安全的分组模式ECB(仅供学习参考使用,实现简单,没有其他的干扰), 切记,不要用于生产系统,以免造成损失!!!

相关文章
|
6月前
|
Java 数据安全/隐私保护
des加密+base64编码,base64解码+des解密
des加密+base64编码,base64解码+des解密
132 0
|
6月前
|
算法 安全 C语言
使用C语言实现DES算法代码
使用C语言实现DES算法代码
202 0
|
6月前
|
算法 搜索推荐 Java
DES - 对称加密算法简要介绍与JAVA实现
DES - 对称加密算法简要介绍与JAVA实现
111 2
|
2月前
|
存储 安全 数据安全/隐私保护
浅谈对称加密(AES与DES)
浅谈对称加密(AES与DES)
|
3月前
|
算法 JavaScript 前端开发
对称加密算法解析:DES、AES及其在`pycryptodome` 和 `crypto-js` 模块中的应用
对称加密算法解析:DES、AES及其在`pycryptodome` 和 `crypto-js` 模块中的应用
178 1
|
5月前
|
存储 算法 安全
Java中的DES和3DES加密算法详解
Java中的DES和3DES加密算法详解
|
5月前
|
Java C# 数据安全/隐私保护
|
4月前
|
C# 数据安全/隐私保护
Des加密和解密
Des加密和解密
52 0
|
6月前
|
安全 算法 数据库
MD5、SHA、DES、AES、RSA的算法说明
【5月更文挑战第10天】MD5、SHA、DES、AES、RSA的算法说明
318 2
|
6月前
|
存储 算法 安全
加密解密(DES)
加密解密(DES)
下一篇
无影云桌面