2012-10-21 3 views
3

최신 RNCryptor 소스를 사용하고 암호화 된 데이터를 PHP 스크립트로 보내려고합니다.PHP에서 RNCryptor AES 256 헤더에서 IV 검색

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; 
} 

PHP의 바이너리 데이터 작업에 익숙하지 않은 경우 다음 언팩 기능을 사용하여 올바르게 수정 했습니까?

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

참고 : 암호화 된 데이터는 전송 전 코코아에서 인코딩 된 Base64입니다.

상기 PHP 스크립트는 ...

어레이 ([버전] => 1 [옵션] => 1 salt1] => 109 salt2] => 195 [salt3 같은 데이터를 리턴 ] [> hmac1] => 8 [hmac2] => 152 [hmac3] => 185 [소금 4] => 71 [소금 5] => 130 소금 6 소금 => 209 소금 7 =>230 [소금 8] => [hmac4] => 25 [hmac8] => 228 [iv1] => 43 [iv2] => 220 [iv3] => 188 [hmac4] = > 80 [iv4] => 102 [iv5] => 142 [iv6] => 144 [iv7] => 172 [iv8] 155 [iv12] => 117 [iv13] => 188 [iv14] => 67 [iv15] => 24 [iv16] => 191 [aes1] => 1 [aes6] => 123 [aes7] => 28 [aes8] => 130 [aes9] => 110 [aes2] => 227 [aes3] => 45 [aes4] => 194 [aes5] => 57 [aes6] [aes10] => 117 [aes15] => 56 [aes16] => 168 [aes17] => 54 [aes10] => 012 [aes10] => [aes18] => 198 [aes19] => 113 [aes20] => 120 [aes21] => 138 [aes22] aes26] => 109 [aes27] => 177 [aes28] => 167 [aes29] => 103 [aes31] => 139 [aes31] => 243 [aes32] => 199 [aes33] => 214 [aes34] [aes39] => 71 [aes36] => 199 [aes37] => 173 [aes38] => 012 [aes39] => => 27 [aes43] => 248 [aes44] ​​=> 175 [aes46] => 203 [aes46] => 123 [aes47] => 21)

PHP MCrypt 함수에서 어떻게 사용할 수 있습니까?

감사합니다.


편집 나는 다음에 내 PHP 스크립트를 업데이트 한 drew010의 대답에 대응

... 난 아직 텍스트를 스크램블 얻고 어떻게 이제까지

<? 
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; 
?> 

.

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

내가 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 
    } 
}; 

이 함수는 키를 생성한다고 생각합니까?

+ (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; 
} 

다시 한번 감사드립니다.

MCRYPT_RIJNDAEL_128이 맞습니까? RNCryptor 설정이 256을 사용한다고 제안하더라도 실제로 알고리즘은 128이고 IV 크기는 128 블록 크기와 관련이 있습니다. 16 바이트 IV를 사용하기 위해 PHP를 강제로 사용하기 위해 MCRYPT_RIJNDAEL_128을 사용하고 나서 256 바이트를 32 바이트 키로 먹여야합니다.

답변

4

이 아이폰 OS

$b64_data의 최신 RNCryptor와 나를 위해 작동 : 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)); 

pbkdf2https://defuse.ca/php-pbkdf2.htm에서, 위의 문제와 동일합니다.

+0

이것은 HMAC를 제거합니다. HMAC가 없으면 다른 사람이 도중에 메시지를 수정했는지 감지 할 수 없습니다. 공격자가 일반 텍스트의 일부를 알고 있으면 해독이 다르게 읽도록 암호문을 수정할 수 있습니다. 예를 들어 Eve가 Alice가 Bob에게 $ 10을 보내는 것을 알고 있으면 암호를 모른 채 $ 99를 Eve로 보내도록 메시지를 변경할 수 있습니다. 이런 종류의 공격을 막기 때문에 HMAC를 확인하는 것이 좋습니다. (그럼에도 불구하고 코드 덕분에 @bubba_gump.) –

+0

내 기쁨, Rob. 비록 내가 공격자가 그 일을 할 수있는 방법을 알기에 충분히 정교하지는 않지만, 당신이 말하는 것을 이해합니다. 그 구멍을 막기 위해 HMAC를 어떻게 생각할 수 있습니까? –

+0

다음은 파이썬 공격에 대한 단계별 설명입니다. https://github.com/rnapier/security-blunders/blob/master/modaes.py –

1

이렇게하려면 unpack을 사용할 필요가 없습니다.

전체 base64 인코딩 된 문자열을받은 후 디코드하면 이제 문자열의 시작 부분에 IV가있는 이진 문자열을 가져야합니다.

그런 다음 substr()을 사용하여 필요한 각 데이터를 가져올 수 있습니다. 예를 들어

: 당신이 암호문을 위해 미리 다른 필드가있는 경우

$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 

, 올바른 순서에있는 다른 데이터를 위와 동일한 작업을 수행해야합니다.

일단 데이터가 있으면, $data을 IV와 키와 함께 mcrypt에 전달할 수 있습니다.

+0

답장을 보내 주셔서 감사합니다. 나는 여전히 스크램블 된 텍스트를 받고 있습니다. 나는 당신의 답을 바탕으로 수정 된 원본으로 원본을 편집했다. –

+0

C면에있는 것처럼 PHP 필드에서 헤더 필드 (salt, IV, version, hmac 등)가 PHP 측면에서 동일합니까? – drew010

0

MCRYPT_RIJNDAEL_128이 맞습니까? RNCryptor 설정이 256을 사용한다고 제안하더라도 실제로 알고리즘은 128이고 IV 크기는 128 블록 크기와 관련이 있습니다. 16 바이트 IV를 사용하기 위해 PHP를 강제로 사용하기 위해 MCRYPT_RIJNDAEL_128을 사용하고 나서 256 바이트를 32 바이트 키로 먹여야합니다.

MCRYPT_RIJNDAEL_128의 "128"은 키 크기가 아니라 블록 크기를 나타냅니다. Rijndael 알고리즘은 여러 블록 크기를 처리 할 수 ​​있지만 AES는 128 비트 블록 만 처리 할 수 ​​있습니다. 이는 키 크기와 무관합니다. CBC IV는 항상 AES에서 항상 16 바이트 인 블록 크기 여야합니다. (Rijndael과 AES는 매우 유사하지만 동일하지는 않습니다.) Rydndael은 AES보다 유연합니다.

pbkdf2() 함수에서 16 바이트가 아닌 32 바이트 (256 비트)의 키 길이를 전달해야합니다. 나는 PHP mcrypt 모듈이 256 비트 키를 전달할 경우 자동으로 256 비트 AES로 전환 할 것이라고 생각한다. (Understanding PHP AES Encryption에 대한 의견에 기반하여, 특히 mcrypt에 익숙하지 않다.) PBKDF2를 올바르게 구현하고 있다고 가정합니다. 거기서 당신의 코드를 공부하지 않았습니다.

RNCryptor는 끝에 32 바이트 HMAC를 추가합니다. 나는 당신의 현재 코드가 32 바이트의 쓰레기를 결국에는 해독하려고 시도 할 것이라고 믿는다. 일반적으로이 HMAC를 제거하고 데이터가 전송 중에 수정되지 않았고 암호가 올바른지 확인해야합니다.

관련 문제