问题
Using the latest source of RNCryptor and attempting to send the encrypted data to a PHP script.
RNCryptor packages the IV into the header section which is prepended onto the actual encrypted data.
- (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?
<?
$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.
The above PHP script returns data such as...
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 )
How am I able to use this in the PHP MCrypt functions?
Thanks.
EDIT
In response to drew010's answer I have updated my PHP script to the following...
<?
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äà€•Æ‹ò
I looked back at RNCryptor and used the following values for the PHP script
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.
回答1:
This works for me with the latest RNCryptor in iOS
$b64_data
: base64-encoded encrypted data$pwd
: password
// 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.
回答2:
You shouldn't need to use unpack for this.
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.
You can then use substr()
to grab each piece you require from the data.
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.
回答3:
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.
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.)
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.
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.
来源:https://stackoverflow.com/questions/13000843/retrieve-iv-from-rncryptor-aes-256-header-in-php