什么是RC4 ?
RC4加密算法是大名鼎鼎的RSA三人組中的頭號人物Ron Rivest在1987年設計的密鑰長度可變的流加密算法簇,之所以稱其為簇,是由於其核心部分的S-box長度可為任意,但一般為256字節。
在密碼學中,RC4(來自Rivest Cipher 4的縮寫)是一種流加密算法,密鑰長度可變。它加解密使用相同的密鑰,因此也屬於對稱加密算法。所謂對稱加密,就是加密和解密的過程是一樣的。RC4是有線等效加密(WEP)中采用的加密算法,也曾經是TLS可采用的算法之一。
RC4已經成為一些常用的協議和標准的一部分,如1997年的WEP和2003/2004年無線卡的WPA; 和1995年的SSL,以及后來1999年的TLS。讓它如此廣泛分布和使用的主要因素是它不可思議的簡單和速度,不管是軟件還是硬件,實現起來都十分容易。
基本原理
對明文使用同一個密鑰異或兩次最后得到的是原文
- 加密:原文和Keystream進行異或得到密文
- 解密:密文和Keystream進行異或得到原文
圖片來源:《802.11無線網絡權威指南》 第 5 章 圖5-1 串流密碼鎖的一般運作程序
流程圖解
生成秘鑰流(KeyStream)
從上圖可以看出來,RC4加密原理很簡單,只需要一個KeyStream與明文進行異或即可,密鑰流的長度和明文的長度是對應的。RC4算法的的主要代碼還是在於如何生成秘鑰流。
密鑰流的生成由兩部分組成:
- KSA(the Key-Scheduling Algorithm)
- PRGA(the Pseudo-Random Generation Algorithm)
利用Key生成S盒——The key-scheduling algorithm (KSA)
/* 得到S-box */ int i = 0; for (i = 0; i < 256; i++) { S[i] = i; T[i] = puc_key[i % key_length]; } for (i = 0; i < 256; i++) { j = (j + S[i] + T[i]) % 256; swap_uchar(&S[i], &S[j]); //交換S[i]和S[j] }
利用S盒生成密鑰流——The pseudo-random generation algorithm(PRGA)
/* 生成密鑰流 Keystream */ int i = 0; int j = 0; int t = 0; unsigned long k = 0; for (k = 0; k < ul_data_length; k++) { i = (i + 1) % 256; j = (j + puc_sbox[i]) % 256; swap_uchar(&puc_sbox[i], &puc_sbox[j]); t = (puc_sbox[i] + puc_sbox[j]) % 256; puc_key_stream[k] = puc_sbox[t]; }
代碼實現
#include<stdio.h> #include<string.h> #define SBOX_LEN 256 #define rc4_encrypt rc4_crypt #define rc4_decrypt rc4_crypt static inline void swap_uchar(unsigned char *puc_x, unsigned char *puc_y) { *puc_x = *puc_x ^ *puc_y; *puc_y = *puc_x ^ *puc_y; *puc_x = *puc_x ^ *puc_y; } void hexdump(unsigned char *puc_data, int length) { int i = 0; for (i = 0; i < length; i++) { printf("%02X", puc_data[i]); if (i && (i + 1) % 16 == 0) { putchar('\n'); } } printf("\n"); } /** * 利用Key生成S盒 * the Key-Scheduling Algorithm */ static void rc4_ksa(unsigned char *puc_sbox, unsigned char *puc_key, int key_length) { int i = 0; int j = 0; char tmp[SBOX_LEN] = {0}; for (i = 0; i < SBOX_LEN; i++) { puc_sbox[i] = i; tmp[i] = puc_key[i % key_length]; } for (i = 0; i < SBOX_LEN; i++) { j = (j + puc_sbox[i] + tmp[i]) % SBOX_LEN; swap_uchar(&puc_sbox[i], &puc_sbox[j]); //交換puc_sbox[i]和puc_sbox[j] } } /** * 利用S盒生成密鑰流 * The pseudo-random generation algorithm(PRGA) */ static void rc4_prga(unsigned char *puc_sbox, unsigned char *puc_key_stream, unsigned long ul_data_length) { int i = 0; int j = 0; int t = 0; unsigned long k = 0; for (k = 0; k < ul_data_length; k++) { i = (i + 1) % SBOX_LEN; j = (j + puc_sbox[i]) % SBOX_LEN; swap_uchar(&puc_sbox[i], &puc_sbox[j]); t = (puc_sbox[i] + puc_sbox[j]) % SBOX_LEN; /* 為了更清晰理解rc4算法流程,此處保存keystream,不直接進行XOR運算 */ puc_key_stream[k] = puc_sbox[t]; } } /*加解密*/ void rc4_crypt(unsigned char *puc_data, unsigned char *puc_key_stream, unsigned long ul_data_length) { unsigned long i = 0; /* 把PRGA算法放在加解密函數中可以不需要保存keystream */ for (i = 0; i < ul_data_length; i++) { puc_data[i] ^= puc_key_stream[i]; } } int main(int argc, char *argv[]) { unsigned char sbox[SBOX_LEN] = {0}; char key[SBOX_LEN] = {"abcdefghijklmnopqrstuvwxyz"}; //秘鑰內容隨便定義 char data[512] = "lsRJ@.0 lvfvr#9527"; unsigned char puc_keystream[512] = {0}; unsigned long ul_data_length = strlen(data); printf("key=%s, length=%d\n\n", key, strlen(key)); printf("Raw data string:%s\n", data); printf("Raw data hex:\n"); hexdump(data, ul_data_length); /* 生成S-box */ rc4_ksa(sbox, (unsigned char *)key, strlen(key)); /* 生成keystream並保存,S-box也會被更改 */ rc4_prga(sbox, puc_keystream, ul_data_length); printf("S-box final status:\n"); hexdump(sbox, sizeof(sbox)); printf("key stream:\n"); hexdump(puc_keystream, ul_data_length); /* 加密 */ rc4_encrypt((unsigned char*)data, puc_keystream, ul_data_length); printf("cipher hexdump:\n"); hexdump(data, ul_data_length); /* 解密 */ rc4_decrypt((unsigned char*)data, puc_keystream, ul_data_length); printf("decypt data:%s\n", data); return 0; }
運行示例:
參考
《802.11無線網絡權威指南》 第5章