RC4加密與解密


介紹

在密碼學RC4(Rivest Cipher 4,也稱為ARC4ARCFOUR,意為所謂的RC4)是一種流密碼盡管它以簡單性和軟件速度着稱,但在RC4中發現了多個漏洞,使其不安全。不丟棄輸出密鑰流的開頭或使用非隨機或相關密鑰時,它特別容易受到攻擊RC4的使用特別有問題,導致協議非常不安全,例如WEP。

 

參數介紹

參數名 說明
S S-box,長度為256的char型數組,char S[256]
K 密鑰Key,用戶自定義,長度在1~256,用來打亂S-box
T 臨時變量,長度為256的char型數組
D 保存加密前/后數據

基本流程

  • 初始化 S 和 T 數組。
  • 初始化置換 S。
  • 生成密鑰流。

初始化S和T數組+初始化置換S

void RC4_Init(unsigned char* S, unsigned char* K, unsigned int len) {
    int i, j = 0;
    unsigned char ch_tmp, T[256] = { 0 };//臨時變量
    for (i = 0; i < 256; ++i) {
        S[i] = i;//初始化S-box
        T[i] = K[i % len];//密鑰填充臨時數組
    }
    //打亂S-box
    for (i = 0; i < 256; ++i) {
        j = (j + S[i] + T[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
    }
}

 

生成密鑰流

void RC4_Crypt(unsigned char* S, unsigned char* D, unsigned int len) {
    int i = 0, j = 0, int_tmp;
    unsigned int n;
    unsigned char ch_tmp;
    for (n = 0; n < len; ++n) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
        int_tmp = (S[i] + S[j]) % 256;
        D[n] ^= S[n];
    }
}

 

完整代碼實現

C語言

#include <bits/stdc++.h>

#pragma warning(disable:4996)

void RC4_Init(unsigned char* S, unsigned char* K, unsigned int len) {
    int i, j = 0;
    unsigned char ch_tmp, T[256] = { 0 };//臨時變量
    for (i = 0; i < 256; ++i) {
        S[i] = i;//初始化S-box
        T[i] = K[len % 256];//密鑰填充臨時數組
    }
    //打亂S-box
    for (i = 0; i < 256; ++i) {
        j = (j + S[i] + T[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
    }
}

void RC4_Crypt(unsigned char* S, unsigned char* D, unsigned int len) {
    int i = 0, j = 0, int_tmp;
    unsigned int n;
    unsigned char ch_tmp;
    for (n = 0; n < len; ++n) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
        int_tmp = (S[i] + S[j]) % 256;
        D[n] ^= S[n];
    }
}

int main(void)
{
    unsigned char S_box[256] = { 0 };
    unsigned char Key[] = { "helloworld" };
    unsigned char Data[] = { "youareso" };
    int i;

    printf("加密前數據:%s\n\n", Data);
    printf("密鑰:%s\n\n", Key);

    RC4_Init(S_box, Key, strlen((char*)Key));
    printf("S-box:\n");
    for (i = 0; i < 256; ++i) {
        printf("%02x", S_box[i]);
        if ((i + 1) % 16 == 0) printf("\n");
    }

    RC4_Crypt(S_box, Data, strlen((char*)Data));

    printf("\n加密后數據:%s\n", Data);

    system("PAUSE");
    return 0;
}

 

RC4+Base64

不過在實際做題中,往往會將RC4與變表Base64結合起來考(先后順序)

#include <bits/stdc++.h>

#pragma warning(disable:4996)

void RC4_Init(unsigned char* S, unsigned char* K, unsigned int len) {
    int i, j = 0;
    unsigned char ch_tmp, T[256] = { 0 };//臨時變量
    for (i = 0; i < 256; ++i) {
        S[i] = i;//初始化S-box
        T[i] = K[len % 256];//密鑰填充臨時數組
    }
    //打亂S-box
    for (i = 0; i < 256; ++i) {
        j = (j + S[i] + T[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
    }
}

void RC4_Crypt(unsigned char* S, unsigned char* D, unsigned int len) {
    int i = 0, j = 0, int_tmp;
    unsigned int n;
    unsigned char ch_tmp;
    for (n = 0; n < len; ++n) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        ch_tmp = S[i];
        S[i] = S[j];
        S[j] = ch_tmp;
        int_tmp = (S[i] + S[j]) % 256;
        D[n] ^= S[n];
    }
}

unsigned char* base64_encode(unsigned char* str)
{
    long len;
    long str_len;
    unsigned char* res = { 0 };
    int i, j;
    unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    //unsigned char base64_table[] = "0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    str_len = strlen((char*)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';

    for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
    {
        res[i] = base64_table[str[j] >> 2];
        res[i + 1] = base64_table[(str[j] & 0x3) << 4 | (str[j + 1] >> 4)];
        res[i + 2] = base64_table[(str[j + 1] & 0xf) << 2 | (str[j + 2] >> 6)];
        res[i + 3] = base64_table[str[j + 2] & 0x3f];
    }

    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((char*)code);
    //判斷編碼后的字符串后是否有=  
    if (strstr((char*)code, "=="))
        str_len = len / 4 * 3 - 2;
    else if (strstr((char*)code, "="))
        str_len = len / 4 * 3 - 1;
    else
        str_len = len / 4 * 3;

    res = (unsigned char*)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;

}

int main(void)
{
    unsigned char S_box[256] = { 0 }, S_box2[256] = { 0 };
    unsigned char Key[] = { "helloworld" };
    unsigned char Data[] = { "youareso" };
    char tmp[256] = { 0 };
    int i;

    printf("加密前數據:%s\n\n",Data);
    printf("密鑰:%s\n\n", Key);

    RC4_Init(S_box, Key, strlen((char*)Key));
    printf("S-box:\n");
    for (i = 0; i < 256; ++i) {
        printf("%02x", S_box[i]);
        S_box2[i] = S_box[i];
        if ((i + 1) % 16 == 0) printf("\n");
    }

    RC4_Crypt(S_box, Data, strlen((char*)Data));

    strcpy(tmp, (const char*)base64_encode(Data));
    printf("\n加密后數據:%s\n", tmp);

    strcpy((char*)Data, (char*)base64_decode((unsigned char*)tmp));
    RC4_Crypt(S_box2, Data, strlen((char*)Data));
    printf("\n解密后數據:%s\n", Data);

    system("PAUSE");
    return 0;
}

 

Python代碼

# -*- coding: utf-8 -*-
import random, base64
from hashlib import sha1


def crypt(data, key):
    """RC4 algorithm"""
    x = 0
    box = range(256)
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        box[i], box[x] = box[x], box[i]
    x = y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))

    return ''.join(out)


def tencode(data, key, encode=base64.b64encode, salt_length=16):
    """RC4 encryption with random salt and final encoding"""
    salt = ''
    for n in range(salt_length):
        salt += chr(random.randrange(256))
    data = salt + crypt(data, sha1(key + salt).digest())
    if encode:
        data = encode(data)
    return data


def tdecode(data, key, decode=base64.b64decode, salt_length=16):
    """RC4 decryption of encoded data"""
    if decode:
        data = decode(data)
    salt = data[:salt_length]
    return crypt(data[salt_length:], sha1(key + salt).digest())


if __name__ == '__main__':
    data = 'JcckEQrhrmVawW9p+KRI7pDZ81VbpkVU30RQ7pskkaZh+tWh'
    key = 'Biub1uBIv'
    decoded_data = tdecode(data=data, key=key)
    print("明文是:")
    print decoded_data

 

在線解密

http://ctf.ssleye.com/rc4.html

http://www.ssleye.com/rc4_cipher.html

http://tool.chacuo.net/cryptrc4

 

參考

https://en.wikipedia.org/wiki/RC4

https://blog.csdn.net/CharlesGodX/article/details/90065683

https://www.ctfwp.com/articals/2019xihulunjian.html?h=rc4

https://blog.csdn.net/qq_26093511/article/details/78836087

https://ctf-wiki.github.io/ctf-wiki/crypto/streamcipher/special/rc4-zh/

https://github.com/BjdsecCA/BJDCTF2020

 https://blog.csdn.net/huangyimo/article/details/82970903

 

拓展

流密碼一般逐字節或者逐比特處理信息。一般來說

  • 流密碼的密鑰長度會與明文的長度相同。
  • 流密碼的密鑰派生自一個較短的密鑰,派生算法通常為一個偽隨機數生成算法。

需要注意的是,流加密目前來說都是對稱加密。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM