从RNCryptor AES 256头的PHP中检索IV。

时间:2022-11-26 18:32:49

Using the latest source of RNCryptor and attempting to send the encrypted data to a PHP script.

使用最新的RNCryptor源并尝试将加密的数据发送到PHP脚本。

RNCryptor packages the IV into the header section which is prepended onto the actual encrypted data.

RNCryptor将IV打包到标题部分,并将其预置到实际的加密数据上。

- (NSData *)header
{
  uint8_t header[2] = {kRNCryptorFileVersion, self.options};
  NSMutableData *headerData = [NSMutableData dataWithBytes:header length:sizeof(header)];
  if (self.options & kRNCryptorOptionHasPassword) {
    [headerData appendData:self.encryptionSalt]; // 8 bytes
    [headerData appendData:self.HMACSalt]; // 8 bytes
  }
  [headerData appendData:self.IV]; // BlockSizeAES128
  return headerData;
}

I am new to working with binary data in PHP, am I correct with using the following unpack function?

我在PHP中使用二进制数据是新手,我使用下面的unpack函数是正确的吗?

<?
$baseEncodedString = "...";
$data = mb_convert_encoding($baseEncodedString, "UTF-8", "BASE64" );
$array = unpack("Cversion/Coptions/C8salt/C8hmac/C16iv/C*aes", $data);
print_r($array);
?>

NOTE: The encrypted data is Base64 encoded from cocoa before transmission.

注意:加密的数据是Base64,在传输之前由cocoa编码。

The above PHP script returns data such as...

上面的PHP脚本返回诸如……

Array ( [version] => 1 [options] => 1 [salt1] => 109 [salt2] => 195 [salt3] => 185 [salt4] => 71 [salt5] => 130 [salt6] => 209 [salt7] => 230 [salt8] => 25 [hmac1] => 8 [hmac2] => 152 [hmac3] => 188 [hmac4] => 135 [hmac5] => 117 [hmac6] => 169 [hmac7] => 25 [hmac8] => 228 [iv1] => 43 [iv2] => 220 [iv3] => 80 [iv4] => 102 [iv5] => 142 [iv6] => 144 [iv7] => 172 [iv8] => 104 [iv9] => 216 [iv10] => 45 [iv11] => 155 [iv12] => 117 [iv13] => 188 [iv14] => 67 [iv15] => 24 [iv16] => 191 [aes1] => 122 [aes2] => 227 [aes3] => 45 [aes4] => 194 [aes5] => 57 [aes6] => 123 [aes7] => 28 [aes8] => 130 [aes9] => 110 [aes10] => 122 [aes11] => 97 [aes12] => 118 [aes13] => 214 [aes14] => 117 [aes15] => 56 [aes16] => 168 [aes17] => 54 [aes18] => 198 [aes19] => 113 [aes20] => 120 [aes21] => 138 [aes22] => 67 [aes23] => 223 [aes24] => 200 [aes25] => 11 [aes26] => 109 [aes27] => 177 [aes28] => 167 [aes29] => 103 [aes30] => 139 [aes31] => 243 [aes32] => 199 [aes33] => 214 [aes34] => 214 [aes35] => 241 [aes36] => 199 [aes37] => 173 [aes38] => 219 [aes39] => 71 [aes40] => 97 [aes41] => 32 [aes42] => 27 [aes43] => 248 [aes44] => 175 [aes45] => 203 [aes46] => 123 [aes47] => 21 )

阵列((版本)= >[选项]= > 1[salt1]= > 109[salt2]= > 195[salt3]= > 185[salt4]= > 71[salt5]= > 130[salt6]= > 209[salt7]= > 230[salt8]= > 25[hmac1]= > 8[hmac2]= > 152[hmac3]= > 188[hmac4]= > 135[hmac5]= > 117[hmac6]= > 169[hmac7]= > 25[hmac8]= > 228[iv1]= > 43[iv2]= > 220[iv3]= > 80[iv4]= > 102[iv5]= > 142[iv6]= > 144[iv7]= > 172[iv8]= > 104[iv9]= > 216[iv10]= > 45[iv11]= > 155[iv12]= > 117[iv13]= > 188[iv14]= > 67[iv15]= > 24[iv16]=[aes1]= > 122 > 191[aes2]= > 227[aes3]= > 45[aes4]= > 194[aes5]= > 57[aes6]= > 123[aes7]= > 28[aes8]= > 130[aes9]= > 110[aes10]= > 122[aes11]= > 97[aes12]= > 118[aes13]= > 214[aes14]= > 117[aes15]= > 56[aes16]= > 168[aes17]= > 54[aes18]= > 198[aes19]= > 113[aes20]= > 120[aes21]= > 138[aes22]= > 67[aes23]= > 223[aes24]= > 200[aes25]11[aes26]= > 109 = >[aes27]= > 177[aes28]= > 167[aes29]= > 103[aes30]= > 139[aes31]= > 243[aes32]= > 199[aes33]= > 214[aes34]= > 214[aes35]= > 241宋体;mso - fareast - font - family:宋体;mso - bidi - font - family: " times new roman "; mso - bidi - theme - font:

How am I able to use this in the PHP MCrypt functions?

我如何在PHP MCrypt函数中使用它?

Thanks.

谢谢。


EDIT

编辑

In response to drew010's answer I have updated my PHP script to the following...

为了响应drew010的答案,我将我的PHP脚本更新为以下内容…

<?
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}

$base = $_GET['base'];
$data = mb_convert_encoding($base, "UTF-8", "BASE64" );
//$data = base64_decode($base);

$header = array();
$header['ver'] = substr($data, 0, 1);
$header['options'] = substr($data, 1, 1);
$header['salt'] = substr($data, 2, 8);
$header['hmac'] = substr($data, 10, 8);
$header['iv'] = substr($data, 18, 16);
$data = substr($data, 34);

$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, pbkdf2('SHA256', 'password', $header['salt'], 10000, 16), $header['iv']);

//$decrypted = mcrypt_decrypt('rijndael-256','password',$data,'',$header['iv']);
$decrypted = mdecrypt_generic($td, $data);
echo $decrypted;
?>

How ever I am still getting scrambled text.

我怎么还在纠结文本。

U¸¦uÀ­ˆÆ&bŸ8:f`ôShŽºÃ~:¾ÉöÁß=Ç®nqäà€•Æ‹ò

U¸¦uA­ˆÆ&bÿ8:f 'oShŽº~:¾EoAß= C®nqaa€•Æ喜爱o

I looked back at RNCryptor and used the following values for the PHP script

我查看了RNCryptor,并使用了PHP脚本的以下值。

static const RNCryptorSettings kRNCryptorAES256Settings = {
    .algorithm = kCCAlgorithmAES128,
    .blockSize = kCCBlockSizeAES128,
    .IVSize = kCCBlockSizeAES128,
    .options = kCCOptionPKCS7Padding,
    .HMACAlgorithm = kCCHmacAlgSHA256,
    .HMACLength = CC_SHA256_DIGEST_LENGTH,

    .keySettings = {
        .keySize = kCCKeySizeAES256,
        .saltSize = 8,
        .PBKDFAlgorithm = kCCPBKDF2,
        .PRF = kCCPRFHmacAlgSHA1,
        .rounds = 10000
    },

    .HMACKeySettings = {
        .keySize = kCCKeySizeAES256,
        .saltSize = 8,
        .PBKDFAlgorithm = kCCPBKDF2,
        .PRF = kCCPRFHmacAlgSHA1,
        .rounds = 10000
    }
};

This function I believe produces the key?

我认为这个函数产生了键?

+ (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt settings:(RNCryptorKeyDerivationSettings)keySettings
{
  NSMutableData *derivedKey = [NSMutableData dataWithLength:keySettings.keySize];

  int result = CCKeyDerivationPBKDF(keySettings.PBKDFAlgorithm,         // algorithm
                                    password.UTF8String,                // password
                                    password.length,                    // passwordLength
                                    salt.bytes,                         // salt
                                    salt.length,                        // saltLen
                                    keySettings.PRF,                    // PRF
                                    keySettings.rounds,                 // rounds
                                    derivedKey.mutableBytes,            // derivedKey
                                    derivedKey.length);                 // derivedKeyLen

  // Do not log password here
  // TODO: Is is safe to assert here? We read salt from a file (but salt.length is internal).
  NSAssert(result == kCCSuccess, @"Unable to create AES key for password: %d", result);

  return derivedKey;
}

Thanks again.

再次感谢。

Is MCRYPT_RIJNDAEL_128 correct? Even though RNCryptor settings suggest its using 256 the actually algorithm is 128 and the IV size relates to the 128 block size. I have read somewhere to force PHP to use the 16 byte IV you must use MCRYPT_RIJNDAEL_128 then to have 256 feed it a 32 byte key.

MCRYPT_RIJNDAEL_128正确吗?即使RNCryptor设置建议使用256,实际上算法是128,IV大小与128块大小有关。我已经阅读了一些地方来强制PHP使用16字节,您必须使用MCRYPT_RIJNDAEL_128,然后有256个feed,一个32字节的密钥。

3 个解决方案

#1


4  

This works for me with the latest RNCryptor in iOS

这对我在iOS中的最新RNCryptor很有用。

$b64_data: base64-encoded encrypted data
$pwd: password

$b64_data: base64编码的加密数据$pwd:密码。

// back to binary
$bin_data = mb_convert_encoding($b64_data, "UTF-8", "BASE64");
// extract salt
$salt = substr($bin_data, 2, 8);
// extract HMAC salt
$hmac_salt = substr($bin_data, 10, 8);
// extract IV
$iv = substr($bin_data, 18, 16);
// extract data
$data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
// extract HMAC
$hmac = substr($bin_data, strlen($bin_data) - 32);

// make HMAC key
$hmac_key = $this->pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
// make HMAC hash
$hmac_hash = hash_hmac('sha256', $data , $hmac_key, true);
// check if HMAC hash matches HMAC
if($hmac_hash != $hmac) return false;

// make data key
$key = $this->pbkdf2('SHA1', $password, $salt, 10000, 32, true);
// decrypt
$ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
return trim(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\xFF]/u', '', $ret));

pbkdf2 is the same as in the question above, from https://defuse.ca/php-pbkdf2.htm.

pbkdf2与上面的问题一样,来自https://defuse.ca/php-pbkdf2.htm。

#2


1  

You shouldn't need to use unpack for this.

您不需要使用unpack来做这个。

Once you receive the full base64 encoded string, decode it, and now you should have a binary string which has the IV at the beginning of the string.

一旦你接收到完整的base64编码的字符串,解码它,现在你应该有一个二进制字符串,它在字符串的开头有IV。

You can then use substr() to grab each piece you require from the data.

然后可以使用substr()来从数据中获取所需的每个部分。

For example:

例如:

$base = $_GET['base'];
$data = base64_decode($base);

$iv   = substr($data, 0, 32);  // get 32 byte IV
$data = substr($data, 32);     // set data to begin after the IV now

If you have other fields prepended to the ciphertext, make sure to do the same as above with the other data in the correct sequence.

如果你有其他的字段预置到密文中,请确保在正确的序列中与上面的其他数据相同。

Once you have those pieces of data, you can pass $data to mcrypt along with the IV and your key.

一旦您拥有了这些数据,您就可以将$data和IV和您的密钥一起传递给mcrypt。

#3


0  

Is MCRYPT_RIJNDAEL_128 correct? Even though RNCryptor settings suggest its using 256 the actually algorithm is 128 and the IV size relates to the 128 block size. I have read somewhere to force PHP to use the 16 byte IV you must use MCRYPT_RIJNDAEL_128 then to have 256 feed it a 32 byte key.

MCRYPT_RIJNDAEL_128正确吗?即使RNCryptor设置建议使用256,实际上算法是128,IV大小与128块大小有关。我已经阅读了一些地方来强制PHP使用16字节,您必须使用MCRYPT_RIJNDAEL_128,然后有256个feed,一个32字节的密钥。

The "128" in MCRYPT_RIJNDAEL_128 refers to the block size, not the key size. The Rijndael algorithm can handle several block sizes, but AES can only handle 128 bit blocks. This is independent of the key-size. The CBC IV should always be the block size, which again is always 16 bytes in AES. (Rijndael and AES are very similar, but not identical. Rijndael is more flexible than AES.)

MCRYPT_RIJNDAEL_128中的“128”是指块大小,而不是密钥大小。Rijndael算法可以处理多个块大小,但是AES只能处理128位块。这与密钥大小无关。CBC IV应该始终是块大小,这在AES中总是16个字节。(Rijndael和AES非常相似,但并不完全相同。)Rijndael比AES更灵活。

In your pbkdf2() function, you should pass a key-length of 32 bytes (256 bits), not 16 bytes. I believe that the PHP mcrypt module will automatically switch to 256-bit AES if passed a 256-bit key (based on the comments on Understanding PHP AES Encryption; I'm not particularly familiar with mcrypt). I am assuming you're implementing PBKDF2 correctly; I haven't studied your code there.

在您的pbkdf2()函数中,您应该传递一个密钥长度32字节(256位),而不是16字节。我相信PHP mcrypt模块会自动切换到256位AES,如果传递了一个256位的密钥(基于理解PHP AES加密的评论;我对mcrypt不是特别熟悉。我假设你正确地实现了PBKDF2;我没有研究过你的代码。

Note that RNCryptor appends a 32-byte HMAC at the end. I believe your current code will try to decrypt that, leading to 32 bytes of garbage at the end. Typically you should strip this HMAC off and verify it to ensure that the data was not modified in transit and that the password is correct.

请注意,RNCryptor在最后附加一个32字节的HMAC。我相信您当前的代码将尝试解密,最终导致32字节的垃圾。通常,您应该删除这个HMAC,并验证它以确保数据在传输过程中没有被修改,并且密码是正确的。

#1


4  

This works for me with the latest RNCryptor in iOS

这对我在iOS中的最新RNCryptor很有用。

$b64_data: base64-encoded encrypted data
$pwd: password

$b64_data: base64编码的加密数据$pwd:密码。

// back to binary
$bin_data = mb_convert_encoding($b64_data, "UTF-8", "BASE64");
// extract salt
$salt = substr($bin_data, 2, 8);
// extract HMAC salt
$hmac_salt = substr($bin_data, 10, 8);
// extract IV
$iv = substr($bin_data, 18, 16);
// extract data
$data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
// extract HMAC
$hmac = substr($bin_data, strlen($bin_data) - 32);

// make HMAC key
$hmac_key = $this->pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
// make HMAC hash
$hmac_hash = hash_hmac('sha256', $data , $hmac_key, true);
// check if HMAC hash matches HMAC
if($hmac_hash != $hmac) return false;

// make data key
$key = $this->pbkdf2('SHA1', $password, $salt, 10000, 32, true);
// decrypt
$ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
return trim(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\xFF]/u', '', $ret));

pbkdf2 is the same as in the question above, from https://defuse.ca/php-pbkdf2.htm.

pbkdf2与上面的问题一样,来自https://defuse.ca/php-pbkdf2.htm。

#2


1  

You shouldn't need to use unpack for this.

您不需要使用unpack来做这个。

Once you receive the full base64 encoded string, decode it, and now you should have a binary string which has the IV at the beginning of the string.

一旦你接收到完整的base64编码的字符串,解码它,现在你应该有一个二进制字符串,它在字符串的开头有IV。

You can then use substr() to grab each piece you require from the data.

然后可以使用substr()来从数据中获取所需的每个部分。

For example:

例如:

$base = $_GET['base'];
$data = base64_decode($base);

$iv   = substr($data, 0, 32);  // get 32 byte IV
$data = substr($data, 32);     // set data to begin after the IV now

If you have other fields prepended to the ciphertext, make sure to do the same as above with the other data in the correct sequence.

如果你有其他的字段预置到密文中,请确保在正确的序列中与上面的其他数据相同。

Once you have those pieces of data, you can pass $data to mcrypt along with the IV and your key.

一旦您拥有了这些数据,您就可以将$data和IV和您的密钥一起传递给mcrypt。

#3


0  

Is MCRYPT_RIJNDAEL_128 correct? Even though RNCryptor settings suggest its using 256 the actually algorithm is 128 and the IV size relates to the 128 block size. I have read somewhere to force PHP to use the 16 byte IV you must use MCRYPT_RIJNDAEL_128 then to have 256 feed it a 32 byte key.

MCRYPT_RIJNDAEL_128正确吗?即使RNCryptor设置建议使用256,实际上算法是128,IV大小与128块大小有关。我已经阅读了一些地方来强制PHP使用16字节,您必须使用MCRYPT_RIJNDAEL_128,然后有256个feed,一个32字节的密钥。

The "128" in MCRYPT_RIJNDAEL_128 refers to the block size, not the key size. The Rijndael algorithm can handle several block sizes, but AES can only handle 128 bit blocks. This is independent of the key-size. The CBC IV should always be the block size, which again is always 16 bytes in AES. (Rijndael and AES are very similar, but not identical. Rijndael is more flexible than AES.)

MCRYPT_RIJNDAEL_128中的“128”是指块大小,而不是密钥大小。Rijndael算法可以处理多个块大小,但是AES只能处理128位块。这与密钥大小无关。CBC IV应该始终是块大小,这在AES中总是16个字节。(Rijndael和AES非常相似,但并不完全相同。)Rijndael比AES更灵活。

In your pbkdf2() function, you should pass a key-length of 32 bytes (256 bits), not 16 bytes. I believe that the PHP mcrypt module will automatically switch to 256-bit AES if passed a 256-bit key (based on the comments on Understanding PHP AES Encryption; I'm not particularly familiar with mcrypt). I am assuming you're implementing PBKDF2 correctly; I haven't studied your code there.

在您的pbkdf2()函数中,您应该传递一个密钥长度32字节(256位),而不是16字节。我相信PHP mcrypt模块会自动切换到256位AES,如果传递了一个256位的密钥(基于理解PHP AES加密的评论;我对mcrypt不是特别熟悉。我假设你正确地实现了PBKDF2;我没有研究过你的代码。

Note that RNCryptor appends a 32-byte HMAC at the end. I believe your current code will try to decrypt that, leading to 32 bytes of garbage at the end. Typically you should strip this HMAC off and verify it to ensure that the data was not modified in transit and that the password is correct.

请注意,RNCryptor在最后附加一个32字节的HMAC。我相信您当前的代码将尝试解密,最终导致32字节的垃圾。通常,您应该删除这个HMAC,并验证它以确保数据在传输过程中没有被修改,并且密码是正确的。