LOADING

加载过慢请开启缓存 浏览器默认开启

常见的加密和编码

Base64

一种基于64个可打印的字符来表示二进制的数据的一种方法。

https://cloud.tencent.com/developer/article/1868972

动画演示

总的来说就是把原数据的二进制按3字节分块,后再按6位二进制分成4块,然后在每组二进制前面补00,变成8位(一块二进制内少于6位在末尾补0,三字节数据块内存在空字节就用等号补上。),按分块的二进制表示的值为索引到表中去寻找对应的元素。

解码时按4字节到表中寻找元素后计算索引按三字节恢复就行。

源码解读,来自

https://gitcode.com/g310773517/base64/overview?isLogin=1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define _CRT_SECURE_NO_WARNINGS

//base64查询表
char* base64_encodetable = (char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//base64反查询表
char base64_decodetable[128] = {     //把结果的索引与结果进行映射,可以直接按下标查找
    -1, -1, -1, -1, -1, -1, -1, -1, //0-7
    -1, -1, -1, -1, -1, -1, -1, -1, //8-15
    -1, -1, -1, -1, -1, -1, -1, -1, //16-23
    -1, -1, -1, -1, -1, -1, -1, -1, //24-31
    -1, -1, -1, -1, -1, -1, -1, -1, //32-39
    -1, -1, -1, 62, -1, -1, -1, 63, //40-47, + /
    52, 53, 54, 55, 56, 57, 58, 59, //48-55, 0-7
    60, 61, -1, -1, -1,  0, -1, -1, //56-63, 8-9
    -1,  0,  1,  2,  3,  4,  5,  6, //64-71, A-G
     7,  8,  9, 10, 11, 12, 13, 14, //72-79, H-O
    15, 16, 17, 18, 19, 20, 21, 22, //80-87, P-W
    23, 24, 25, -1, -1, -1, -1, -1, //88-95, X-Z
    -1, 26, 27, 28, 29, 30, 31, 32, //96-103, a-g
    33, 34, 35, 36, 37, 38, 39, 40, //104-111, h-o
    41, 42, 43, 44, 45, 46, 47, 48, //112-119, p-w
    49, 50, 51, -1, -1, -1, -1, -1, //120-127, x-z
};

//数字字符串表
static char* num_table = (char*)"0123456789";

//char类型数转换为2进制字符串格式, 如2->"10", 6->"110"
static char* char2binstr(char value)
{
    int i = 0;
    char binstr[9] = {};

    //取到每一位之后,查表得到对应的字符拼接成一个字符串
    for (i = 0; i < 8; i++)
        binstr[7 - i] = num_table[(value & (0x1 << i)) >> i];

    return _strdup(binstr);
}

//2进制字符串格式数据转换为char类型数,如"10"->2, "110"->6
static char binstr2char(char* binstr)
{
    int i = 0;
    char value = 0;
    int length = 0;

    if (!binstr)
        return 0;

    length = strlen(binstr);

    //取得2进制字符串的每一个字节,将其转化对应的数字,然后还原数据。
    for (i = 0; i < length; i++)
        value += (binstr[length - 1 - i] - 0x30) << i;

    return value;
}

/*
 * Base64编码说明
 *  Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
 *  如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='。
 *  Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。
 */
static char* _base64_section_encode(char* src, int length)
{
    int i = 0;
    char dest[5] = "====";
    char binstr[33] = "000000000000000000000000";
    char tmp[7] = {};
    char* tmp1 = NULL;

    //每字节8位,每次转换3字节,不足3字节时,高位补0
    //先拼接一个完整的24字节2进制字符串
    for (i = 0; i < length; i++) {
        tmp1 = char2binstr(src[i]);
        strncpy((char*)binstr + i * 8, tmp1, 8);
        free(tmp1);
    }

    //24字节,分4组,每组6字节,将每组转换为数字格式,每个数字查表得到对应码。
    //3种情况:
    // 1. 如果最后剩下1个数据,编码结果后加2个=,即查表2次,查表次数正好是数据字节数 + 1
    // 2. 如果最后剩下2个数据,编码结果后加1个=,即查表3次
    // 3. 如果没有剩下任何数据(剩下3个数据),就什么都不要加,即查表4次
    for (i = 0; i < length + 1; i++) {
        strncpy(tmp, binstr + 6 * i, 6);
        dest[i] = base64_encodetable[binstr2char(tmp)];
    }

    return _strdup(dest);
}

//输入原始内容和长度,得到base64编码
char* base64_encode(char* src, int length)
{
    int i = 0;
    char* base64code = NULL;
    int codelength = 0;
    char* tmp = NULL;

    //每3个字节一组转化为4字节(每6位一组得到4组,转换为新字符),不足3字节的填充到3字节进行转化。
    //因此计算共有多少组转换后的4字节乘以4就是编码后的长度
    codelength = ((length / 3) + (length % 3 > 0 ? 1 : 0)) * 4;
    base64code = (char*)malloc(codelength + 1);
    memset(base64code, 0, codelength + 1);

    for (i = 0; i < length / 3; i++) {
        tmp = _base64_section_encode(src + i * 3, 3);
        strcat(base64code, tmp);
        free(tmp);
    }

    if (length % 3) {
        tmp = _base64_section_encode(src + length - (length % 3), length % 3);
        strcat(base64code, tmp);
        free(tmp);
    }
    return base64code;
}

//4字节反转化为3字节
static char* _base64_section_decode(char* dest)
{
    int i = 0;
    int j = 0;
    static char src[3] = {};
    char tmp[9] = {};

    memset(src, 0, sizeof(src) / sizeof(src[0]));
    for (i = 0; i < 3; i++) {
        strncpy(tmp, dest + 8 * i, 8);
        src[i] = binstr2char(tmp);
    }

    return src;
}

int base64_decode_length(char* src)
{
    int length = 0;
    int padding_count = 0;

    if (src[strlen(src) - 1] == '=')
        padding_count++;

    if (src[strlen(src) - 2] == '=')
        padding_count++;

    length = strlen(src) / 4 * 3 - padding_count;
    return 0;
}

//输入base64编码后文本,解码出原始内容
char* base64_decode(char* src, int* destlen)
{
    int i = 0;
    int j = 0;
    char* binstr = NULL;
    int binstr_length = 0;
    char* tmp = NULL;
    char* tmp1 = NULL;
    char* dest = NULL;
    int cnt = 0;

    binstr_length = strlen(src) * 8 + 1;
    binstr = (char*)malloc(binstr_length);
    memset(binstr, 0, binstr_length);

    *destlen = base64_decode_length(src);
    dest = (char*)malloc(*destlen);
    memset(dest, 0, *destlen);

    for (i = 0; i < strlen(src) / 4; i++) {
        for (j = 0; j < 4; j++) {
            tmp1 = char2binstr(base64_decodetable[src[i * 4 + j]]);
            strcat(binstr, tmp1 + 2);
            free(tmp1);
        }
    }

    for (i = 0; i < strlen(src) / 4; i++) {
        //因为_base64_section_decode函数每次固定返回3字节数据,此处需要根据提前计算出来的解码总长度和已解码长度来判断最后1次需要拷贝的准确数据量。
        tmp = _base64_section_decode(binstr + i * 24);
        memmove(dest + i * 3, tmp, *destlen - cnt < 3 ? *destlen - cnt : 3);
        cnt += 3;
    }

    free(binstr);

    return dest;
}
int main() {
    char a[10] = "systema";
    char * b = base64_encode(a, strlen(a));
    printf("%s", b);
}

一般形式(特征)

/*base64.c*/  
#include "base64.h"  
  
unsigned char *base64_encode(unsigned char *str)  
{  
    long len;  
    long str_len;  
    unsigned char *res;  
    int i,j;  
//定义base64编码表  
    unsigned char *base64_table=(unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
  
//计算经过base64编码后的字符串长度  
    str_len=strlen(str);  
    if(str_len % 3 == 0)  
        len=str_len/3*4;  
    else  
        len=(str_len/3+1)*4;  
  
    res=(unsigned char*)malloc(sizeof(unsigned char)*len+1);  
    res[len]='\0';  
  
//以3个8位字符为一组进行编码  
    for(i=0,j=0;i<len-2;j+=3,i+=4)  
    {  
        res[i]=base64_table[str[j]>>2]; //取出第一个字符的前6位并找出对应的结果字符  
        res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)]; //将第一个字符的后位与第二个字符的前4位进行组合并找到对应的结果字符  
        res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)]; //将第二个字符的后4位与第三个字符的前2位组合并找出对应的结果字符  
        res[i+3]=base64_table[str[j+2]&0x3f]; //取出第三个字符的后6位并找出结果字符  
    }  
  
    switch(str_len % 3)  
    {  
        case 1:  
            res[i-2]='=';  
            res[i-1]='=';  
            break;  
        case 2:  
            res[i-1]='=';  
            break;  
    }  
  
    return res;  
}  
  
unsigned char *base64_decode(unsigned char *code)  
{  
//根据base64表,以字符找到对应的十进制数据  
    int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,62,0,0,0,
             63,52,53,54,55,56,57,58,
             59,60,61,0,0,0,0,0,0,0,0,
             1,2,3,4,5,6,7,8,9,10,11,12,
             13,14,15,16,17,18,19,20,21,
             22,23,24,25,0,0,0,0,0,0,26,
             27,28,29,30,31,32,33,34,35,
             36,37,38,39,40,41,42,43,44,
             45,46,47,48,49,50,51
               };  
    long len;  
    long str_len;  
    unsigned char *res;  
    int i,j;  
  
//计算解码后的字符串长度  
    len=strlen(code);  
//判断编码后的字符串后是否有=  
    if(strstr(code,"=="))  
        str_len=len/4*3-2;  
    else if(strstr(code,"="))  
        str_len=len/4*3-1;  
    else  
        str_len=len/4*3;  
  
    res=malloc(sizeof(unsigned char)*str_len+1);  
    res[str_len]='\0';  
  
//以4个字符为一位进行解码  
    for(i=0,j=0;i < len-2;j+=3,i+=4)  
    {  
        res[j]=((unsigned char)table[code[i]])<<2 | (((unsigned char)table[code[i+1]])>>4); //取出第一个字符对应base64表的十进制数的前6位与第二个字符对应base64表的十进制数的后2位进行组合  
        res[j+1]=(((unsigned char)table[code[i+1]])<<4) | (((unsigned char)table[code[i+2]])>>2); //取出第二个字符对应base64表的十进制数的后4位与第三个字符对应bas464表的十进制数的后4位进行组合  
        res[j+2]=(((unsigned char)table[code[i+2]])<<6) | ((unsigned char)table[code[i+3]]); //取出第三个字符对应base64表的十进制数的后2位与第4个字符进行组合  
    }  
  
    return res;  
  
}  

Salsa20

需要的数据:8字节的随机数noce,128位或256位的密钥,不定长度明文/密文,一个16字节密钥拓展常量”expand 32-byte k”,一个常量[0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]。

加密流程,密钥拓展->把拓展的密钥和随机数,计数器,常量排列成一个4x4的矩阵,每个元素4字节->行列分别多轮加密每行/列3个为一组加密->把矩阵拆分成单字节的盒->明文与盒异或

加解密脚本

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

enum s20_status_t  //返回加解密结果状态
{
    S20_SUCCESS,
    S20_FAILURE
};

enum s20_keylen_t  //salsa20支持128和256位密钥
{
    S20_KEYLEN_256,
    S20_KEYLEN_128
};

static uint32_t rotl(uint32_t value, int shift)   //32位数据位移交换操作
{
    return (value << shift) | (value >> (32 - shift));
}

static void s20_quarterround(uint32_t* y0, uint32_t* y1, uint32_t* y2, uint32_t* y3)  //行列加密逻辑
{
    *y1 = *y1 ^ rotl(*y0 + *y3, 7);
    *y2 = *y2 ^ rotl(*y1 + *y0, 9);
    *y3 = *y3 ^ rotl(*y2 + *y1, 13);
    *y0 = *y0 ^ rotl(*y3 + *y2, 18);
}

static void s20_rowround(uint32_t y[16])  //列加密
{
    s20_quarterround(&y[0], &y[1], &y[2], &y[3]);
    s20_quarterround(&y[5], &y[6], &y[7], &y[4]);
    s20_quarterround(&y[10], &y[11], &y[8], &y[9]);
    s20_quarterround(&y[15], &y[12], &y[13], &y[14]);
}

static void s20_columnround(uint32_t x[16])    //行加密
{
    s20_quarterround(&x[0], &x[4], &x[8], &x[12]);
    s20_quarterround(&x[5], &x[9], &x[13], &x[1]);
    s20_quarterround(&x[10], &x[14], &x[2], &x[6]);
    s20_quarterround(&x[15], &x[3], &x[7], &x[11]);
}

static void s20_doubleround(uint32_t x[16])  //行列加密,这次就是一轮加密
{
    s20_columnround(x);
    s20_rowround(x);
}

// Creates a little-endian word from 4 bytes pointed to by b
static uint32_t s20_littleendian(uint8_t* b)  //端序转化,字节数据转小端序4字节
{
    return b[0] +
        ((uint_fast16_t)b[1] << 8) +
        ((uint_fast32_t)b[2] << 16) +
        ((uint_fast32_t)b[3] << 24);
}

// Moves the little-endian word into the 4 bytes pointed to by b
static void s20_rev_littleendian(uint8_t* b, uint32_t w)  //4字节按小端序转字节数据
{
    b[0] = w;
    b[1] = w >> 8;
    b[2] = w >> 16;
    b[3] = w >> 24;
}

// The core function of Salsa20
static void s20_hash(uint8_t seq[64])
{
    int i;
    uint32_t x[16];
    uint32_t z[16];

    for (i = 0; i < 16; ++i)
        x[i] = z[i] = s20_littleendian(seq + (4 * i)); //转成4字节分组加密

    for (i = 0; i < 10; ++i)  //轮加密
        s20_doubleround(z);

    for (i = 0; i < 16; ++i) {
        z[i] += x[i];
        s20_rev_littleendian(seq + (4 * i), z[i]);  //转成单字节生成盒
    }
}

// The 16-byte (128-bit) key expansion function
static void s20_expand16(uint8_t* k,  //密钥拓展
    uint8_t n[16],
    uint8_t keystream[64])
{
    int i, j;
    // The constants specified by the Salsa20 specification, 'tau'
    // "expand 16-byte k"
    uint8_t t[4][4] = {
      { 'e', 'x', 'p', 'a' },
      { 'n', 'd', ' ', '1' },
      { '6', '-', 'b', 'y' },
      { 't', 'e', ' ', 'k' }
    };

    // Copy all of 'tau' into the correct spots in our keystream block
    for (i = 0; i < 64; i += 20)
        for (j = 0; j < 4; ++j)
            keystream[i + j] = t[i / 20][j];

    // Copy the key and the nonce into the keystream block
    for (i = 0; i < 16; ++i) {
        keystream[4 + i] = k[i];
        keystream[44 + i] = k[i];
        keystream[24 + i] = n[i];
    }

    s20_hash(keystream);
}


// The 32-byte (256-bit) key expansion function
static void s20_expand32(uint8_t* k,
    uint8_t n[16],
    uint8_t keystream[64])
{
    int i, j;
    // The constants specified by the Salsa20 specification, 'sigma'
    // "expand 32-byte k"
    uint8_t o[4][4] = {
      { 'e', 'x', 'p', 'a' },
      { 'n', 'd', ' ', '3' },
      { '2', '-', 'b', 'y' },
      { 't', 'e', ' ', 'k' }
    };

    // Copy all of 'sigma' into the correct spots in our keystream block
    for (i = 0; i < 64; i += 20)
        for (j = 0; j < 4; ++j)
            keystream[i + j] = o[i / 20][j];

    // Copy the key and the nonce into the keystream block
    for (i = 0; i < 16; ++i) {
        keystream[4 + i] = k[i];
        keystream[44 + i] = k[i + 16];
        keystream[24 + i] = n[i];
    }

    s20_hash(keystream);
}

enum s20_status_t s20_crypt(  //加解密函数
    uint8_t* key,    //密钥
    enum s20_keylen_t keylen,  //密钥长度
    uint8_t nonce[8],   //随机数
    uint32_t si,   //密钥偏移量,决定从什么时候使用密钥
    uint8_t* buf,  //要输入的数据
    uint32_t buflen)  //数去的长度
{
    uint8_t keystream[64];  //密钥流,盒
    uint8_t n[16] = { 0 };  //随机数和计数器
    uint32_t i;

    void (*expand)(uint8_t*, uint8_t*, uint8_t*) = NULL;
    if (keylen == S20_KEYLEN_256)
        expand = s20_expand32;
    if (keylen == S20_KEYLEN_128)
        expand = s20_expand16;      

    if (expand == NULL || key == NULL || nonce == NULL || buf == NULL)
        return S20_FAILURE;

    for (i = 0; i < 8; ++i)
        n[i] = nonce[i];  //生成随机数与计数器

    if (si % 64 != 0) {
        s20_rev_littleendian(n + 8, si / 64);
        (*expand)(key, n, keystream); 
    }

    for (i = 0; i < buflen; ++i) {
        if ((si + i) % 64 == 0) {
            s20_rev_littleendian(n + 8, ((si + i) / 64));
            (*expand)(key, n, keystream);  //密钥拓展,生成盒
        }

        buf[i] ^= keystream[(si + i) % 64];  //与原数据异或加密
    }

    return S20_SUCCESS;
}

int main() {

    uint8_t key[32] = { 0x59, 0x6F, 0x75, 0x74, 0x68, 0x20, 0x53, 0x74,0x72,0x65, 0x6E, 0x67, 0x74, 0x68, 0x65, 0x6E, 0x73,0x20, 0x74, 0x68, 0x65, 0x20, 0x4E, 0x61,0x74, 0x69, 0x6F, 0x6E };
    uint8_t nonce[8] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
    unsigned char data[48] = {
    0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2,
    0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58,
    0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1, 0xFD, 0xF7, 0xFF, 0x7F, 0x0, 0x0
    };

    printf("%d",s20_crypt(key, S20_KEYLEN_256, nonce, 0, (uint8_t*)data, 48));

    for (int i = 0; i < 48; i++) {
        printf("%c", data[i]);
    }
}

参考ChaCha20加密 与 Salsa20加密 - TLSN - 博客园

【动画密码学】Salsa20|流密码_哔哩哔哩_bilibili

alexwebr/salsa20: An implementation of the Salsa20 stream cipher in C99


ChaCha20

需要的数据:一个计数器counter一般为0,8字节的随机数noce,128位或256位的密钥,不定长度明文/密文,一个16字节密钥拓展常量”expand 32-byte k”,。

加密流程,密钥拓展->把拓展的密钥和随机数,计数器,常量排列成一个4x4的矩阵(顺序排列),每个元素4字节->列加密,斜对角加密,相比salsa20还多了一次异或->把矩阵拆分成单字节的盒->明文与盒异或.

加解密脚本

#include <stdint.h>
#include <string.h>
#include <stdio.h>

static inline void u32t8le(uint32_t v, uint8_t p[4]) {
    p[0] = v & 0xff;
    p[1] = (v >> 8) & 0xff;
    p[2] = (v >> 16) & 0xff;
    p[3] = (v >> 24) & 0xff;
}

static inline uint32_t u8t32le(uint8_t p[4]) {
    uint32_t value = p[3];

    value = (value << 8) | p[2];
    value = (value << 8) | p[1];
    value = (value << 8) | p[0];

    return value;
}

static inline uint32_t rotl32(uint32_t x, int n) {
    // http://blog.regehr.org/archives/1063
    return x << n | (x >> (-n & 31));
}

// https://tools.ietf.org/html/rfc7539#section-2.1
static void chacha20_quarterround(uint32_t* x, int a, int b, int c, int d) {
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16);
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12);
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8);
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);
}

static void chacha20_serialize(uint32_t in[16], uint8_t output[64]) {
    int i;
    for (i = 0; i < 16; i++) {
        u32t8le(in[i], output + (i << 2));
    }
}

static void chacha20_block(uint32_t in[16], uint8_t out[64], int num_rounds) { // num_rounds 一般为20 
    int i;
    uint32_t x[16];

    memcpy(x, in, sizeof(uint32_t) * 16);

    for (i = num_rounds; i > 0; i -= 2) {
        //odd round
        chacha20_quarterround(x, 0, 4, 8, 12);
        chacha20_quarterround(x, 1, 5, 9, 13);
        chacha20_quarterround(x, 2, 6, 10, 14);
        chacha20_quarterround(x, 3, 7, 11, 15);
        //even round 
        chacha20_quarterround(x, 0, 5, 10, 15);
        chacha20_quarterround(x, 1, 6, 11, 12);
        chacha20_quarterround(x, 2, 7, 8, 13);
        chacha20_quarterround(x, 3, 4, 9, 14);
    }

    for (i = 0; i < 16; i++) {
        x[i] += in[i];
    }

    chacha20_serialize(x, out);
}

// https://tools.ietf.org/html/rfc7539#section-2.3
static void chacha20_init_state(uint32_t s[16], uint8_t key[32], uint32_t counter, uint8_t nonce[12]) {
    int i;

    // refer: https://dxr.mozilla.org/mozilla-beta/source/security/nss/lib/freebl/chacha20.c
    // convert magic number to string: "expand 32-byte k"
    s[0] = 0x61707865;
    s[1] = 0x3320646e;
    s[2] = 0x79622d32;
    s[3] = 0x6b206574;

    for (i = 0; i < 8; i++) {
        s[4 + i] = u8t32le(key + i * 4);
    }

    s[12] = counter;

    for (i = 0; i < 3; i++) {
        s[13 + i] = u8t32le(nonce + i * 4);
    }
}

void ChaCha20XOR(uint8_t key[32], uint32_t counter, uint8_t nonce[12], uint8_t* in, uint8_t* out, int inlen) {
    int i, j;

    uint32_t s[16];
    uint8_t block[64];

    chacha20_init_state(s, key, counter, nonce);

    for (i = 0; i < inlen; i += 64) {
        chacha20_block(s, block, 20);
        s[12]++;

        for (j = i; j < i + 64; j++) {
            if (j >= inlen) {
                break;
            }
            out[j] = in[j] ^ block[j - i];
        }
    }
}

int main(int argc, char** argv) {
    int i;

    uint8_t nonce[8] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
    unsigned char data[48] = {
    0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2,
    0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58,
    0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1, 0xFD, 0xF7, 0xFF, 0x7F, 0x0, 0x0
    };

    uint8_t key[32] = { 0x59, 0x6F, 0x75, 0x74, 0x68, 0x20, 0x53, 0x74, 0x72, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x65, 0x6E,
    0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E ,0x0,0x0,0x0,0x0 };

    uint8_t encrypt[114];

    ChaCha20XOR(key, 0, nonce, data, encrypt, 48); 
    
    for (int i = 0; i < 48; i++) {
        printf("%c", encrypt[i]);
    }
   
   
    return 0;
}
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

struct chacha20_context
{
    uint32_t keystream32[16];
    size_t position;

    uint8_t key[32];
    uint8_t nonce[12];
    uint64_t counter;

    uint32_t state[16];
};

void chacha20_init_context(struct chacha20_context* ctx, uint8_t key[], uint8_t nounc[], uint64_t counter);

void chacha20_xor(struct chacha20_context* ctx, uint8_t* bytes, size_t n_bytes);


static uint32_t rotl32(uint32_t x, int n)
{
    return (x << n) | (x >> (32 - n));
}

static uint32_t pack4(const uint8_t* a)
{
    uint32_t res = 0;
    res |= (uint32_t)a[0] << 0 * 8;
    res |= (uint32_t)a[1] << 1 * 8;
    res |= (uint32_t)a[2] << 2 * 8;
    res |= (uint32_t)a[3] << 3 * 8;
    return res;
}

static void unpack4(uint32_t src, uint8_t* dst) {
    dst[0] = (src >> 0 * 8) & 0xff;
    dst[1] = (src >> 1 * 8) & 0xff;
    dst[2] = (src >> 2 * 8) & 0xff;
    dst[3] = (src >> 3 * 8) & 0xff;
}

static void chacha20_init_block(struct chacha20_context* ctx, uint8_t key[], uint8_t nonce[])
{
    memcpy(ctx->key, key, sizeof(ctx->key));
    memcpy(ctx->nonce, nonce, sizeof(ctx->nonce));

    const uint8_t* magic_constant = (uint8_t*)"expand 32-byte k";
    ctx->state[0] = pack4(magic_constant + 0 * 4);
    ctx->state[1] = pack4(magic_constant + 1 * 4);
    ctx->state[2] = pack4(magic_constant + 2 * 4);
    ctx->state[3] = pack4(magic_constant + 3 * 4);
    ctx->state[4] = pack4(key + 0 * 4);
    ctx->state[5] = pack4(key + 1 * 4);
    ctx->state[6] = pack4(key + 2 * 4);
    ctx->state[7] = pack4(key + 3 * 4);
    ctx->state[8] = pack4(key + 4 * 4);
    ctx->state[9] = pack4(key + 5 * 4);
    ctx->state[10] = pack4(key + 6 * 4);
    ctx->state[11] = pack4(key + 7 * 4);
    // 64 bit counter initialized to zero by default.
    ctx->state[12] = 0;
    ctx->state[13] = pack4(nonce + 0 * 4);
    ctx->state[14] = pack4(nonce + 1 * 4);
    ctx->state[15] = pack4(nonce + 2 * 4);

    memcpy(ctx->nonce, nonce, sizeof(ctx->nonce));
}

static void chacha20_block_set_counter(struct chacha20_context* ctx, uint64_t counter)
{
    ctx->state[12] = (uint32_t)counter;
    ctx->state[13] = pack4(ctx->nonce + 0 * 4) + (uint32_t)(counter >> 32);
}

static void chacha20_block_next(struct chacha20_context* ctx) {
    // This is where the crazy voodoo magic happens.
    // Mix the bytes a lot and hope that nobody finds out how to undo it.
    for (int i = 0; i < 16; i++) ctx->keystream32[i] = ctx->state[i];

#define CHACHA20_QUARTERROUND(x, a, b, c, d) \
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); \
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); \
    x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); \
    x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);

    for (int i = 0; i < 10; i++)
    {
        CHACHA20_QUARTERROUND(ctx->keystream32, 0, 4, 8, 12)
            CHACHA20_QUARTERROUND(ctx->keystream32, 1, 5, 9, 13)
            CHACHA20_QUARTERROUND(ctx->keystream32, 2, 6, 10, 14)
            CHACHA20_QUARTERROUND(ctx->keystream32, 3, 7, 11, 15)
            CHACHA20_QUARTERROUND(ctx->keystream32, 0, 5, 10, 15)
            CHACHA20_QUARTERROUND(ctx->keystream32, 1, 6, 11, 12)
            CHACHA20_QUARTERROUND(ctx->keystream32, 2, 7, 8, 13)
            CHACHA20_QUARTERROUND(ctx->keystream32, 3, 4, 9, 14)
    }

    for (int i = 0; i < 16; i++) ctx->keystream32[i] += ctx->state[i];

    uint32_t* counter = ctx->state + 12;
    // increment counter
    counter[0]++;
    if (0 == counter[0])
    {
        // wrap around occured, increment higher 32 bits of counter
        counter[1]++;
        // Limited to 2^64 blocks of 64 bytes each.
        // If you want to process more than 1180591620717411303424 bytes
        // you have other problems.
        // We could keep counting with counter[2] and counter[3] (nonce),
        // but then we risk reusing the nonce which is very bad.
        assert(0 != counter[1]);
    }
}

void chacha20_init_context(struct chacha20_context* ctx, uint8_t key[], uint8_t nonce[], uint64_t counter)
{
    memset(ctx, 0, sizeof(struct chacha20_context));

    chacha20_init_block(ctx, key, nonce);
    chacha20_block_set_counter(ctx, counter);

    ctx->counter = counter;
    ctx->position = 64;
}

void chacha20_xor(struct chacha20_context* ctx, uint8_t* bytes, size_t n_bytes)
{
    uint8_t* keystream8 = (uint8_t*)ctx->keystream32;
    for (size_t i = 0; i < n_bytes; i++)
    {
        if (ctx->position >= 64)
        {
            chacha20_block_next(ctx);
            for (int i = 0; i < 64; i++) {
                printf("%.2x,", keystream8[i]);
            }
            ctx->position = 0;
        }
        bytes[i] ^= keystream8[ctx->position];
        ctx->position++;
    }
}


int main() {

   
    uint8_t nonce[8] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
    unsigned char data[48] = {
    0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2,
    0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58,
    0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1, 0xFD, 0xF7, 0xFF, 0x7F, 0x0, 0x0
    };

    uint8_t key[32] = { 0x59, 0x6F, 0x75, 0x74, 0x68, 0x20, 0x53, 0x74, 0x72, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x65, 0x6E,
    0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E ,0x0,0x0,0x0,0x0};
    
    struct chacha20_context ctx;

    chacha20_init_context(&ctx, key, nonce, 0);
    chacha20_xor(&ctx,(uint8_t*) data,48);

    printf("\n");
    for (int i = 0; i < 48; i++) {
        printf("%.2x,", data[i]);
    }
}

Ginurx/chacha20-c: ChaCha20 stream cipher implemented in C

ChaCha20加密 与 Salsa20加密 - TLSN - 博客园


RC4

https://blog.csdn.net/xiao__1bai/article/details/123357156

演示动画

对称加密算法,可以用写内存解决。

单字节加密。

大概流程:初始化盒S,用密钥初始化盒子T,用S和T的值做为索引组合交换盒S中的内容打乱S盒,遍历要加密的数据,再次用S盒中的数据作为下标和S盒内的数据进行交换打乱,把S盒中的数据相加作为索引在S盒中寻找数据与原数据进行异或。

代码解读

来自上面的链接

#include<stdio.h>
 
/*
程序实现时,需要注意的是,状态向量数组S和临时向量数组T
的类型应设为unsigned char,而不是char。因为在一些机器
下,将char默认做为signed char看待,在算法中计算下标i,j的
时候,会涉及char转int,如果是signed的char,那么将char的8
位拷贝到int的低8位后,还会根据char的符号为,在int的高位补
0或1。由于密钥是随机产生的,如果遇到密钥的某个字节的高
位为1的话,那么计算得到的数组下标为负数,就会越界。
*/
typedef struct _RC4INFO
{
    unsigned char s_box[256];
    unsigned char t_box[256];
}RC4_INFO,*PRC4_INFO;			//定义 RC4 中要用到的 S-Box 和临时向量 T,封装在结构体中并给正常别名和指针别名。 
 
 
/*
初始化函数 需要传入key 以及 keylen
主要有几个步骤
1.初始化Sbox
2.将key填充到Tbox中
3.组合sbox[i] 与 tbox[i] 然后进行交换
*/
void rc4_init(PRC4_INFO prc4,unsigned char key[],unsigned int keylen)
{
    int i=0;
    int j=0;
    unsigned char tmp;
    if(prc4==NULL)
    {
        return;
    }
    
    /*
    初始化存储0-255字节的Sbox(其实就是一个数组)
    填充key到256个字节数组中称为Tbox(你输入的key不满256个字节则初始化到256个字节)
    */ 
    for(i=0;i<256;i++)
    {
        prc4->s_box[i] = i;
        prc4->t_box[i] = key[i % keylen];	//如果密钥的长度是256字节,就直接把密钥的值赋给k,否则,轮转地将密钥的每个字节赋给k 
    }
    
    //交换s[i]与s[j]   i 从0开始一直到255下标结束. j是 s[i]与T[i]组合得出的下标
    for(i=0;i<256;i++)
    {
        j=(j+prc4->s_box[i]+prc4->t_box[i])%256;
        //开始交换
        tmp=prc4->s_box[i];
        prc4->s_box[i]=prc4->s_box[j];
        prc4->s_box[j]=tmp;
    }
 } 
 
/*
RC4加密其实就是遍历数据,将数据与sbox进行异或加密,而在此之前还需要交换一次sbox的数据
交换完之后 再把s[i] + s[j]的组合当做下标再去异或. 
*/
void rc4_crypt(unsigned char data[],unsigned int datalen,unsigned char key[],unsigned int keylen)	//参数分别是要加密的数据、要加密的数据长度、加密数据所用的Key、加密数据所用的key长度
{
    int dn=0;	//data[n]的意思
    int i=0;
    int j=0;	//i j分别用于交换sbox[i] 和 sbox[j]
    int t=0;	//t = s[i] +s[j]
    unsigned char tmp;
    
    RC4_INFO rc4;		//这里定义前面的结构题 
    rc4_init(&rc4,key,keylen);		//在加密函数中调用初始化函数,就省去了其它代码中出现的要保存初始化 sBox 的现象了.
    
    for(dn=0;dn<datalen;dn++)
    {
        //i确保S-box的每个元素都得到处理,j保证S-box的搅乱是随机的。
        i=(i+1)%256;
        j=(j+rc4.s_box[i])%256;
        
        //交换 s_box[i] 和 s_box[j]
        tmp=rc4.s_box[i];
        rc4.s_box[i] = rc4.s_box[j];
        rc4.s_box[j] = tmp;
        
        //交换完之后 再把s[i] + s[j]的组合当做下标再去异或.
        t = (rc4.s_box[i] + rc4.s_box[j]) % 256;
        data[dn] ^= rc4.s_box[t];
     } 
}
 
void EntryBuffer(unsigned char data[],unsigned int datalen)
{
    unsigned char key[]="pjrHeldsadf";
    rc4_crypt(data,datalen,key,sizeof(key)/sizeof(key[0]));
}
 
int main()
{
    char Hell[] = "黄河远上白云间";
    printf("加密前:pData=%s\n\n", Hell);
    EntryBuffer((unsigned char*)Hell,sizeof(Hell)/sizeof(Hell[0])); //加第一次调用就是加密 
    printf("加密后:pData=%s\n\n", Hell);
    EntryBuffer((unsigned char*)Hell,sizeof(Hell)/sizeof(Hell[0])); //由于异或运算的对合性,RC4加密解密使用同一套算法。
    printf("解密后:pData=%s\n\n", Hell);
    return 0;
}

一般形式(特征)

#include<stdio.h> 
#include<stdlib.h>
#include<string.h>

//s 表的长度取 256
#define size 256

unsigned char sbox[257] = { 0 };

// 初始化 s 表
void init_sbox(unsigned char* key) {
    unsigned int i, j, k;
    int tmp;

    for (i = 0; i < size; i++) {
        sbox[i] = i;
    }

    j =k = 0;
    for (i = 0; i < size; i++) {
        tmp = sbox[i];
        j = (j + tmp + key[i % strlen((char*)key)]) % size;  //注意此处的长度,自己计算一下密钥和数据的长度多次尝试
        //这里就不创建T盒了,直接对密钥进行遍历
        sbox[i] = sbox[j];
        sbox[j] = tmp;
        /*if (++k >= 12)
            k = 0;*/
    }
}

// 加解密函数
void enc_dec(unsigned char* key, unsigned char* data) {
    int i, j, k, R, tmp;

    init_sbox(key);

    j = k = 0;
    for (i = 0; i < strlen((char*)data); i++) {
        j = (j + 1) % size;
        k = (k + sbox[j]) % size;

        tmp = sbox[j];
        sbox[j] = sbox[k];
        sbox[k] = tmp;

        R = sbox[(sbox[j] + sbox[k]) % size];

        data[i] ^= R;
    }
}

int main() {
    unsigned char key[100] = { 0x52, 0x69, 0x63, 0x61, 0x72, 0x64, 0x6F, 0x5F, 0x4D, 0x5F, 0x6C };//, 0x75 };
    unsigned char data[100] = { 0xC2, 0xB0, 0x0E, 0xBE, 0xDB, 0xDF, 0x95, 0x2D, 0xCF, 0x4B, 0x74, 0x41, 0xF3, 0xC9, 0x43, 0xA7,
    0x3C, 0x70, 0xC2, 0x2F, 0xFF, 0x8D, 0x65, 0x2C, 0x5C };
    printf("key:%d,data:%d", strlen((char*)key), strlen((char*)data));  //查看一下key的长度是否异常,如果有异常请手动输入
    printf("\n");
    /*printf("输入你要加密的字符:");
    scanf("%100s", data);
    printf("输入密钥:");
    scanf("%40s", key);
    enc_dec(key, data);
    printf("enc: %s\n", data);*/
    enc_dec(key, data);
    printf("dec: %s\n", data);
    return 0;
}

TEA

https://blog.csdn.net/xiao__1bai/article/details/123307059

动画演示

位移与运算的关系

A >> n == A / pow(2,n)
A << n == A * pow(2,n)

加密流程:需要两个4字节的明文和四个4字节的密钥,以及一个delta值(通常为黄金分割数(5√-2)/2 与 232 的乘积0x9e3779b9即2654435769),把明文分组进行多轮与相加加密。且为分组加密,组与组间相互影响。

代码分析

(来自上面文章)

#include <stdio.h>
#include <stdint.h>
 
void encrypt (uint32_t *v,uint32_t *k ){
    uint32_t v0=v[0],v1=v[1],sum=0,i;
    uint32_t delta=0x9e3779b9;  //2654435769
    uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];
    for(i=0;i<32;i++){
        sum+=delta;
        v0+=((v1 << 4) + k0)^(v1 + sum)^((v1 >> 5) + k1);
        v1+=((v0 << 4) + k2)^(v0 + sum)^((v0 >> 5) + k3);
    } 
    v[0]=v0;v[1]=v1;
}
void decrypt (uint32_t *v,uint32_t *k){
    uint32_t v0=v[0],v1=v[1],sum=0xC6EF3720,i;	//这里的sum是0x9e3779b9*32后截取32位的结果,截取很重要。//这里的数据类型可能会有符号,可以尝试改为int
    uint32_t delta=0x9e3779b9;
    uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];
    for (i=0;i<32;i++){
        v1-=((v0 << 4) + k2)^(v0 + sum)^((v0 >> 5) + k3);
        v0-=((v1 << 4) + k0)^(v1 + sum)^((v1 >> 5) + k1);
        sum-=delta;
    } 
    v[0]=v0;v[1]=v1;
}
 
int main()
{
    uint32_t v[2]={1,2},k[4]={2,2,3,4};  //若有多组数据,可以每两个进行加密。
    printf("加密前的数据:%u %u\n",v[0],v[1]);	//%u 以十进制形式输出无符号整数 
    encrypt(v,k);
    printf("加密后数据:%u %u\n",v[0],v[1]);
    decrypt(v,k);
    printf("解密后数据:%u %u\n",v[0],v[1]);
    return 0;
}

分组加密的分类

密码学:分组密码.(块密码:是一种对称密码算法)_分组加密-CSDN博客

CEB:明文的每个块都独立地加密成密文的每个块。

CBC:个明文块先与前一个密文块进行异或(XOR)后再进行加密。第1个块中需要使用初始化向量(第一组密文与初始向量异或)。

OFB:将之前一次的加密结果使用密钥再次进行加密(第1次对IV进行加密),产生的块作为密钥流,然后将其与明文块进行异或,得到密文。

CFB:类似OFB,只不过将上一组的密文作为下一组的输入来加密进行反馈。


XTEA

解析 TEA 加密算法(C语言、python):_tea加密-CSDN博客

演示动画

XTEA是TEA的升级版,增加了更多的密钥表,移位和异或操作

代码

#include<stdio.h>
#include<stdint.h>
 
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]){
    unsigned int i;
    uint32_t v0=v[0],v1=v[1],sum=0,delta=0x9E3779B9;
    for(i=0;i<num_rounds;i++){
        v0+=(((v1 << 4)^(v1 >> 5)) + v1)^(sum + key[sum & 3]);
        sum+=delta;
        v1+=((( v0 << 4)^(v0 >> 5)) + v0)^(sum + key[(sum >> 11) & 3]);
    }
    v[0]=v0;v[1]=v1;
}
 
void decipher(unsigned int num_rounds,uint32_t v[2],uint32_t const key[4]){
    unsigned int i;
    uint32_t v0=v[0],v1=v[1],delta=0x9E3779B9,sum=delta*num_rounds;
    for(i=0;i<num_rounds;i++){
    v1-=(((v0 << 4)^(v0 >> 5)) + v0)^(sum + key[(sum >> 11) & 3]);
    sum-=delta;
    v0-=(((v1 << 4)^(v1 >> 5)) + v1)^(sum + key[sum & 3]);
    } 
    v[0]=v0;v[1]=v1;
}
 
int main(){
    uint32_t v[2]={1,2};
    uint32_t const k[4]={2,2,3,4};
    unsigned int r=32;				//这里是加密轮数,自己设置 
    printf("加密前原始数据:%u %u\n",v[0],v[1]);
    encipher(r,v,k);
    printf("加密后原始数据:%u %u\n",v[0],v[1]);
    decipher(r,v,k);
    printf("解密后原始数据:%u %u\n",v[0],v[1]);
    return 0;
}

XXTEA

解析 TEA 加密算法(C语言、python):_tea加密-CSDN博客

XXTEA在可变长度块上运行,这些块是32位大小的任意倍数(最小64位),使用128位密钥(16字节)。

加密流程:最开始是以v[n-1]和v[1]得出v[0],后面是用v[0]和v[2]得出v[1],以此类推三组作为一次加密,每次加密遍历一遍,一共加密6+52/n次。

代码分析

(来自上面的文章)

#include<stdio.h>
#include<stdint.h>
#define DELTA 0x933779b9
void btea(uint32_t *v,int n,uint32_t const key[4])
{
    uint32_t y,z,sum,delta;
    unsigned p,rounds,e;
    delta = 0x933779b9;
    rounds=6+52/n;	//这里可以说是预定义值,n=2是rounds=32
    sum=0;
    z=v[n-1];
    do
    {
        sum+=delta;
        e=(sum>>2)&3;
        for(p=0;p<n-1;p++)        //注意这里的p是从0~n-1
        {
            y=v[p+1];
            z=v[p]+=(((z >> 5 ^ y << 2)+(y >> 3 ^ z << 4))^((sum ^ y)+(key[(p & 3) ^ e] ^ z)));
        }
        y=v[0];
        z=v[n-1]+=(((z >> 5 ^ y << 2)+(y >> 3 ^ z << 4))^((sum ^ y)+(key[(p & 3) ^ e] ^ z)));        //这里的MX中传入的p=n-1
    }	
    while(--rounds);
}
void dec_btea(uint32_t *v,int n,uint32_t const key[4])
{
    uint32_t y,z,sum,delta;
    unsigned p,rounds,e;
    delta = 0x933779b9;
    rounds=6+52/n;
    sum=rounds*delta;
    y=v[0];
    do
    {
        e=(sum>>2)&3;
        for(p=n-1;p>0;p--)    //注意这里的p是从n-1~0,和上面是反过来的
        {
            z=v[p-1];
            y=v[p]-=(((z >> 5 ^ y << 2)+(y >> 3 ^ z << 4))^((sum ^ y)+(key[(p & 3) ^ e] ^ z)));
        }
        z=v[n-1];
        y=v[0]-=(((z >> 5 ^ y << 2)+(y >> 3 ^ z << 4))^((sum ^ y)+(key[(p & 3) ^ e] ^ z)));   //这里的MX中传入的 p=0
        sum-=delta;
    }
    while(--rounds);
    }
}
 
int main()
{
    uint32_t v[2]={1,2};
    uint32_t const k[4]={2,2,3,4};
    int n=2;
    printf("加密前原始数据:%u %u\n",v[0],v[1]);
    btea(v,n,k);
    printf("加密后数据:%u %u\n",v[0],v[1]);
    btea(v,-n,k);
    printf("解密后数据:%u %u\n",v[0],v[1]);
    return 0; 
    
}

TEA系列的特征

TEA系列最明显的特征就是DELTA=0x933779b9,有时值不一样,但是delta是一定有的。还有就是那一串异或相加加密的代码。

在TEA和XTEA中每次加密只会涉及到两个密文,而XXTEA有三个密文。在XTEA和XXTEA中有&3,而TEA中没有。在XXTEA中的轮数是6+52/n。

AES

AES加密算法原理详解-CSDN博客

演示动画

AES 是一种对称密钥加密方法,依赖于分组密码,将数据分成块,对每个块进行加密以确保一致的安全性。AES 加密提供三种密钥长度——128 位、192 位和 256 位。包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S盒。

AES加密比较复杂,我们不深入研究,只掌握一些特征用于分析,具体解密方式可以用赛博厨师和Python脚本

  • 加密过程:密钥扩展–>轮密钥生成–>字节替换、行移位–>列替换和轮密钥加

  • 密钥长度和轮数的对应

    • 128bits–>10rounds
    • 192bits–>12rounds
    • 256bits–>14rounds

    S盒常量

unsigned char S[256] = {
        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

代码实现(来自AES算法描述及C语言实现_c语言实现aes算法-CSDN博客)

#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct{
    uint32_t eK[44], dK[44];    // encKey, decKey
    int Nr; // 10 rounds
}AesKey;

#define BLOCKSIZE 16  //AES-128分组长度为16字节

// uint8_t y[4] -> uint32_t x
#define LOAD32H(x, y) \
  do { (x) = ((uint32_t)((y)[0] & 0xff)<<24) | ((uint32_t)((y)[1] & 0xff)<<16) | \
             ((uint32_t)((y)[2] & 0xff)<<8)  | ((uint32_t)((y)[3] & 0xff));} while(0)

// uint32_t x -> uint8_t y[4]
#define STORE32H(x, y) \
  do { (y)[0] = (uint8_t)(((x)>>24) & 0xff); (y)[1] = (uint8_t)(((x)>>16) & 0xff);   \
       (y)[2] = (uint8_t)(((x)>>8) & 0xff); (y)[3] = (uint8_t)((x) & 0xff); } while(0)

// 从uint32_t x中提取从低位开始的第n个字节
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)

/* used for keyExpansion */
// 字节替换然后循环左移1位
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
                ((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))

// uint32_t x循环左移n位
#define ROF32(x, n)  (((x) << (n)) | ((x) >> (32-(n))))
// uint32_t x循环右移n位
#define ROR32(x, n)  (((x) >> (n)) | ((x) << (32-(n))))

/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
// AES-128轮常量
static const uint32_t rcon[10] = {
        0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
        0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
// S盒
unsigned char S[256] = {
        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//逆S盒
unsigned char inv_S[256] = {
        0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
        0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
        0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
        0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
        0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
        0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
        0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
        0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
        0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
        0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
        0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
        0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
        0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
        0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
        0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
        0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

/* copy in[16] to state[4][4] */
int loadStateArray(uint8_t (*state)[4], const uint8_t *in) {
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            state[j][i] = *in++;
        }
    }
    return 0;
}

/* copy state[4][4] to out[16] */
int storeStateArray(uint8_t (*state)[4], uint8_t *out) {
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            *out++ = state[j][i];
        }
    }
    return 0;
}
//秘钥扩展
int keyExpansion(const uint8_t *key, uint32_t keyLen, AesKey *aesKey) {

    if (NULL == key || NULL == aesKey){
        printf("keyExpansion param is NULL\n");
        return -1;
    }

    if (keyLen != 16){
        printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
        return -1;
    }

    uint32_t *w = aesKey->eK;  //加密秘钥
    uint32_t *v = aesKey->dK;  //解密秘钥

    /* keyLen is 16 Bytes, generate uint32_t W[44]. */

    /* W[0-3] */
    for (int i = 0; i < 4; ++i) {
        LOAD32H(w[i], key + 4*i);
    }

    /* W[4-43] */
    for (int i = 0; i < 10; ++i) {
        w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
        w[5] = w[1] ^ w[4];
        w[6] = w[2] ^ w[5];
        w[7] = w[3] ^ w[6];
        w += 4;
    }

    w = aesKey->eK+44 - 4;
    //解密秘钥矩阵为加密秘钥矩阵的倒序,方便使用,把ek的11个矩阵倒序排列分配给dk作为解密秘钥
    //即dk[0-3]=ek[41-44], dk[4-7]=ek[37-40]... dk[41-44]=ek[0-3]
    for (int j = 0; j < 11; ++j) {

        for (int i = 0; i < 4; ++i) {
            v[i] = w[i];
        }
        w -= 4;
        v += 4;
    }

    return 0;
}

// 轮秘钥加
int addRoundKey(uint8_t (*state)[4], const uint32_t *key) {
    uint8_t k[4][4];

    /* i: row, j: col */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            k[i][j] = (uint8_t) BYTE(key[j], 3 - i);  /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */
            state[i][j] ^= k[i][j];
        }
    }

    return 0;
}

//字节替换
int subBytes(uint8_t (*state)[4]) {
    /* i: row, j: col */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            state[i][j] = S[state[i][j]]; //直接使用原始字节作为S盒数据下标
        }
    }

    return 0;
}

//逆字节替换
int invSubBytes(uint8_t (*state)[4]) {
    /* i: row, j: col */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            state[i][j] = inv_S[state[i][j]];
        }
    }
    return 0;
}

//行移位
int shiftRows(uint8_t (*state)[4]) {
    uint32_t block[4] = {0};

    /* i: row */
    for (int i = 0; i < 4; ++i) {
    //便于行循环移位,先把一行4字节拼成uint_32结构,移位后再转成独立的4个字节uint8_t
        LOAD32H(block[i], state[i]);
        block[i] = ROF32(block[i], 8*i);
        STORE32H(block[i], state[i]);
    }

    return 0;
}

//逆行移位
int invShiftRows(uint8_t (*state)[4]) {
    uint32_t block[4] = {0};

    /* i: row */
    for (int i = 0; i < 4; ++i) {
        LOAD32H(block[i], state[i]);
        block[i] = ROR32(block[i], 8*i);
        STORE32H(block[i], state[i]);
    }

    return 0;
}

/* Galois Field (256) Multiplication of two Bytes */
// 两字节的伽罗华域乘法运算
uint8_t GMul(uint8_t u, uint8_t v) {
    uint8_t p = 0;

    for (int i = 0; i < 8; ++i) {
        if (u & 0x01) {    //
            p ^= v;
        }

        int flag = (v & 0x80);
        v <<= 1;
        if (flag) {
            v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
        }

        u >>= 1;
    }

    return p;
}

// 列混合
int mixColumns(uint8_t (*state)[4]) {
    uint8_t tmp[4][4];
    uint8_t M[4][4] = {{0x02, 0x03, 0x01, 0x01},
                       {0x01, 0x02, 0x03, 0x01},
                       {0x01, 0x01, 0x02, 0x03},
                       {0x03, 0x01, 0x01, 0x02}};

    /* copy state[4][4] to tmp[4][4] */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j){
            tmp[i][j] = state[i][j];
        }
    }

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {  //伽罗华域加法和乘法
            state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
                        ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
        }
    }

    return 0;
}

// 逆列混合
int invMixColumns(uint8_t (*state)[4]) {
    uint8_t tmp[4][4];
    uint8_t M[4][4] = {{0x0E, 0x0B, 0x0D, 0x09},
                       {0x09, 0x0E, 0x0B, 0x0D},
                       {0x0D, 0x09, 0x0E, 0x0B},
                       {0x0B, 0x0D, 0x09, 0x0E}};  //使用列混合矩阵的逆矩阵

    /* copy state[4][4] to tmp[4][4] */
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j){
            tmp[i][j] = state[i][j];
        }
    }

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
                          ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
        }
    }

    return 0;
}

// AES-128加密接口,输入key应为16字节长度,输入长度应该是16字节整倍数,
// 这样输出长度与输入长度相同,函数调用外部为输出数据分配内存
int aesEncrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *pt, uint8_t *ct, uint32_t len) {

    AesKey aesKey;
    uint8_t *pos = ct;
    const uint32_t *rk = aesKey.eK;  //解密秘钥指针
    uint8_t out[BLOCKSIZE] = {0};
    uint8_t actualKey[16] = {0};
    uint8_t state[4][4] = {0};

    if (NULL == key || NULL == pt || NULL == ct){
        printf("param err.\n");
        return -1;
    }

    if (keyLen > 16){
        printf("keyLen must be 16.\n");
        return -1;
    }

    if (len % BLOCKSIZE){
        printf("inLen is invalid.\n");
        return -1;
    }

    memcpy(actualKey, key, keyLen);
    keyExpansion(actualKey, 16, &aesKey);  // 秘钥扩展

    // 使用ECB模式循环加密多个分组长度的数据
    for (int i = 0; i < len; i += BLOCKSIZE) {
        // 把16字节的明文转换为4x4状态矩阵来进行处理
        loadStateArray(state, pt);
        // 轮秘钥加
        addRoundKey(state, rk);

        for (int j = 1; j < 10; ++j) {
            rk += 4;
            subBytes(state);   // 字节替换
            shiftRows(state);  // 行移位
            mixColumns(state); // 列混合
            addRoundKey(state, rk); // 轮秘钥加
        }

        subBytes(state);    // 字节替换
        shiftRows(state);  // 行移位
        // 此处不进行列混合
        addRoundKey(state, rk+4); // 轮秘钥加
        
        // 把4x4状态矩阵转换为uint8_t一维数组输出保存
        storeStateArray(state, pos);

        pos += BLOCKSIZE;  // 加密数据内存指针移动到下一个分组
        pt += BLOCKSIZE;   // 明文数据指针移动到下一个分组
        rk = aesKey.eK;    // 恢复rk指针到秘钥初始位置
    }
    return 0;
}

// AES128解密, 参数要求同加密
int aesDecrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len) {
    AesKey aesKey;
    uint8_t *pos = pt;
    const uint32_t *rk = aesKey.dK;  //解密秘钥指针
    uint8_t out[BLOCKSIZE] = {0};
    uint8_t actualKey[16] = {0};
    uint8_t state[4][4] = {0};

    if (NULL == key || NULL == ct || NULL == pt){
        printf("param err.\n");
        return -1;
    }

    if (keyLen > 16){
        printf("keyLen must be 16.\n");
        return -1;
    }

    if (len % BLOCKSIZE){
        printf("inLen is invalid.\n");
        return -1;
    }

    memcpy(actualKey, key, keyLen);
    keyExpansion(actualKey, 16, &aesKey);  //秘钥扩展,同加密

    for (int i = 0; i < len; i += BLOCKSIZE) {
        // 把16字节的密文转换为4x4状态矩阵来进行处理
        loadStateArray(state, ct);
        // 轮秘钥加,同加密
        addRoundKey(state, rk);

        for (int j = 1; j < 10; ++j) {
            rk += 4;
            invShiftRows(state);    // 逆行移位
            invSubBytes(state);     // 逆字节替换,这两步顺序可以颠倒
            addRoundKey(state, rk); // 轮秘钥加,同加密
            invMixColumns(state);   // 逆列混合
        }

        invSubBytes(state);   // 逆字节替换
        invShiftRows(state);  // 逆行移位
        // 此处没有逆列混合
        addRoundKey(state, rk+4);  // 轮秘钥加,同加密

        storeStateArray(state, pos);  // 保存明文数据
        pos += BLOCKSIZE;  // 输出数据内存指针移位分组长度
        ct += BLOCKSIZE;   // 输入数据内存指针移位分组长度
        rk = aesKey.dK;    // 恢复rk指针到秘钥初始位置
    }
    return 0;
}
// 方便输出16进制数据
void printHex(uint8_t *ptr, int len, char *tag) {
    printf("%s\ndata[%d]: ", tag, len);
    for (int i = 0; i < len; ++i) {
        printf("%.2X ", *ptr++);
    }
    printf("\n");
}

int main() {

    // case 1
    const uint8_t key[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; //十六字节密钥
    const uint8_t pt[16]={0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34}; //
    uint8_t ct[16] = {0};     // 外部申请输出数据内存,用于加密后的数据
    uint8_t plain[16] = {0};  // 外部申请输出数据内存,用于解密后的数据

    aesEncrypt(key, 16, pt, ct, 16); // 加密
    printHex(pt, 16, "plain data:"); // 打印初始明文数据
    printf("expect cipher:\n39 25 84 1D 02 DC 09 FB DC 11 85 97 19 6A 0B 32\n");  // 正常解密后的数据内容

    printHex(ct, 16, "after encryption:");  // 打印加密后的密文

    aesDecrypt(key, 16, ct, plain, 16);       // 解密
    printHex(plain, 16, "after decryption:"); // 打印解密后的明文数据

    // case 2
    // 16字节字符串形式秘钥
    const uint8_t key2[]="1234567890123456";
    // 32字节长度字符串明文
    const uint8_t *data = (uint8_t*)"abcdefghijklmnopqrstuvwxyz123456";  
    uint8_t ct2[32] = {0};    //外部申请输出数据内存,用于存放加密后数据
    uint8_t plain2[32] = {0}; //外部申请输出数据内存,用于存放解密后数据
    //加密32字节明文
    aesEncrypt(key2, 16, data, ct2, 32); 

    printf("\nplain text:\n%s\n", data);
    printf("expect ciphertext:\nfcad715bd73b5cb0488f840f3bad7889\n");
    printHex(ct2, 32, "after encryption:");
    
    // 解密32字节密文
    aesDecrypt(key2, 16, ct2, plain2, 32);  
    // 打印16进制形式的解密后的明文
    printHex(plain2, 32, "after decryption:"); 

    // 因为加密前的数据为可见字符的字符串,打印解密后的明文字符,与加密前明文进行对比
    printf("output plain text\n");
    for (int i = 0; i < 32; ++i) {
        printf("%c ", plain2[i]);
    }

    return 0;
}

加解密脚本

import base64

from Crypto.Cipher import AES
password = b'1234567812345678' #秘钥,b就是表示为bytes类型
iv = b'1234567812345678' # iv偏移量,bytes类型
text = b'abcdefghijklmnhi' #需要加密的内容,bytes类型


aes = AES.new(password,AES.MODE_CBC,iv) #创建一个aes对象
# AES.MODE_CBC 表示模式是CBC模式
en_text = aes.encrypt(text)
print("密文:",en_text) #加密明文,bytes类型

m = b''   #要解密的base64代码
debase64 = base64.decodebytes(m)

aes = AES.new(password,AES.MODE_CBC,iv) #CBC模式下解密需要重新创建一个aes对象
den_text = aes.decrypt(debase64)
print("明文:",den_text)
import base64
from base64 import encode

from Crypto.Cipher import AES

password = b'1234567812345678' #秘钥,b就是表示为bytes类型

text = b'abcdefghijklmnhi' #需要加密的内容,bytes类型
aes = AES.new(password,AES.MODE_ECB) #创建一个aes对象
# AES.MODE_ECB 表示模式是ECB模式


m = b'Pd04a4bt7Bcf97KEfgLGQw=='   #要解密的base64值
en_text = base64.decodebytes(m)


# en_text = aes.encrypt(text) #加密明文
# print("密文:",en_text) #加密明文,bytes类型


den_text = aes.decrypt(en_text) # 解密密文
print("明文:",den_text)

# print(den_text.decode('gbk'))   #中文输出

解密AES需要逆s盒,逆s盒脚本为

new_s_box = [
    0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
]
import string
new_contrary_sbox = [0]*256
print(len(new_s_box))
for i in range(256):
    line = (new_s_box[i]&0xf0)>>4
    rol = new_s_box[i]&0xf
    new_contrary_sbox[(line*16)+rol] = i
print(len(new_contrary_sbox))
for i in new_contrary_sbox:
    print(f"{hex(i)},",end='')

SM4

SM4 的加密过程将数据块分成 128 位(16 字节)的分组进行加密,使用 32 轮加密操作,基于复杂的置换和替代(P-box 和 S-box)。由于其固定的 128 位分组长度,处理非 128 位整数倍的明文数据时,通常需要进行填充。主要包含异或、移位以及盒变换操作。它分为密钥拓展和加/解密两个模块,这两个模块的流程大同小异。其中,移位变换是指循环左移;盒变换是一个将8bit输入映射到8bit输出的变换,是一个固定的变换。

代码分析(来自Python实现SM4加解密算法_sm4 python-CSDN博客

# S盒,用于字节替换
S_BOX = [
    0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
    0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
    0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
    0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
    0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
    0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
    0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
    0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
    0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
    0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
    0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
    0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
    0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
    0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
    0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
    0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
]

# 轮常数,用于密钥扩展
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# 固定参数,用于轮密钥生成
CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]

def byte_sub(byte):
    """S盒字节替换"""
    return S_BOX[byte]

def l_transformation(b):
    """线性变换"""
    return b ^ (b << 2) ^ (b << 10) ^ (b << 18) ^ (b << 24)

def t_function(x):
    """T函数,包括S盒替换和线性变换"""
    b = byte_sub((x >> 24) & 0xFF) << 24 | \
        byte_sub((x >> 16) & 0xFF) << 16 | \
        byte_sub((x >> 8) & 0xFF) << 8 | \
        byte_sub(x & 0xFF)
    return l_transformation(b)

def key_expansion(key):
    """密钥扩展生成32轮密钥"""
    K = [key[i] ^ FK[i] for i in range(4)]
    rk = []
    for i in range(32):
        temp = K[i] ^ t_function(K[i + 1] ^ K[i + 2] ^ K[i + 3] ^ CK[i])
        rk.append(temp)
        K.append(temp)
    return rk

def sm4_encrypt_block(plaintext, key):
    """SM4单块加密"""
    rk = key_expansion(key)
    X = plaintext.copy()
    for i in range(32):
        temp = X[i] ^ t_function(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[i])
        X.append(temp)
    return X[35:31:-1]  # 反序输出

def sm4_decrypt_block(ciphertext, key):
    """SM4单块解密"""
    rk = key_expansion(key)[::-1]  # 轮密钥反序
    X = ciphertext.copy()
    for i in range(32):
        temp = X[i] ^ t_function(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[i])
        X.append(temp)
    return X[35:31:-1]  # 反序输出

# 示例使用
plaintext = [0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210]
key = [0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210]

ciphertext = sm4_encrypt_block(plaintext, key)
print(f"加密后的密文: {ciphertext}")

decrypted_text = sm4_decrypt_block(ciphertext, key)
print(f"解密后的明文: {decrypted_text}")

常用解密脚本

来自sm4 加解密 ( python版本) - 宁青楼 - 博客园

# -*- coding: utf-8 -*-
"""
author: 沈天巡
date:2023/4/27-10:33

说明:
"""

import binascii
from gmssl import sm4


class SM4:
    """
    国产加密 sm4加解密
    """

    def __init__(self):
        self.crypt_sm4 = sm4.CryptSM4()  # 实例化

    def str_to_hexStr(self , hex_str):
        """
        字符串转hex
        :param hex_str: 字符串
        :return: hex
        """
        hex_data = hex_str.encode('utf-8')
        str_bin = binascii.unhexlify(hex_data)
        return str_bin.decode('utf-8')

    def encryptSM4(self , encrypt_key , value):
        """
        国密sm4加密
        :param encrypt_key: sm4加密key
        :param value: 待加密的字符串
        :return: sm4加密后的十六进制值
        """
        crypt_sm4 = self.crypt_sm4
        crypt_sm4.set_key(encrypt_key.encode() , sm4.SM4_ENCRYPT)  # 设置密钥
        date_str = str(value)
        encrypt_value = crypt_sm4.crypt_ecb(date_str.encode())  # 开始加密。bytes类型
        return encrypt_value.hex()  # 返回十六进制值

    def decryptSM4(self , decrypt_key , encrypt_value):
        """
        国密sm4解密
        :param decrypt_key:sm4加密key
        :param encrypt_value: 待解密的十六进制值
        :return: 原字符串
        """
        crypt_sm4 = self.crypt_sm4
        crypt_sm4.set_key(decrypt_key.encode() , sm4.SM4_DECRYPT)  # 设置密钥
        decrypt_value = crypt_sm4.crypt_ecb(bytes.fromhex(encrypt_value))  # 开始解密。十六进制类型
        return decrypt_value.decode()
        # return self.str_to_hexStr(decrypt_value.hex())


if __name__ == '__main__':
    key = "love_is_the_soul"
    strData = "jntm"
    SM4 = SM4()
    print("原字符:" , strData)
    encData = SM4.encryptSM4(key , strData)  # 加密后的数据,返回bytes类型
    print("sm4加密结果:" , encData)

    # decData = SM4.decryptSM4(key , encData)
    # print("sm4解密结果:" , decData)  # 解密后的数据

blowfish

BlowFish是一个对称区块加密算法。每次加密数据为 64位 (2个int)类型数据大小。八个字节。密钥采用32-448位。进行多轮加密在加密或者初始化的过程中会使用两个盒来进行加密分别是PBOX 以及SBOX,这两个盒都是预计产生的,可以看成常数。

大概流程:对盒中PBOX的进行轮询与key进行加密。再用一遍加密函数加密0来改变PBOX和SBOX中的值。最后用明文去查找表数据进行运算替换。

常量特征

p_box = [
    0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
    0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
    0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
    0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
    0x9216D5D9, 0x8979FB1B]

s_box = [
    [0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
     0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
     0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
     0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
     0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
     0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
     0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
     0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
     0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
     0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
     0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
     0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
     0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
     0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
     0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
     0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
     0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
     0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
     0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
     0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
     0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
     0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
     0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
     0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
     0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
     0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
     0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
     0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
     0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
     0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
     0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
     0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
     0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
     0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
     0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
     0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
     0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
     0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
     0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
     0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
     0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
     0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
     0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
     0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
     0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
     0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
     0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
     0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
     0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
     0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
     0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
     0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
     0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
     0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
     0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
     0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
     0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
     0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
     0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
     0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
     0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
     0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
     0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
     0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A],
    [0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
     0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
     0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
     0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
     0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
     0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
     0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
     0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
     0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
     0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
     0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
     0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
     0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
     0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
     0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
     0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
     0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
     0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
     0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
     0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
     0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
     0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
     0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
     0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
     0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
     0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
     0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
     0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
     0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
     0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
     0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
     0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
     0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
     0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
     0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
     0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
     0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
     0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
     0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
     0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
     0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
     0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
     0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
     0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
     0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
     0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
     0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
     0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
     0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
     0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
     0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
     0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
     0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
     0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
     0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
     0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
     0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
     0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
     0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
     0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
     0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
     0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
     0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
     0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7],
    [0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
     0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
     0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
     0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
     0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
     0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
     0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
     0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
     0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
     0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
     0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
     0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
     0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
     0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
     0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
     0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
     0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
     0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
     0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
     0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
     0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
     0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
     0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
     0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
     0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
     0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
     0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
     0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
     0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
     0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
     0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
     0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
     0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
     0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
     0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
     0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
     0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
     0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
     0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
     0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
     0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
     0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
     0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
     0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
     0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
     0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
     0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
     0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
     0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
     0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
     0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
     0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
     0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
     0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
     0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
     0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
     0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
     0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
     0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
     0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
     0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
     0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
     0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
     0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0],
    [0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
     0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
     0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
     0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
     0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
     0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
     0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
     0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
     0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
     0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
     0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
     0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
     0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
     0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
     0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
     0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
     0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
     0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
     0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
     0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
     0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
     0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
     0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
     0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
     0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
     0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
     0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
     0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
     0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
     0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
     0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
     0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
     0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
     0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
     0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
     0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
     0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
     0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
     0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
     0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
     0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
     0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
     0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
     0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
     0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
     0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
     0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
     0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
     0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
     0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
     0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
     0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
     0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
     0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
     0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
     0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
     0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
     0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
     0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
     0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
     0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
     0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
     0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
     0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6]]

代码实现

来自BlowFish 加密算法 python实现 - yring

import Crypto.Util.number
import copy
from Blow_BOX import *

key_pbox = [0 for i in range(18)]
Encrypt = []


def init_key_pbox(_key):   #密钥初始化盒
    index = 0
    for i in range(18):
        for j in range(4):
            key_pbox[i] = ord(_key[index]) | (key_pbox[i] << 8)
            index += 1
            if index >= len(_key):
                index = 0

    for i in range(18):
        p_box[i] ^= key_pbox[i]


def Fn(Left):
    a = (Left & 0xff000000) >> 24
    b = (Left & 0x00ff0000) >> 16
    c = (Left & 0x0000ff00) >> 8
    d = Left & 0x000000ff

    Sa = s_box[0][a]
    Sb = s_box[1][b]
    Sc = s_box[2][c]
    Sd = s_box[3][d]

    return (((Sa + Sb) ^ Sc) + Sd) & 0xffffffff


def Blow_Main_Encrypt(Left, Right):  #主加密函数,查表异或替换
    for i in range(16):
        Left ^= p_box[i]
        Right ^= Fn(Left)
        Temp = Left
        Left = Right
        Right = Temp

    Temp = Left
    Left = Right ^ p_box[17]
    Right = Temp ^ p_box[16]
    return Left, Right


def Blow_Main_Decrypt(Left, Right):
    for i in range(17, 1, -1):
        Left ^= p_box[i]
        Right ^= Fn(Left)

        Temp = Left
        Left = Right
        Right = Temp

    Temp = Left
    Left = Right ^ p_box[0]
    Right = Temp ^ p_box[1]
    return Left , Right


def Change_Box(): #盒变化
    Left = 0
    Right = 0
    for i in range(0, 18, 2):
        Left, Right = Blow_Main_Encrypt(Left, Right)
        p_box[i] = Left
        p_box[i + 1] = Right

    for i in range(4):
        for j in range(0, 256, 2):
            Left, Right = Blow_Main_Encrypt(Left, Right)
            s_box[i][j] = Left
            s_box[i][j + 1] = Right


def BlowFish_Encrypt(data):
    while len(data) % 8:
        data += '0'
    cipher = ''
    for i in range(0, len(data), 8):
        Left = (ord(data[i]) << 24) | (ord(data[i + 1]) << 16) | (ord(data[i + 2]) << 8) | (ord(data[i + 3]))
        Right = (ord(data[i + 4]) << 24) | (ord(data[i + 5]) << 16) | (ord(data[i + 6]) << 8) | (ord(data[i + 7]))
        Left, Right = Blow_Main_Encrypt(Left, Right)
        cipher += hex(Left)[2:]
        cipher += hex(Right)[2:]
    global Encrypt
    Encrypt = []
    print(cipher)


def BlowFish_Decrypt(cipher):
    plain = ''
    for i in range(0, len(cipher), 16):
        Left = cipher[i:i + 8]
        Right = cipher[i + 8:i + 16]
        Left = int(Left, base=16)
        Right = int(Right, base=16)

        Left, Right = Blow_Main_Decrypt(Left, Right)
        plain += hex(Left)[2:]
        plain += hex(Right)[2:]

    plain = int(plain, base=16)
    print(Crypto.Util.number.long_to_bytes(plain))
    global Encrypt
    Encrypt = []


if __name__ == '__main__':
    key = input("PLZ input the key First\n")
    init_key_pbox(key)
    Change_Box()
    m = input("PLZ choose a model: 1.Encrypt 2.Decrypt 3.exit\n")
    while 1:
        if m == '1':
            data = input("PLZ input the data:\n")
            BlowFish_Encrypt(data)
            m = input("PLZ choose a model: 1.Encrypt 2.Decrypt 3.exit\n")
        if m == '2':
            cipher = input("PLZ input the cipher:\n")
            BlowFish_Decrypt(cipher)
            m = input("PLZ choose a model: 1.Encrypt 2.Decrypt 3.exit\n")

        if m == '3':
            break

DES

演示动画

分块对称加密算法,用64位明文被分为一组进行加密,使用64位密钥。若有多于64位的明文需要进行多组加密。

密钥拓展->二分明文->(半部分操作)明文ip置换->明文E拓展->轮密钥异或->加密分组s盒代换->p盒置换操作->继续右半部分相同操作->重复左右操作多轮加密->左右拼接ip逆置换

加解密实现代码

来自python实现DES算法-CSDN博客

import re

# ========================================
# 一、子密钥生成
# (1) 初始置换 64->56
# 64位的种子密钥经过PC_1置换后,生成56位的密钥
# (2) 划分 56->(28,28)
# 经过初始置换后的56位密钥被均分成C0和D0两部分
# (3) 循环左移
# 第一轮,C0和D0根据移位次数表各自进行循环左移
# 得到C1和D1
# 每一轮的C和D值是由上一轮的C和D值循环左移得到的
# (4) 合并 (28,28)->56->48
# 左移后的两部分再次合并,通过一个选择压缩表(PC_2)
# 得到这一轮的子密钥
# (5)重复3、4操作,最终得到16个子密钥
# ========================================

# 置换选择表1(PC_1) 64->56
PC_1 = [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
        ]

# 选择压缩表2(PC_2) 56->48
PC_2 = [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
        ]

# 移位次数表
shift_num = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]


def pc_1_change(bin_key):
    """初始置换

    64位的种子密钥经过PC_1置换后,生成56位的密钥
    """
    return [bin_key[i - 1] for i in PC_1]  # 列表形式


def shift_left(bin_key, num):
    """实现C和D的循环左移"""
    return bin_key[num:] + bin_key[:num]


def pc_2_change(bin_key):
    """选择压缩

    56位的密钥经过PC_2压缩,生成48位子密钥
    """
    return ''.join([bin_key[i - 1] for i in PC_2])  # 列表转字符串


def get_subkey_list(bin_key):
    """生成16轮的加解子密钥"""
    subkey_list = []  # 存储16轮子密钥
    # 1. 初始置换 64->58
    temp = pc_1_change(bin_key)
    # 2. 循环左移
    for i in shift_num:
        temp[:28] = shift_left(temp[:28], i)  # C部分循环左移
        temp[28:] = shift_left(temp[28:], i)  # D部分循环左移
        subkey_list.append(pc_2_change(temp))  # 生成子密钥
    return subkey_list


# ========================================
# 二、DES加解密实现
# ========================================

# 初始置换表IP 64->64
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
      ]

# 逆置换表_IP 64->64
_IP = [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
       ]

# 扩展置换表E 32->48
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
     ]

# S盒 48->32
S1 = [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
      ]
S2 = [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
      ]
S3 = [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
      ]
S4 = [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
      ]
S5 = [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
      ]
S6 = [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
      ]
S7 = [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
      ]
S8 = [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
      ]
S = [S1, S2, S3, S4, S5, S6, S7, S8]

# P盒
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
     ]


# encrypt
def ip_change(bin_text):
    """初始置换"""
    return [bin_text[i - 1] for i in IP]


def s_box(bin_result):
    """S盒替换"""
    int_result = []
    result = ''
    for i in range(8):
        # 二进制行号
        bin_row = bin_result[i][0] + bin_result[i][5]
        # 二进制列号
        bin_col = ''.join(bin_result[i][j] for j in range(1, 5))
        # 获取对应的十进制数
        int_result.append(S[i][16 * int(bin_row, base=2) + int(bin_col, base=2)])
        # 十进制转成二进制
        result += bin(int_result[-1])[2:].zfill(4)
    return result


def p_box(result):
    """P盒置换"""
    return ''.join(result[i - 1] for i in P)


def f(R, bin_key):
    """轮函数f()"""
    # 1.将R由32位扩展成48位
    R_ext = [R[i - 1] for i in E]
    # 2.与子密钥进行逐位异或
    bin_temp = [str(int(r) ^ int(k)) for r, k in zip(R_ext, bin_key)]
    # 6个字符为一组,共8组
    bin_result = [''.join(bin_temp[i:i + 6]) for i in range(0, len(bin_temp), 6)]
    # 3.S盒替换 48->32
    result = s_box(bin_result)
    # 4.P盒置换 32->32
    return p_box(result)


def _ip_change(bin_text):
    """进行IP-1逆置换"""
    return ''.join(bin_text[i - 1] for i in _IP)


def des_cipher(bin_text, bin_key, reverse_keys=False):
    """通用DES加密解密函数"""
    # 1. 初始置换IP
    bin_text = ip_change(bin_text)
    # 2. 分成左右两部分L、R
    L, R = bin_text[:32], bin_text[32:]
    # 3. 获得16轮子密钥
    subkey_list = get_subkey_list(bin_key)
    if reverse_keys:
        subkey_list = subkey_list[::-1]  # 解密时反转子密钥列表
    # 4. 进行16轮迭代
    for i in subkey_list:
        R_temp = R
        # 轮函数f()结果和L进行异或
        R = ''.join(str(int(r) ^ int(l)) for r, l in zip(f(R, i), L))
        L = R_temp
    # 5. 进行IP-1逆置换 64->64
    return _ip_change(R + L)  # 输出二进制字符串


# 使用示例
def str2bin(text):
    """字符串转二进制字符串"""
    return ''.join(bin(byte)[2:].zfill(8) for byte in text.encode())


def bin2str(bin_text):
    """二进制字符串转字符串"""
    # 1.将二进制字符串按8位分割,并转换为字节数组
    byte_array = bytearray(int(i, 2) for i in re.findall(r'.{8}', bin_text) if int(i, 2) != 0)
    # 2.将字节序列解码为字符串
    return byte_array.decode()


def is_valid_key(key):
    """检查密钥是否有效 64bit"""
    return len(key.encode()) == 8


def des_encrypt(plaintext, key):
    """DES加密"""
    # 1.明文转成二进制字符串, 0填充至64的倍数
    bin_plaintext = str2bin(plaintext)
    padding_len = (64 - (len(bin_plaintext) % 64)) % 64
    bin_padding_plaintext = bin_plaintext + '0' * padding_len
    # 2.进行64位分组加密
    bin_group_64 = re.findall(r'.{64}', bin_padding_plaintext)
    bin_ciphertext = ''
    for g in bin_group_64:
        bin_ciphertext += des_cipher(g, str2bin(key))
    # 3.密文转为16进制输出
    bin_group_4 = re.findall(r'.{4}', bin_ciphertext)
    hex_ciphertext = ''
    for g in bin_group_4:
        hex_ciphertext += format(int(g, 2), 'x')
    return hex_ciphertext


def des_decrypt(hex_ciphertext, key):
    """DES解密"""
    # 1.16进制密文转为2进制字符串
    bin_ciphertext = ''.join(bin(int(h, 16))[2:].zfill(4) for h in hex_ciphertext)
    # 2.进行64位分组解密
    bin_group_64 = re.findall(r'.{64}', bin_ciphertext)
    bin_deciphertext = ''
    for g in bin_group_64:
        bin_deciphertext += des_cipher(g, str2bin(key), reverse_keys=True)
    # 3.将解密密文转为字符串输出
    return bin2str(bin_deciphertext)


def des_run():
    """DES启动界面"""
    flag = True
    while flag:
        print('=' * 3, "DES加密解密", '=' * 3)
        print('[1]加密')
        print('[2]解密')
        print('[0]退出')
        choice = input('请输入你的选择:')
        match choice:
            case '0':
                flag = False
            case '1':
                plaintext = input('请输入明文:')
                key = input('请输入密钥(64bit):')
                if not is_valid_key(key):
                    print('密钥长度错误')
                    continue
                ciphertext = des_encrypt(plaintext, key)
                print(f'密文:{ciphertext}')
            case '2':
                ciphertext = input('请输入密文:')
                key = input('请输入密钥(64bit):')
                if not is_valid_key(key):
                    print('密钥长度错误')
                    continue
                print(f'解密:{des_decrypt(ciphertext, key)}')
            case _:
                print('输入错误')
    print('=' * 15)


if __name__ == '__main__':
    des_run()

CRC64

CRC校验原理及实现 - 知乎

循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。

本质就是一种多项式除法,把原二进制与一个另一个二进制以多项式的形式相除,最后把余数添加到数据末尾用于检验。

我们可以用赛博厨师计算crc的值。

解密脚本(特征)

import struct


def shld(a1, a2):
    return ((a1 << 1) | (a2 >> 31)) & 0xffffffff


def shl(a1):
    return (a1 << 1) & 0xffffffff


def shr(a1):
    return (a1 >> 1) & 0xffffffff


def de(buf):
    for i in range(0, 10, 2):
        ta = buf[i]
        tb = buf[i + 1]
        for k in range(64):

            if (ta & 1):
                ta = (0x54AA4A9 ^ ta) & 0xffffffff
                ta = shr(ta) | ((tb & 1) << 31)

                tb = shr(tb) | (1 << 31)
            else:
                ta = shr(ta) | ((tb & 1) << 31)

                tb = shr(tb)
        buf[i] = ta
        buf[i + 1] = tb


def main():
    flag = [0x149b24c1, 0xbc17996, 0x192ce051, 0x1666bae6, 0xf0f13109, 0x713168a1, 0xcc4fa796, 0x9d5344d9, 0x623329f6, 0x5e22b5da]
    de(flag)
    for i in range(len(flag)):
        print(hex(flag[i]), end=', ')
    print()
    flag = b''.join(struct.pack("<I", i) for i in flag)
    print(flag)


if __name__ == '__main__':
    main()

Hash

  • 数据摘要算法,可以理解为有损压缩
  • 因此,hash不可逆,所以如果解题遇到hash基本以算法识别+撞库为主
  • md5、sha1、sha256、sha384、sha512等等
  • 加密过程有大量移位操作且不可逆

MD5特征常量

演示动画

67452301h
0EFCDAB89h
98BADCFEh 
10325476h

小练习

第一个加密函数是一个魔改xtea,特征是delta和加密代码。

image-20250104205509436

第二个是一个rc4,特征明显,没有魔改,密钥在函数外面的上一层

image-20250104205717563

下一个是base64,特征是一个表和上面的除3分组。

image-20250104205838323

在函数的外围我们可以拿到base64的密文,先进行base64解码,再解rc4.

image-20250104210042553

现在我们来解这个魔改的xtea。这个xtea加密把7个四字节的块按两个一组的方式轮流加密到最后一个数字。分析发现delta的初始值和加密数据的索引有关,且当v4为33时才会结束,也就是说每一轮加密了33次,这样我们就得知了初始的delta值,且delta相加的位置也换到了最后。在第一个行密代码中多异或了一个v3,也就是delta。大概逻辑就是这样。解密时我们先把数据转换一下端序,并分成一个数组的形式以便我们使用(脚本在下方)。因为key太过于明显,且第一次解密错误,我们动调看一下,发现key被修改,我们用修改的key就可以解密成功,最后我们在转一下端序输出就可以得到结果。

#include <stdio.h>
#include <stdlib.h>
#include<stdint.h>

void decipher(unsigned int num_rounds, uint32_t *v, uint32_t const key[4], uint32_t j) {
    unsigned int i;
    uint32_t v0 = v[0], v1 = v[1], delta = 0xA852C66, sum = delta * num_rounds + (j) * delta;

    for (i = 0; i < num_rounds; i++) {
        sum -= delta;
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        v0 -= sum ^ (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0] = v0; v[1] = v1;
}

int main() {
    uint32_t key[4] = {
    0x00000002, 0x00000003, 0x00000004, 0x00000001
    };
    uint32_t m[] = { 0xc54cd766, 0x862d6760, 0x5e051928, 0xa0fbb057, 0x1e656482, 0xe98f9651, 0xde837c45 };
    uint32_t m2[] = { 0x66d74cc5,0x60672d86,0x2819055e,0x57b0fba0,0x8264651e,0x51968fe9,0x457c83de };

    for (int i = 5; i >= 0; i--) {
        decipher(33, &m2[i], key, i);
    }
    for (int i = 0; i < 7; i++) {
        for (int m = 3; m >=0; m--) {
            printf("%c", (m2[i] >> (8 *m)) & 0xff);
        }
    }

}