shadowsocks rc4-md5算法介绍

 

shadowsocks协议早期使用RC4加密算法用于加密数据,不过因为每次数据都适用同一个密钥流加密,存在很大的安全隐患,后面更新了RC4-MD5算法。

即便关于RC4-MD5协议的资料很难找到,而且RC4-MD5算法现在也不在安全,但还是有必要介绍一遍,因为它简单,很便于理解,在后期也会展示RC4算法的缺陷。

RC4-MD5本质上还是RC4对数据进行加密。MD5的意思是对key进行MD5运算,得到RC4的key,最终会被RC4算法用于生成密钥流。简单来说,用户的密码会经过下面步骤转换为RC4的keystream

  1. k1 = toBytes(password)
  2. k2 = ssKey(k1)
  3. rc4-md5-key = md5(toBytes(k2,iv))

上面步骤中,第一步把用户输入的密码转换为字节数组,第二步通过ss函数生成通用的ssKey,第三步生成RC4-MD5算法所需的key,过程就是把sskey和一个随机iv按顺序放到一个字节数组,在使用md5进行运算,得到一个最终的结果,这个Key就是RC4算法将要使用的Key。

sskey的生成算法如下

private static byte[] generateSSKey(String password) {
  byte[] keys = new byte[32];
  byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
  byte[] hash = Hash.md5(bytes);
  byte[] tmp = new byte[bytes.length + hash.length];
  System.arraycopy(hash, 0, keys, 0, hash.length);

  for (int i=hash.length;i<keys.length;i+=hash.length) {
    System.arraycopy(hash, 0, tmp, 0, hash.length);
    System.arraycopy(bytes, 0, tmp, hash.length, bytes.length);
    hash = Hash.md5(tmp);
    System.arraycopy(hash, 0, keys, i, hash.length);
  }

  return keys;
}

rc4-md5-key生成算法如下

public static byte[] rc4md5Key(String password,byte[] iv) {
  byte[] ssKeys = generateSSKey(password);
  byte[] keys = new byte[ssKeys.length + iv.length];
  System.arraycopy(ssKeys,0,keys,0,ssKeys.length);
  System.arraycopy(iv,0,keys,ssKeys.length,iv.length);

  return Hash.md5(keys);
}

当我们得到Key后,就可以使用它来对数据进行加密

public byte[] encrypt(byte[] keys,byte[] plaintext) throw Exception{
	Cipher encoder = Cipher.getInstance("RC4");
  encoder.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keys, "RC4"));
  return encoder.update(content.getBytes());
}

当我们得到密文后,还需要把IV传给ss-server,ss-server会使用同样的算法计算出rc4-md5-key,从而对数据进行解密,组装方式如下:

public void combine() {
  String plaintext = "123456";
  String password = "654321";
  byte[] iv = randomIV();
  
  byte[] key = rc4md5Key(password,iv);
  byte[] ciphertext = encrypt(key,plaintext.getBytes());
  ByteArrayOutputStream os = new ByteArrayOutputStream(key.length + ciphertext.length);
  os.writeBytes(key);
  os.writeBytes(ciphertext);
  
  byte[] data = os.toByteArray();
}

最终我们得到的data就是需要发送给服务端的数据。

虽然RC4-MD5算法早就可以被识别了,不过它足够简单,可以帮助我们理解为什么流加密很脆弱,作为一种入门的算法是不二之选。后续的文章将会继续介绍RC4算法的原理,以及对它的攻击方式。