PHP和Go RC4-Decrypt

I want PHP and Go to produce the same output. Go's encryption code should be RC4-MD5 I tried to decrypt it with py-openssl, and the result was the same as Go. I think maybe php-openssl lacks streaming.

this is my Go code.

    iv, _ := hex.DecodeString("8ec07bf03d8c5eaf37619aaac1cb305c") // iv

    key := evpBytesToKey("123456", 16) // build Key
 fmt.Println(hex.EncodeToString(key))//e10adc3949ba59abbe56e057f20f883e
    h := md5.New()
    h.Write(key)
    h.Write(iv)
    rc4key := h.Sum(nil)
    fmt.Println(hex.EncodeToString(rc4key)) //fd0eb4cfd6b0174968d9979781d95670
    cipher, _ := rc4.NewCipher(rc4key) // rc4key

    text, _ := hex.DecodeString("f1")
    dist := make([]byte, len(text))
    cipher.XORKeyStream(dist, text);
    fmt.Println(hex.EncodeToString(dist)) // 03

    text, _ = hex.DecodeString("f1")
    dist = make([]byte, len(text))
    cipher.XORKeyStream(dist, text);
    fmt.Println(hex.EncodeToString(dist)) // 0e

Output

e10adc3949ba59abbe56e057f20f883e
fd0eb4cfd6b0174968d9979781d95670
03
0e
func evpBytesToKey(password string, keyLen int) (key []byte) {
    const md5Len = 16

    cnt := (keyLen-1)/md5Len + 1
    m := make([]byte, cnt*md5Len)
    copy(m, md5sum([]byte(password)))

    // Repeatedly call md5 until bytes generated is enough.
    // Each call to md5 uses data: prev md5 sum + password.
    d := make([]byte, md5Len+len(password))
    start := 0
    for i := 1; i < cnt; i++ {
        start += md5Len
        copy(d, m[start-md5Len:start])
        copy(d[md5Len:], password)
        copy(m[start:], md5sum(d))
    }
    return m[:keyLen]
}

func md5sum(d []byte) []byte {
    h := md5.New()
    h.Write(d)
    return h.Sum(nil)
}

this is my php code.


//base64_decode('jsB78D2MXq83YZqqwcswXA==') == hex.DecodeString("8ec07bf03d8c5eaf37619aaac1cb305c")
$iv = base64_decode('jsB78D2MXq83YZqqwcswXA==');
$key = evpBytesToKey("123456", 16);
var_dump(binToHex($key));
$rc4key = md5($key . $iv, true);
var_dump(binToHex($rc4key));
//base64_decode('8Q==') == hex.DecodeString("f1")
var_dump(binToHex(openssl_decrypt('8Q==', 'rc4', $rc4key, 0)));
var_dump(binToHex(openssl_decrypt('8Q==', 'rc4', $rc4key, 0)));

Output

string(32) "e10adc3949ba59abbe56e057f20f883e"
string(32) "fd0eb4cfd6b0174968d9979781d95670"
string(2) "03"
string(2) "03"
function evpBytesToKey(string $password, int $keyLen)
{
    $md5Len = 16;
    $cnt = (int)(($keyLen - 1) / $md5Len + 1);
    $m = md5($password, true);
    $start = 0;
    for ($i = 1; $i < $cnt; $i++) {
        //Todo
    }
    return substr($m, 0, $keyLen);
}

function binToHex(string $src)
{
    //from go/src/encoding/hex/hex.go
    $dst = '';
    $hextable = '0123456789abcdef';
    $len = strlen($src);
    for ($i = 0; $i < $len; $i++) {
        $v = ord($src[$i]);
        $dst[$i * 2] = $hextable[$v >> 4];
        $dst[$i * 2 + 1] = $hextable[$v & 0x0f];
    }
    return $dst;
}

I expect php output to be the same as Go's.