RC4 算法实现和介绍

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会话具有不相关的密钥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值