RC4算法实现
参考链接:https://zh.wikipedia.org/wiki/RC4
介绍
RC4 算法是一种对称流密码算法,由美国计算机科学家罗纳德・李维斯特(Ronald Rivest)发明于 1987 年。它的实现相对简单,运行速度快,适用于资源有限的设备和环境,对实时性要求比较高的应用场景。其密钥长度可变,可以在 1 到 256 字节之间,这使得用户可以根据不同的安全需求选择合适的密钥长度,增加了其灵活性。
在加密过程中,RC4 通过将一个密钥和一个初始化向量经过KSA算法和 PRGA 算法生成一个长度为 256 个字节的密钥流,然后将明文与密钥流逐字节异或,得到密文。解密时,将密文与密钥流再次异或即可还原出明文。这种加密和解密方式简单高效,并且可以根据不同的明文长度动态生成密钥流,适应各种数据长度的加密需求。
例如,在无线通信领域中,RC4 算法被用于保护 Wi-Fi 网络的安全。它可以快速地对无线传输的数据进行加密,确保数据的安全性。在电子商务中,RC4 算法可以用于加密信用卡等敏感信息,由于其高效性,可以在不影响用户体验的情况下快速完成加密和解密过程。
此外,RC4 算法不需要进行填充操作,可以直接对数据进行加密,这也增加了它的可塑性。与一些需要进行填充的加密算法相比,RC4 更加简洁灵活,能够适应不同类型的数据格式。
由于其简单和灵活性,RC4曾经在一些流行的加密协议和标准中被使用:如WEP协议、 SSL 协议、 TLS 协议等。
但RC4已被证实存在多种漏洞。尤其是在密钥流的开头未被丢弃,或使用相关密钥时,非常容易被攻击者破解。因此2015年 RFC 7465 规定禁止在 TLS 中使用 rc4 。
算法描述
作为一种流加密算法, RC4 将会生成一个伪随机比特流(密钥流) , 用来与明文异或加密 , 由于异或的可逆性,解密操作只需将密文与密钥流异或即可得到明文。
CT = PT bitxor keystream
PT = CT bitxor keystream
为了生成密钥流,首先用 KSA 算法初始化排列 S,其中秘钥的字节会被加入 S , 然后利用PRGA 算法生成比特流。
KSA :Key-sheduling algorithm ,用于初始化数组 S 的排列
变量含义:
- S:包含256个可能字节的排列数组
- keylength :密钥的字节数 , 1 <= keylength <= 256, 通常在 5 到 16 之间
首先,S 被初始化为恒等置换。然后,以与主PRGA类似的方式对S进行256次迭代处理,但同时也混合了密钥的字节。
for i from 0 to 255
S[i] := i
end for
j := 0
for i from 0 to 255
j := (j + S[i] + key[i mod keylength]) mod 256
swap values of S[i] and S[j]
end for
PRGA :Pseudo-random generation algorithm
j := 0
i := 0
while GeneratingOutput:
i := (i + 1) mod 256
j := (j + S[i]) mod 256
swap values of S[i] and S[j]
t := (S[i] + S[j]) mod 256
K := S[t]
output K
endwhile
c++ 实现
#include <vector>
#include <iostream>
#include <string>
using namespace std;
const int S_length = 256;
class RC4
{
private:
vector<unsigned char> S;
vector<unsigned char> K;
vector<unsigned char> text;
vector<unsigned char> key_stream;
void swap(unsigned char &a1, unsigned char &a2)
{
unsigned char tmp = a1;
a1 = a2;
a2 = tmp;
}
void rc4_KSA()
{
int j = 0;
for (int i = 0; i < S_length; i++)
{
S[i] = i;
}
for (int i = 0; i < S_length; i++)
{
j = (j + S[i] + K[i % K.size()]) % S_length;
swap(S[i], S[j]);
}
}
void rc4_PRGA()
{
int i = 0, j = 0;
for (int n = 0; n < S_length; n++)
{
i = (i + 1) % S_length;
j = (j + S[i]) % S_length;
swap(S[i], S[j]);
int tmp = (S[i] + S[j]) % S_length;
key_stream[n] = static_cast<int>(S[tmp]);
}
}
public:
vector<unsigned char> get_ciphertext(const string &key, const string &plaintext)
{
S.resize(S_length);
key_stream.resize(S_length);
for (unsigned char ch : key)
{
K.push_back(ch);
}
for (unsigned char ch : plaintext)
{
text.push_back(ch);
}
RC4::rc4_KSA(); // Key-scheduling algorithm
RC4::rc4_PRGA(); // Pseudo-random generation algorithm
// xor plaintext and keystream to get ciphertext
for (int i = 0; i < text.size(); i++)
{
text[i] = text[i] ^ key_stream[i];
}
return text;
}
vector<unsigned char> get_plaintext(const string &key, vector<unsigned char> ciphertext)
{
S.resize(S_length);
key_stream.resize(S_length);
for (unsigned char ch : key)
{
K.push_back(ch);
}
RC4::rc4_KSA(); // Key-scheduling algorithm
RC4::rc4_PRGA(); // Pseudo-random generation algorithm
// xor ciphertext and keystream to get plaintext
for (int i = 0; i < text.size(); i++)
{
ciphertext[i] = ciphertext[i] ^ key_stream[i];
}
return ciphertext;
}
};
int main()
{
string key, plaintext;
cout << "Please input your key:";
getline(cin, key);
cout << "Please input your plaintext:";
getline(cin, plaintext);
RC4 rc4_encrypt;
vector<unsigned char> ciphertext = rc4_encrypt.get_ciphertext(key, plaintext);
cout << "This is your ciphertext :" << endl;
for (int i = 0; i < ciphertext.size(); i++)
{
printf("%02X", ciphertext[i]);
}
cout << endl;
RC4 rc4_decrypt;
vector<unsigned char> decrypt_text = rc4_encrypt.get_plaintext(key, ciphertext);
cout << "This is your decrypted plaintext :" << endl;
for (int i = 0; i < decrypt_text.size(); i++)
{
cout << decrypt_text[i];
}
}
安全性
与现代流密码(如eSTREAM中的流密码)不同,RC4的密钥中没有附加随机数。这意味着如果要长期使用单个密钥来安全地加密多个流,必须指定如何组合随机数和长期密钥 。一种解决方案是用随机数对长期密钥进行散列加密来生成新的RC4密钥。然而,许多RC4的应用中只是将key和nonce连接起来,这将会导致相关密钥攻击,如Fluhrer、Mantin和Shamir攻击(破坏了WEP)。
由于RC4是一种流密码,所以它比普通分组密码更具可塑性。如果不与强消息认证码(MAC)一起使用,则容易受到 比特翻转攻击和 流密码攻击。
技术细节浅析
攻击者可以通过中间人手段,监控目标与Https网站之间的会话连接,或者监控WPA-TKIP保护的网络。在第一种情况下,攻击者向另一个非Https网站注入了Javascript代码,会诱使访问它的目标计算机迅速多次传输加密身份认证的cookie。通过分析约9227个加密的cookie,攻击者可以将猜测准确率提升至94%。期间,攻击者需要让目标每秒发送4450个请求,在经过75小时后(特殊情况下只需52小时),就可以完成猜解攻击。在两年前发生的一次攻击中,研究人员需要12230个cookie加密来推断实际内容,期间每秒产生了1700个请求。
新型针对WPA-TKIP的攻击只需要1个小时,攻击者可以任意注入、解密数据包。
这项技术不仅可以解密cookie和Wifi数据包,其他高速传输的加密数据流也有可能被解密。技术是通过向加密payload中注入数据,如每个认证cookie或者Wifi数据包中的标准头部。攻击者会通过组合所有可能的值,通过使用统计偏差找出最有可能的组合。
密钥流偏差
RC4生成的密钥流在不同程度上偏向于某些序列,使其容易受到差分攻击。Itsik Mantin和Adi Shamir 证明了密钥流的第二个输出字节以1/128的概率(而不是1/256)偏向于零。这是因为,如果原始状态的第三个字节为零,第二个字节不等于2,则第二个输出字节始终为零。
COSIC的Souradyuti Paul和Bart Preneel表明,RC4的第一个和第二个字节也有偏差。检测此偏差所需的样本数为225字节
Riddhipratim Basu、Shirshendu Ganguly、Subhamoy Maitra和Goutam Paul对RC4 PRGA的单步进行了完整的表征。他们证明了给定i和j,输出的排列的分布是不均匀的,因此,关于j的信息总是泄露到输出中。
在一些协议中(如 SSL 和 TLS 中曾使用的 RC4 加密),攻击者可以收集大量密文,结合已知偏差和统计分析,逐渐恢复明文数据
Fluhrer, Mantin and Shamir 攻击
2001年,Fluhrer、Mantin和Shamir发现:在所有可能的RC4密钥中,输出密钥流前几个字节的统计数据很强的非随机性,泄露了有关密钥的信息。如果将随机数和长期密钥简单地连接起来生成RC4密钥,则可以通过分析大量使用此密钥加密的消息来发现此长期密钥。这一特性被用来破坏802.11无线网络中使用的WEP加密。
协议可以通过丢弃密钥流的初始部分来防御这种攻击。这种修改后的算法传统上称为“RC4-drop[n]”,其中n是丢弃的初始密钥流字节数。SCAN默认值为n=768字节,但保守值为n=3072字节。
Fluhrer、Mantin和Shamir攻击不适用于基于RC4的SSL,因为SSL通过哈希生成用于RC4的加密密钥,这意味着不同的SSL会话具有不相关的密钥。