我在 Java 中使用 CTR 模式加密了一条消息,并尝试在 iOS 中解密该消息。但是,我的测试程序在解密时会扰乱最后一个 block
代码如下:
+ (NSData *)doCipher:(NSData *)data key:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt {
CCCryptorStatus status = kCCSuccess;
// symmetric cipher reference
CCCryptorRef cryptor = NULL;
// Cipher Text container.
NSData *cipherOrPlainText = nil;
// Pointer to output buffer.
uint8_t *bufferPtr = NULL;
// Remaining bytes to be performed on.
size_t remainingBytes = 0;
// Number of bytes moved to buffer.
size_t movedBytes = 0;
// Total size of the buffer.
size_t bufferPtrSize = 0;
// Placeholder for total written.
size_t totalBytesWritten = 0;
// A friendly helper pointer.
LOGGING_FACILITY(data != nil, @"PlainText object cannot be nil." );
LOGGING_FACILITY(symmetricKey != nil, @"Symmetric key object cannot be nil." );
LOGGING_FACILITY([symmetricKey length] == kCCKeySizeAES256, @"Disjoint choices for key size." );
// pointer to the bytes
void *inputDatPtr = [data bytes];
// Initialization vector;
void *ivPtr;
void *dataPtr = &inputDatPtr[0];
size_t dataBufferSize = [data length];
if (encryptOrDecrypt == kCCDecrypt) {
// iv is first block
ivPtr = &inputDatPtr[0];
dataPtr+= kCCBlockSizeAES128;
dataBufferSize -= kCCBlockSizeAES128;
// NSData *iv = [[data subdataWithRange:NSMakeRange(0, kCCBlockSizeAES128)] copy];
// ivPtr = [iv bytes];
// NSData *inputData = [[data subdataWithRange:NSMakeRange(kCCBlockSizeAES128, data.length - kCCBlockSizeAES128)] copy];
// dataPtr = [inputData bytes];
} else {
NSData *iv = [self generateIv];
ivPtr = [iv bytes];
}
LOGGING_FACILITY(dataBufferSize > 0, @"Empty plaintext passed in." );
// Create and Initialize the cipher reference.
status = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding,
ivPtr, [symmetricKey bytes], kCCKeySizeAES256, NULL, 0, 0, kCCModeOptionCTR_BE, &cryptor);
LOGGING_FACILITY1( status == kCCSuccess, @"Problem creating the context, status == %d.", status );
// Calculate byte block alignment for all calls through to and including final.
bufferPtrSize = CCCryptorGetOutputLength(cryptor, dataBufferSize, true);
// Allocate buffer.
bufferPtr = malloc(bufferPtrSize * sizeof(uint8_t));
// Zero out buffer.
memset((void *) bufferPtr, 0x0, bufferPtrSize);
// Initialize some necessary book keeping.
uint8_t *ptr = bufferPtr;
// Set up initial size.
remainingBytes = bufferPtrSize;
// Actually perform the encryption or decryption.
status = CCCryptorUpdate(cryptor,
dataPtr,
dataBufferSize,
ptr,
remainingBytes,
&movedBytes
);
LOGGING_FACILITY1( status == kCCSuccess, @"Problem with CCCryptorUpdate, status == %d.", status );
// Handle book keeping.
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
// Finalize everything to the output buffer.
status = CCCryptorFinal(cryptor,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if (cryptor) {
(void) CCCryptorRelease(cryptor);
cryptor = NULL;
}
LOGGING_FACILITY1( status == kCCSuccess, @"Problem with encipherment status == %d", status );
cipherOrPlainText = [NSData dataWithBytes:bufferPtr length:(NSUInteger) totalBytesWritten];
if (bufferPtr) free(bufferPtr);
return cipherOrPlainText;
}
测试代码为:
NSString *base64Encrypted = [NSString stringWithFormat:@"HWwBZ7Tw94Bk6qTWbXlvRvISkLZrxxy7bmHG1pFWMGgsuA2LY1Q="];
NSData *encrypted = [NSData dataWithBase64EncodedString:base64Encrypted];
NSString *hexKey = @"38a3ba5932c14cd99924eb303fab0c35f300e1bf022286d15160edd247ef263c";
NSData *keyData = [Crypto dataForHexString:hexKey];
NSData *data = [Crypto doCipher:encrypted key:keyData context:kCCDecrypt];
NSString *decryptedText = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(@"decrypted: %@", decryptedText);
消息应该是:多 block 消息
但是我得到的输出:
2012-11-29 11:04:22.209 crypto-test[23748:1307] decrypted: Multiple block mtn4¶
请注意,IV 是随机生成的,并添加到密文前面,并且数据应该是 UTF8 编码的(但是,当将编码设置为 NSUTF8StringEncoding 时,我得到 NULL 输出)
请您参考如下方法:
因此,在使用 ECB 实现 CTR 后,它解密成功,这表明我上面的 CTR 实现或 CommonCrypto 实现中存在错误。
工作代码:
void xor(uint16_t *data, uint16_t *data2, size_t count, uint16_t *out) {
uint8_t i;
for(i = 0; i < count; i++){
out[i] = data[i] ^ data2[i];
}
}
+ (NSMutableData *)incrementCtrWithIv:(NSData *)iv increment:(uint16_t)increment {
NSMutableData *ctrBytes = [[NSMutableData alloc] initWithCapacity:kCCBlockSizeAES128];
NSMutableArray *arrayOfBytes = [[NSMutableArray alloc] initWithCapacity:8];
bool carry = true;
// increment the counter (which is the last 8 bytes of the IV) by 1
for (int k = 7; k >= 0 ; k--) {
uint16_t ctr = 0;
[iv getBytes:&ctr range:NSMakeRange((NSUInteger) (8+k), 1)];
if (carry){
if ((ctr + increment) > 255){
ctr = (uint16_t) (ctr + increment) % 256;
increment -= ctr;
} else {
ctr += increment;
carry = false;
}
}
[arrayOfBytes addObject:[NSData dataWithBytes:&ctr length:1]];
}
// append the bytes in correct order (reverse of above)
for (int k=7; k>=0;k--) {
[ctrBytes appendData:[arrayOfBytes objectAtIndex:(NSUInteger) k]];
}
return ctrBytes;
}
+ (NSData *)symmetricCipher:(NSData *)data key:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt {
// Cipher Text container.
NSMutableData *cipherOrPlainText = [[NSMutableData alloc] initWithCapacity:([data length] + kCCBlockSizeAES128)];
// Initialization vector;
NSData *iv = [[data subdataWithRange:NSMakeRange(0, kCCBlockSizeAES128)] copy];
NSUInteger dataLength = data.length;
uint8_t start = 0;
if (encryptOrDecrypt == kCCDecrypt) {
// iv is first block
start += kCCBlockSizeAES128;
dataLength-=kCCBlockSizeAES128;
} else {
iv = [self generateIv];
}
NSData *incrementalIv = [iv copy];
size_t blocks = ((dataLength + kCCBlockSizeAES128) / kCCBlockSizeAES128);
// should fit for less than 2^16 blocks
for (uint16_t j = 0; j<blocks; j++) {
size_t realLengthRemaining = dataLength - j*kCCBlockSizeAES128;
size_t actualLength = (size_t) (realLengthRemaining > kCCBlockSizeAES128 ? kCCBlockSizeAES128 : realLengthRemaining);
if (realLengthRemaining > 0){
// increment the counter (which is the last 8 bytes of the IV) by 1
if (0 != j) {
NSData *ctrBytes = [Crypto incrementCtrWithIv:incrementalIv increment:1];
// start with nonce
NSMutableData *ctrIv = [[iv subdataWithRange:NSMakeRange(0, 8)] mutableCopy];
// and append the counter
[ctrIv appendData:ctrBytes];
// next Iv is equal to this calculated iv
incrementalIv = ctrIv;
}
// symmetric cipher reference
CCCryptorRef cryptor = NULL;
// Pointer to output buffer.
uint8_t *bufferPtr = NULL;
// Remaining bytes to be performed on.
size_t remainingBytes = 0;
// Number of bytes moved to buffer.
size_t movedBytes = 0;
// Total size of the buffer.
size_t bufferPtrSize = 0;
// Placeholder for total written.
size_t totalBytesWritten = 0;
// A friendly helper pointer.
LOGGING_FACILITY(data != nil, @"PlainText object cannot be nil." );
LOGGING_FACILITY(symmetricKey != nil, @"Symmetric key object cannot be nil." );
LOGGING_FACILITY([symmetricKey length] == kCCKeySizeAES256, @"Disjoint choices for key size." );
// Create and Initialize the cipher reference.
CCCryptorStatus status = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES128, ccNoPadding,
nil, [symmetricKey bytes], kCCKeySizeAES256, NULL, 0, 0, 0, &cryptor);
LOGGING_FACILITY1( status == kCCSuccess, @"Problem creating the context, status == %d.", status );
// Calculate byte block alignment for all calls through to and including final.
bufferPtrSize = CCCryptorGetOutputLength(cryptor, kCCBlockSizeAES128, true);
// Allocate buffer.
bufferPtr = malloc(bufferPtrSize * sizeof(uint8_t));
// Zero out buffer.
memset((void *) bufferPtr, 0x0, bufferPtrSize);
// Initialize some necessary book keeping.
uint8_t *ptr = bufferPtr;
// Set up initial size.
remainingBytes = bufferPtrSize;
// Actually perform the encryption or decryption.
status = CCCryptorUpdate(cryptor,
[incrementalIv bytes],
kCCBlockSizeAES128,
ptr,
remainingBytes,
&movedBytes
);
LOGGING_FACILITY1( status == kCCSuccess, @"Problem with CCCryptorUpdate, status == %d.", status );
// Handle book keeping.
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
// Finalize everything to the output buffer.
status = CCCryptorFinal(cryptor,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if (cryptor) {
(void) CCCryptorRelease(cryptor);
cryptor = NULL;
}
NSData *out = [NSData dataWithBytes:bufferPtr length:(NSUInteger) totalBytesWritten];
if (bufferPtr) free(bufferPtr);
NSData *input = [data subdataWithRange:NSMakeRange(start + j*kCCBlockSizeAES128, actualLength)];
uint16_t xord[kCCBlockSizeAES128] = {0};
// xor the counter with the message block
xor([input bytes],[out bytes], actualLength, xord);
LOGGING_FACILITY1( status == kCCSuccess, @"Problem with encipherment status == %d", status );
NSData *blockData = [NSData dataWithBytes:xord length:(NSUInteger) actualLength];
NSString *decryptedText = [[NSString alloc] initWithData:blockData encoding:NSASCIIStringEncoding];
// NSLog(@"decrypted block %d: %@", j ,decryptedText);
[cipherOrPlainText appendData:blockData];
[self wipeData:blockData];
}
}
if (encryptOrDecrypt == kCCEncrypt) {
NSMutableData *cipherText = [[NSMutableData alloc] initWithData:iv];
[cipherText appendData:cipherOrPlainText];
cipherOrPlainText = cipherText;
}
[self wipeData:iv];
[self wipeData:incrementalIv];
return cipherOrPlainText;
}
+ (void)wipeData:(NSData *)data {
memset([data bytes], 0, [data length]);
}