RC家族加密算法详解

Sher10ck 发布于 2026-04-07 207 次阅读


一、引言

近期也是对加密算法也是做了一套详尽的学习,主要是前期学习过程中仅记忆了相关特征,并未进行深度研究,RC家族将以以下顺序展开:RC 家族全貌 → 再重点讲 RC4 原理、逆向识别、代码实现 → 再补 RC2/RC5/RC6 的结构特征与最小实现。

常见成员:

  • RC2:分组密码
  • RC4:流密码(最为常见)
  • RC5:参数化分组密码
  • RC6:RC5 的改进版,AES 候选算法之一

二、RC 家族总体区别

算法类型核心特点CTF中常见度逆向识别难度
RC2分组密码64-bit 分组,历史较老
RC4流密码256字节状态数组,KSA+PRGA很高
RC5分组密码参数化设计,数据相关循环移位中高
RC6分组密码在RC5基础上加入乘法,128-bit分组中低

三、RC4

RC4是加解密可逆的加密算法,因此我们只讲述加密过程,RC4加密主要实现两个阶段:

KSA(Key Scheduling Algorithm,密钥调度)

  • 初始化 S[0..255]
  • 用密钥不断打乱 S 盒

PRGA(Pseudo-Random Generation Algorithm,伪随机生成)

与明文异或得到密文

不断交换 S[i]S[j]

输出密钥流字节

KSA

S[i]=i,i=0,1,,255S[i] = i,\quad i=0,1,\dots,255j=(j+S[i]+K[imodkeylen])mod256j = (j + S[i] + K[i \bmod keylen]) \bmod 256

然后交换:S[i],S[j]=S[j],S[i]S[i], S[j] = S[j], S[i]

PRGA

i=(i+1)mod256i = (i + 1) \bmod 256j=(j+S[i])mod256j = (j + S[i]) \bmod 256

交换:S[i],S[j]=S[j],S[i]S[i], S[j] = S[j], S[i]

输出索引:t=(S[i]+S[j])mod256t = (S[i] + S[j]) \bmod 256

密钥流字节:k=S[t]k = S[t]

最终:cipher[i]=plain[i]kcipher[i] = plain[i] \oplus k

这么说可能有些抽象,我们直接用一段代码来展示

def rc4_crypt(key: bytes, data: bytes) -> bytes:
    # KSA
    S = list(range(256))
    j = 0
    key_len = len(key)

    for i in range(256):
        j = (j + S[i] + key[i % key_len]) & 0xFF
        S[i], S[j] = S[j], S[i]

    # PRGA
    i = 0
    j = 0
    out = bytearray()

    for byte in data:
        i = (i + 1) & 0xFF
        j = (j + S[i]) & 0xFF
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) & 0xFF]
        out.append(byte ^ k)

    return bytes(out)

四、RC2(基本不考魔改)

RC2的加密方式我们不做详细研究,只需要知道RC2是DES的替代算法,仿照的还是DES的相关模式,明文和密文都是64bit构成,密钥长度为1bit到128bit,下面给出简单库函数调用方法

#ECB模式
from Crypto.Cipher import ARC2
from Crypto.Util.Padding import pad, unpad

key = b"12345678"   # RC2 常见 8~16 字节
plaintext = b"hello rc2"

cipher = ARC2.new(key, ARC2.MODE_ECB)
ciphertext = cipher.encrypt(pad(plaintext, ARC2.block_size))

cipher2 = ARC2.new(key, ARC2.MODE_ECB)
decrypted = unpad(cipher2.decrypt(ciphertext), ARC2.block_size)

print("ciphertext:", ciphertext.hex())
print("decrypted :", decrypted)

#CBC模式
from Crypto.Cipher import ARC2
from Crypto.Util.Padding import pad, unpad

key = b"12345678"
iv = b"12345678"
plaintext = b"hello rc2 cbc"

cipher = ARC2.new(key, ARC2.MODE_CBC, iv=iv)
ciphertext = cipher.encrypt(pad(plaintext, ARC2.block_size))

cipher2 = ARC2.new(key, ARC2.MODE_CBC, iv=iv)
decrypted = unpad(cipher2.decrypt(ciphertext), ARC2.block_size)

print("ciphertext:", ciphertext.hex())
print("decrypted :", decrypted)

五、RC5|RC6

RC5 最关键的标签有三个:

  • w:字长
  • r:轮数
  • b:密钥长度(字节)

如果反编译代码中大量出现 ROL/ROR,而且移位位数不是常数,而是由另一个寄存器或变量决定,那么很可能要往 RC5/RC6 方向想。

RC5 逆向识别点

  1. 常见魔数:
    • 0xB7E15163
    • 0x9E3779B9
  2. 分组通常是两个字(如两个 32 位)
  3. 每轮结构很像: A=((AB)B)+S[2i]A = ((A \oplus B) \lll B) + S[2i]B=((BA)A)+S[2i+1]B = ((B \oplus A) \lll A) + S[2i+1]

RC6 可以说是“RC5 加强版”。

RC6采用128-bit 分组

用四个寄存器:

A, B, C, D

大量循环移位

还会出现乘法:t=(B×(2B+1))log2wt = (B \times (2B+1)) \lll \log_2 wu=(D×(2D+1))log2wu = (D \times (2D+1)) \lll \log_2 w

#RC5
def rol(x, r, w=32):
    mask = (1 << w) - 1
    r &= (w - 1)
    return ((x << r) & mask) | (x >> (w - r))


def ror(x, r, w=32):
    mask = (1 << w) - 1
    r &= (w - 1)
    return (x >> r) | ((x << (w - r)) & mask)


def rc5_key_schedule(key: bytes, w=32, r=12):
    # 这里只写最常用的 w=32
    if w != 32:
        raise ValueError("This minimal implementation uses w=32 only.")

    P = 0xB7E15163
    Q = 0x9E3779B9
    u = w // 8

    c = max(1, (len(key) + u - 1) // u)
    L = [0] * c

    # 按 little-endian 装入 L
    for i in range(len(key) - 1, -1, -1):
        L[i // u] = ((L[i // u] << 8) + key[i]) & 0xFFFFFFFF

    t = 2 * (r + 1)
    S = [0] * t
    S[0] = P
    for i in range(1, t):
        S[i] = (S[i - 1] + Q) & 0xFFFFFFFF

    A = B = i = j = 0
    for _ in range(3 * max(c, t)):
        A = S[i] = rol((S[i] + A + B) & 0xFFFFFFFF, 3, w)
        B = L[j] = rol((L[j] + A + B) & 0xFFFFFFFF, (A + B), w)
        i = (i + 1) % t
        j = (j + 1) % c

    return S


def rc5_encrypt_block(block: bytes, S, w=32, r=12):
    if len(block) != 8:
        raise ValueError("RC5-32 block size must be 8 bytes.")

    A = int.from_bytes(block[:4], "little")
    B = int.from_bytes(block[4:], "little")

    A = (A + S[0]) & 0xFFFFFFFF
    B = (B + S[1]) & 0xFFFFFFFF

    for i in range(1, r + 1):
        A = (rol(A ^ B, B, w) + S[2 * i]) & 0xFFFFFFFF
        B = (rol(B ^ A, A, w) + S[2 * i + 1]) & 0xFFFFFFFF

    return A.to_bytes(4, "little") + B.to_bytes(4, "little")


def rc5_decrypt_block(block: bytes, S, w=32, r=12):
    if len(block) != 8:
        raise ValueError("RC5-32 block size must be 8 bytes.")

    A = int.from_bytes(block[:4], "little")
    B = int.from_bytes(block[4:], "little")

    for i in range(r, 0, -1):
        B = ror((B - S[2 * i + 1]) & 0xFFFFFFFF, A, w) ^ A
        A = ror((A - S[2 * i]) & 0xFFFFFFFF, B, w) ^ B

    B = (B - S[1]) & 0xFFFFFFFF
    A = (A - S[0]) & 0xFFFFFFFF

    return A.to_bytes(4, "little") + B.to_bytes(4, "little")


if __name__ == "__main__":
    key = bytes(range(16))      # 16字节密钥
    pt = b"12345678"            # 8字节分组

    S = rc5_key_schedule(key, w=32, r=12)
    ct = rc5_encrypt_block(pt, S, w=32, r=12)
    dt = rc5_decrypt_block(ct, S, w=32, r=12)

    print("pt:", pt)
    print("ct:", ct.hex())
    print("dt:", dt)
#RC6
def rol(x, r, w=32):
    mask = (1 << w) - 1
    r &= (w - 1)
    return ((x << r) & mask) | (x >> (w - r))


def ror(x, r, w=32):
    mask = (1 << w) - 1
    r &= (w - 1)
    return (x >> r) | ((x << (w - r)) & mask)


def rc6_key_schedule(key: bytes, w=32, r=20):
    if w != 32:
        raise ValueError("This minimal implementation uses w=32 only.")

    P = 0xB7E15163
    Q = 0x9E3779B9
    u = w // 8

    c = max(1, (len(key) + u - 1) // u)
    L = [0] * c

    for i in range(len(key) - 1, -1, -1):
        L[i // u] = ((L[i // u] << 8) + key[i]) & 0xFFFFFFFF

    t = 2 * r + 4
    S = [0] * t
    S[0] = P
    for i in range(1, t):
        S[i] = (S[i - 1] + Q) & 0xFFFFFFFF

    A = B = i = j = 0
    for _ in range(3 * max(c, t)):
        A = S[i] = rol((S[i] + A + B) & 0xFFFFFFFF, 3, w)
        B = L[j] = rol((L[j] + A + B) & 0xFFFFFFFF, (A + B), w)
        i = (i + 1) % t
        j = (j + 1) % c

    return S


def rc6_encrypt_block(block: bytes, S, w=32, r=20):
    if len(block) != 16:
        raise ValueError("RC6 block size must be 16 bytes.")

    A = int.from_bytes(block[0:4], "little")
    B = int.from_bytes(block[4:8], "little")
    C = int.from_bytes(block[8:12], "little")
    D = int.from_bytes(block[12:16], "little")

    B = (B + S[0]) & 0xFFFFFFFF
    D = (D + S[1]) & 0xFFFFFFFF

    lgw = 5  # log2(32)

    for i in range(1, r + 1):
        t = rol((B * ((2 * B + 1) & 0xFFFFFFFF)) & 0xFFFFFFFF, lgw, w)
        u = rol((D * ((2 * D + 1) & 0xFFFFFFFF)) & 0xFFFFFFFF, lgw, w)

        A = (rol(A ^ t, u, w) + S[2 * i]) & 0xFFFFFFFF
        C = (rol(C ^ u, t, w) + S[2 * i + 1]) & 0xFFFFFFFF

        A, B, C, D = B, C, D, A

    A = (A + S[2 * r + 2]) & 0xFFFFFFFF
    C = (C + S[2 * r + 3]) & 0xFFFFFFFF

    return (
        A.to_bytes(4, "little") +
        B.to_bytes(4, "little") +
        C.to_bytes(4, "little") +
        D.to_bytes(4, "little")
    )


def rc6_decrypt_block(block: bytes, S, w=32, r=20):
    if len(block) != 16:
        raise ValueError("RC6 block size must be 16 bytes.")

    A = int.from_bytes(block[0:4], "little")
    B = int.from_bytes(block[4:8], "little")
    C = int.from_bytes(block[8:12], "little")
    D = int.from_bytes(block[12:16], "little")

    lgw = 5

    C = (C - S[2 * r + 3]) & 0xFFFFFFFF
    A = (A - S[2 * r + 2]) & 0xFFFFFFFF

    for i in range(r, 0, -1):
        A, B, C, D = D, A, B, C

        u = rol((D * ((2 * D + 1) & 0xFFFFFFFF)) & 0xFFFFFFFF, lgw, w)
        t = rol((B * ((2 * B + 1) & 0xFFFFFFFF)) & 0xFFFFFFFF, lgw, w)

        C = ror((C - S[2 * i + 1]) & 0xFFFFFFFF, t, w) ^ u
        A = ror((A - S[2 * i]) & 0xFFFFFFFF, u, w) ^ t

    D = (D - S[1]) & 0xFFFFFFFF
    B = (B - S[0]) & 0xFFFFFFFF

    return (
        A.to_bytes(4, "little") +
        B.to_bytes(4, "little") +
        C.to_bytes(4, "little") +
        D.to_bytes(4, "little")
    )


if __name__ == "__main__":
    key = bytes(range(16))
    pt = b"0123456789ABCDEF"   # 16字节分组

    S = rc6_key_schedule(key, w=32, r=20)
    ct = rc6_encrypt_block(pt, S, w=32, r=20)
    dt = rc6_decrypt_block(ct, S, w=32, r=20)

    print("pt:", pt)
    print("ct:", ct.hex())
    print("dt:", dt)
此作者没有提供个人介绍。
最后更新于 2026-04-07