Decrypt large amount of RSA encrypted data - objective-c

Hi guys I needed simple RSA Encryption Decryption.
I tried the code examples on Apple developer guide, it works perfectly for small amount of text but the example code doesn't cater for situations of large encrypted data.
Take note of the comment that it is suggesting us to "split the data up into blocks equal to plainBufferSize":
- (NSData*)decryptedDataFromData:(NSData*)data usingKey:(SecKeyRef)key
{
OSStatus status = noErr;
size_t cipherBufferSize = [data length];
uint8_t *cipherBuffer = (uint8_t *)[data bytes];
size_t plainBufferSize;
uint8_t *plainBuffer;
// Allocate the buffer
plainBufferSize = SecKeyGetBlockSize(key);
plainBuffer = malloc(plainBufferSize);
if (plainBufferSize < cipherBufferSize) {
// Ordinarily, you would split the data up into blocks
// equal to plainBufferSize, with the last block being
// shorter. For simplicity, this example assumes that
// the data is short enough to fit.
printf("Could not decrypt. Packet too large.\n");
return nil;
}
// Error handling
status = SecKeyDecrypt(key,
kSecPaddingPKCS1,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize
); // 3
// Error handling
// Store or display the decrypted text
if(key) CFRelease(key);
NSData *decrypted = [NSData dataWithBytes:(const void *)plainBuffer length:plainBufferSize];
return decrypted;
}
Any clues on how should I modify this method so that it will split the data in blocks to handle large amount of data?

According to RFC3447 RSAES-PKCS1-v1_5 encryption scheme you are using can operate on messages of length up to k - 11 octets (k is the octet length of the RSA modulus) so if you are using 2048-bit RSA key then maximum length of the plain data to be encrypted is 245 bytes. So you will need to split plain data to the chunks of this size and then encrypt each of them individually but this is rather rare and slow solution. It is much better (and also pretty common) to generate symmetric AES key, encrypt large data using AES algorithm and then encrypt small AES key with RSA key.

Related

How to detect termination character in SChannel-based HTTPS client

I've searched StackOverflow trying to find a similar problem, but haven't come across it, so I am posting this question.
I am trying to write an C++ HTTPS client using Microsoft's SChannel libraries, and I'm getting stochastic errors with chunked message transfer. This issue only seems to occur on very long downloads -- short ones generally work OK. Most of the time the code works properly -- even for long downloads -- but occasionally the recv() command gracefully timesout, disconnecting my TLS session, and other times, I get an incomplete last packet. The stochastic errors appear to be the result of the different size chunks and encryption blocks the server is using to pass the data. I know I need to handle this variation, but while this would be easy to solve on an unencrypted HTTP connection, the encryption aspect is causing me problems.
First, the timeout problem, which occurs about 5% of the time I request large HTTP requests (about 10 MB of data from a single HTTP GET request).
The timeout is resulting because on the last chunk I have specified a bigger receive buffer than the data remaining on a blocking socket. The obvious fix to this is to only request exactly the number of bytes I need for the next chunk, and that is what I did. But for some reason, the amount received from each request is less than what I request, yet appears to be missing no data after decryption. I'm guessing this must be due to some compression in the data stream, but I don't know. IN any event, if it is using compression, I have no idea how to translate the size of the decrypted uncompressed byte stream into the size of compressed encrypted byte stream including the encryption headers and trailers to request the exact right number of bytes. Can anyone help me do that?
The alternative approach is for me to just look for two CR+LFs in a row, which would also signal the end of the HTTPS response. But because the data is encrypted, I can't figure out how to look byte by byte. SChannel's DecryptMessage() seems to do its decryptions in blocks, not byte by byte. Can anyone in this forum provide any advice on how to do byte-by-byte decryption to enable me to look for the end of the chunked output?
The second problem is DecryptMessage sometimes erroneously thinks it is done decrypting before I reach the actual end of the message. The resultant behavior is I go on to the next HTTP request, and I get the rest of the previous response where I am expecting to see the header of the new request.
The obvious solution to this is to check the contents of the decrypted message to see if we actually reached the end, and if not, try to receive more data before sending the next HTTP request. But when I do this, and try to decrypt, I get a decryption error message.
Any advice/help anyone can provide on a strategies would be appreciated. I've attached the relevant code sections for the read/decrypt process of the HTTP body -- I'm not including the header read and parsing because that is working without any problems.
do
{
// Note this receives large files OK, but I can't tell when I hit the end of the buffer, and this
// hangs. Need to consider a non-blocking socket?
// numBytesReceived = recv(windowsSocket, (char*)inputBuffer, inputBufSize, 0);
m_ErrorLog << "Next read size expected " << nextReadSize << endl;
numBytesReceived = recv(windowsSocket, (char*)inputBuffer, nextReadSize, 0);
m_ErrorLog << "NumBytesReceived = " << numBytesReceived << endl;
if (m_BinaryBufLen + numBytesReceived > m_BinaryBufAllocatedSize)
::EnlargeBinaryBuffer(m_BinaryBuffer,m_BinaryBufAllocatedSize,m_BinaryBufLen,numBytesReceived+1);
memcpy(m_BinaryBuffer+m_BinaryBufLen,inputBuffer,numBytesReceived);
m_BinaryBufLen += numBytesReceived;
lenStartDecryptedChunk = decryptedBodyLen;
do
{
// Decrypt the received data.
Buffers[0].pvBuffer = m_BinaryBuffer;
Buffers[0].cbBuffer = m_BinaryBufLen;
Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1
Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2
Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3
Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4
Message.ulVersion = SECBUFFER_VERSION; // Version number
Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
Message.pBuffers = Buffers; // Pointer to array of buffers
scRet = m_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);
if (scRet == SEC_E_INCOMPLETE_MESSAGE)
break;
if( scRet == SEC_I_CONTEXT_EXPIRED )
{
m_ErrorLog << "Server shut down connection before I finished reading" << endl;
m_ErrorLog << "# of Bytes Requested = " << nextReadSize << endl;
m_ErrorLog << "# of Bytes received = " << numBytesReceived << endl;
m_ErrorLog << "Decrypted data to this point = " << endl;
m_ErrorLog << decryptedBody << endl;
m_ErrorLog << "BinaryData just decrypted: " << endl;
m_ErrorLog << Buffers[0].pvBuffer << endl;
break; // Server signalled end of session
}
if( scRet != SEC_E_OK &&
scRet != SEC_I_RENEGOTIATE &&
scRet != SEC_I_CONTEXT_EXPIRED )
{
DisplaySECError((DWORD)scRet,errmsg);
m_ErrorLog << "CSISPDoc::ReadDecrypt(): " << "Failed to decrypt message--Error=" << errmsg;
if (decryptedBody)
m_ErrorLog << decryptedBody << endl;
return scRet;
}
// Locate data and (optional) extra buffers.
pDataBuffer = NULL;
pExtraBuffer = NULL;
for(i = 1; i < 4; i++)
{
if( pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA )
pDataBuffer = &Buffers[i];
if( pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA )
pExtraBuffer = &Buffers[i];
}
// Display the decrypted data.
if(pDataBuffer)
{
length = pDataBuffer->cbBuffer;
if( length ) // check if last two chars are CR LF
{
buff = (PBYTE)pDataBuffer->pvBuffer; // printf( "n-2= %d, n-1= %d \n", buff[length-2], buff[length-1] );
if (decryptedBodyLen+length+1 > decryptedBodyAllocatedSize)
::EnlargeBuffer(decryptedBody,decryptedBodyAllocatedSize,decryptedBodyLen,length+1);
memcpy_s(decryptedBody+decryptedBodyLen,decryptedBodyAllocatedSize-decryptedBodyLen,buff,length);
decryptedBodyLen += length;
m_ErrorLog << buff << endl;
}
}
// Move any "extra" data to the input buffer -- this has not yet been decrypted.
if(pExtraBuffer)
{
MoveMemory(m_BinaryBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
m_BinaryBufLen = pExtraBuffer->cbBuffer; // printf("inputStrLen= %d \n", inputStrLen);
}
}
while (pExtraBuffer);
if (decryptedBody)
{
if (incompletePacket)
p1 = decryptedBody + lenStartFragmentedPacket;
else
p1 = decryptedBody + lenStartDecryptedChunk;
p2 = p1;
pEndDecryptedBody = decryptedBody+decryptedBodyLen;
if (lastDecryptRes != SEC_E_INCOMPLETE_MESSAGE)
chunkSizeBlock = true;
do
{
while (p2 < pEndDecryptedBody && (*p2 != '\r' || *(p2+1) != '\n'))
p2++;
// if we're here, we probably found the end of the current line. The pattern we are
// reading is chunk length, chunk, chunk length, chunk,...,chunk lenth (==0)
if (*p2 == '\r' && *(p2+1) == '\n') // new line character -- found chunk size
{
if (chunkSizeBlock) // reading the size of the chunk
{
pStartHexNum = SkipWhiteSpace(p1,p2);
pEndHexNum = SkipWhiteSpaceBackwards(p1,p2);
chunkSize = HexCharToInt(pStartHexNum,pEndHexNum);
p2 += 2; // skip past the newline character
chunkSizeBlock = false;
if (!chunkSize) // chunk size of 0 means we're done
{
bulkReadDone = true;
p2 += 2; // skip past the final CR+LF
}
nextReadSize = chunkSize+8; // chunk + CR/LF + next chunk size (4 hex digits) + CR/LF + encryption header/trailer
}
else // copy the actual chunk
{
if (p2-p1 != chunkSize)
{
m_ErrorLog << "Warning: Actual chunk size of " << p2 - p1 << " != stated chunk size = " << chunkSize << endl;
}
else
{
// copy over the actual chunk data //
if (m_HTTPBodyLen + chunkSize > m_HTTPBodyAllocatedSize)
::EnlargeBuffer(m_HTTPBody,m_HTTPBodyAllocatedSize,m_HTTPBodyLen,chunkSize+1);
memcpy_s(m_HTTPBody+m_HTTPBodyLen,m_HTTPBodyAllocatedSize,p1,chunkSize);
m_HTTPBodyLen += chunkSize;
m_HTTPBody[m_HTTPBodyLen] = 0; // null-terminate
p2 += 2; // skip over chunk and end of line characters
chunkSizeBlock = true;
chunkSize = 0;
incompletePacket = false;
lenStartFragmentedPacket = 0;
}
}
p1 = p2; // move to start of next chunk field
}
else // got to end of encrypted body with no CR+LF found --> fragmeneted chunk. So we need to read and decrypt at least one more chunk
{
incompletePacket = true;
lenStartFragmentedPacket = p1-decryptedBody;
}
}
while (p2 < pEndDecryptedBody);
lastDecryptRes = scRet;
}
}
while (scRet == SEC_E_INCOMPLETE_MESSAGE && !bulkReadDone);
TLS does not support byte-by-byte decryption.
TLS 1.2 breaks its input into blocks of up to 16 kiB, then encrypts them into ciphertext blocks that are slightly larger due to the need for encryption IVs/nonces and integrity protection tags/MACs. It is not possible to decrypt a block until the entire block is available. You can find the full details at https://www.rfc-editor.org/rfc/rfc5246#section-6.2.
Since you're already able to decrypt the first few blocks (containing the headers), you should be able to read the HTTP length so that you at least know the plaintext length that you're expecting, which you can then compare to the number of bytes that you've decrypted from the stream. That won't tell you how many bytes of ciphertext you need, though -- you can get an upper bound on the size of a fragment by calling m_pSPPI->QueryContextAttributes() and then should read either at least that number of bytes or until end of stream before trying to decrypt.
Have you tried looking at other examples? http://www.coastrd.com/c-schannel-smtp appears to contain a detailed example of an SChannel-based TLS client.
I was finally able to figure this out. I fixed this by decrypting each TCP/IP packet as it came in to check for the CR+LF+CR+LF in the decrypted packet instead of what I had been doing -- trying to consolidate all of the encrypted packets into one buffer prior to decrypting it.
On the "hang" problem, what I thought was happening was that recv() wasn't returning because the amount of data actually received was smaller than my expected receive size. But what actually happened was I had actually received the entire transmission, but I didn't realize it. Thus, I was making additional recv() calls when there was actually no more data to receive. The fact that there was no more data to receive was what caused the connection to time out (causing a "hang").
The truncation problem was occurring because I couldn't detect the CR+LF+CR+LF sequence in the encrypted stream, and I erroneously thought SChannel returned SEC_E_OK on DecryptMessage() only when the entire response was processed.
Both problems were eliminated once I was able to detect the true end of the message by decrypting in piecemeal fashion vs. in bulk.
In order to figure this out, I had to completely restructure the sample SChannel code from www.coastRD.com. While the www.coastRD.com code was very helpful in general, it was written for SMTP transfers, not chunked HTTP encoding. In addition, the way it was written, it was hard to follow the logic for processing variations in how messages were received and processed. Lastly, I spent a lot of time "hacking" Schannel to understand how it behaves and which codes are returned under which conditions, because unfortunately none of that is discussed in any of the Microsoft documentation (that I've seen).
The first thing I needed to understand was how SChannel tries to decrypt a message. In Schannel, the 1st 13 bytes of an encrypted message are the encryption header, and the last 16 bytes are the encryption trailer. I still don't know what the trailer does, but I did realize that the encryption header is never actually encrypted/decrypted. The 1st 5 bytes are just the TLS record header for "application data" (hex code 0x17), followed by two bytes defining the TLS version used, followed by 2 bytes of the TLS record fragment size, followed by leading 0s and one byte which I still haven't figured out.
The reason this matters is that DecryptMessage() only works if the record type is "application data". For any other record type (such as a TLS handshake "finished message), DecryptMessage() won't even try to decrypt it-- it will just return a SEC_E_DECRYPT_FAILURE code.
In addition, I needed to understand that DecryptMessage() often can't decrypt the entire contents of the receive buffer in one pass when using chunked transfer encoding. In order to successfully process the entire contents of the receive buffer and the remainder of the server HTTPS response, I needed to understand two key return codes from DecryptMessage() -- SEC_E_OK and SEC_E_INCOMPLETE_MESSAGE.
When I received SEC_E_OK, it meant DecryptMessage() was able to successfully decrypt at least part of the receive buffer. When this occurred, the 1st 13 bytes (the encryption header) remained unchanged. However, the bytes immediately following the header were decrypted in-place, followed by the encryption trailer (which is also unchanged). Often, there will be additional encrypted data still in the receive buffer after the end of the encryption trailer, which is also unchanged.
Since I was using the SecBufferDesc output buffer structures and 4 SecBuffer structures described in www.coastRD.com's code, I needed to understand that these are not actually 4 separate buffers -- they are just pointers to different locations within the receive buffer. The first buffer is a pointer to the encryption header. The second buffer is a pointer to the beginning of the decrypted data. The 3rd buffer is a pointer to the beginning of the encryption trailer. Lastly, the 4th buffer is a pointer to the "extra" encrypted data that DecryptMessage() was not able to process on the last call.
Once I figured that out, I realized that I needed to copy the decrypted data (the pointer in the second buffer) into a separate buffer, because the receive buffer would probably be overwritten later.
If there was no "extra" data in the 4th buffer, I was done done for the moment -- but this was the exception rather than the rule.
If there was extra data (the usual case), I needed to move that data forward to the very beginning of the receive buffer, and I needed to call DecryptMessage() again. This decrypted the next chunk, and I appended that data to the data I already copied to the separate buffer, and repeated this process until there was either no more data left in the receive buffer to decrypt, or I received a SEC_E_INCOMPLETE_MESSAGE.
If I received a SEC_E_INCOMPLETE_MESSAGE, the data remaining in the receive buffer was unchanged. It wasn't decrypted because it was an incomplete encryption block. Thus, I needed to call recv() again to get more encrypted data from the server to complete the encryption block.
Once that occurred, I appended newly received data to the receive buffer. I appended it to the contents of the receive buffer vs. overwriting it because the latter approach would have overwritten the beginning of the encryption block, producing a SEC_E_DECRYPT_FAILURE message the next time I called DecryptMessage().
Once I appended this new block of data to the receive buffer, I repeated the steps above to decrypt the contents of the receive buffer, and continued to repeat this whole process until I got a SEC_E_OK message on the last chunk of data left in the receive buffer.
But I wasn't necessarily done yet -- there may still be data being sent by the server. Stopping at this point is what caused the truncation issue I had occasionally encountered.
So I now checked the last 4 bytes of the decrypted data to look for CR+LF+CR+LF. If I found that sequence, I knew I had received and decrypted a complete HTTPS response.
But if I hadn't, I needed to call recv() again and repeat the process above until I saw the CR+LF+FR+LF sequence at the end of the data.
Once I implemented this process, I was able to definitively identify the end of the encrypted HTTPS response, which prevented me from making an unnecessary recv() call when no data was remaining, preventing a "hang", as well as prematurely truncating the response.
I apologize for the long answer, but given the lack of documentation on SChannel and its functions like DecryptMessage(), I thought this description of what I learned might be helpful to others who may have also been struggling to use SChannel to process TLS HTTP responses.
Thank you again to user3553031 for trying to help me with this over 7 months ago -- those attempts helped me narrow down the problem.

Ambiguous process calcChecksum

CONTEXT
I'm using a code written to work with a GPS module that connects to the Arduino through serial communication. The module starts each packet with a header (0xb5, 0x62), continues with the information you requested and ends with to bytes of checksum, CK_A, and CK_B. I don't understand the code that calculates that checksum. More info about the algorithm of checksum (8-Bit Fletcher Algorithm) in the module protocol (https://www.u-blox.com/sites/default/files/products/documents/u-blox7-V14_ReceiverDescriptionProtocolSpec_%28GPS.G7-SW-12001%29_Public.pdf), page 74 (87 with index).
MORE INFO
Just wanted to understand the code, it works fine. In the UBX protocol, I mentioned there is also a piece of code that explains how it works (isn't write in c++)
struct NAV_POSLLH {
//Here goes the struct
};
NAV_POSLLH posllh;
void calcChecksum(unsigned char* CK) {
memset(CK, 0, 2);
for (int i = 0; i < (int)sizeof(NAV_POSLLH); i++) {
CK[0] += ((unsigned char*)(&posllh))[i];
CK[1] += CK[0];
}
}
In the link you provide, you can find a link to RFC 1145, containing that Fletcher 8 bit algorithm as well and explaining
It can be shown that at the end of the loop A will contain the 8-bit
1's complement sum of all octets in the datagram, and that B will
contain (n)*D[0] + (n-1)*D[1] + ... + D[n-1].
n = sizeof byte D[];
Quote adjusted to C syntax
Try it with a couple of bytes, pen and paper, and you'll see :)

How do I encrypt a string with an RSA public key from a ASN.1 x509 Byte array?

I have an ASN.1 x509 byte array that contains the modulo and the public key of an RSA pair. My goal is to do whatever it takes to use this to encrypt a string. I'm trying to use openssl in objective-c to accomplish this. Whenever I try to get an RSA object using d2i_X509, it returns null. I'm willing to switch to a different library if I can't accomplish this using openssl. Please help me find something that works.
You generally would not encrypt a string using the public key of an X.509 directly. Instead you would generate a strong random(of a specific quality) key; use normal symmetric encryption (such as AES) and then encyrpt the string with that. You then encrypt the random key with the X.509.
Consult a good PKI/Crypto book (e.g. http://www.amazon.com/Applied-Cryptography-Protocols-Algorithms-Source/dp/0471117099) as to why (sections on key leakage, bit-flipping, padding and (re)encrypting twice).
If you really insist on doing this -have a look at https://github.com/dirkx/smime-add-encryption-for-recipient/blob/master/smime-add-encrypt/main.c its pkcs7_encode_rinfo function.
x509cert = ... something to read your x509 byte array in.
unsigned char *stuff = "Some zecret string";
int stufflen = strlen(stuff);
EVP_PKEY *pkey;
EVP_PKEY_CTX *pctx = NULL;
assert(pkey = = X509_get_pubkey( x509cert));
assert(pctx = EVP_PKEY_CTX_new(pkey, NULL));
assert(EVP_PKEY_encrypt_init(pctx)==1);
assert((EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_ENCRYPT
EVP_PKEY_CTRL_PKCS7_ENCRYPT, 0, ri)==1);
size_t eklen;
assert(EVP_PKEY_encrypt(pctx, NULL, &eklen, stuff, stufflen)==1);
ek = OPENSSL_malloc(eklen);
assert(ek);
unsigned char *ek = NULL;
assert((EVP_PKEY_encrypt(pctx, ek, &eklen, key, keylen)==1);
printf("Encrypted blurp: ");
for(int i = 0; i < eklen; i++) {
printf("0x%02X ", ek[i];
};
printf("\n");
exit(0);

RSA encryption/decryption in objective c

I am tring to implement RSA encryption in my app using SecKeyEncrypt and SecKeyDecrypt.
The issue is that only when the ciphertext is long as KeyblockSize (128,256 etc), I get it decrypted correctly. Also, for each build and run, I get different results (encrypted data and cipher text length) keeping input plain text the same. And on clean and build several times, once in a while cipher text length becomes keyblocksize and so decrypts properly.
Pls help to understand where the problem is. Thanks in advance
Note: From SecKeyEncrypt's definition got to know that the last parameter gives back the no. of bytes written, i.e. ciphertextlength in bytes. This parameter always returns back my keyblocksize for any input string.
But many times the actual ciphertext length varies with this parameter. And whenever ciphertextlength is same as the parameter, gets decrypted correctly.
Posting encrypt and decrypt portions of my code :
- (void)encryptWithPublicKey:(uint8_t *)plainBuffer cipherBuffer:(uint8_t *)cipherBuffer
{
OSStatus status = noErr;
NSLog(#"** original plain text 0: %s", plainBuffer);
size_t plainBufferSize = strlen((char *)plainBuffer);//
size_t cipherBufferSize = CIPHER_BUFFER_SIZE;// its 1024
SecKeyRef key=[self getPublicKeyRef];
NSLog(#"SecKeyGetBlockSize() public = %lu", SecKeyGetBlockSize(key));//it returns 256
// Error handling
// Encrypt using the public.
status = SecKeyEncrypt([self getPublicKeyRef],
kSecPaddingNone,
plainBuffer,
plainBufferSize,
&cipherBuffer[0],
&cipherBufferSize
);
NSLog(#"encryption result code: %ld (size: %lu)", status, cipherBufferSize);
NSLog(#"encrypted text: %s", cipherBuffer);
}
- (void)decryptWithPrivateKey:(uint8_t *)cipherBuffer plainBuffer:(uint8_t *)plainBuffer
{
OSStatus status = noErr;
size_t cipherBufferSize = strlen((char *)cipherBuffer);
// DECRYPTION
size_t plainBufferSize = BUFFER_SIZE;//its 256
// Error handling
status = SecKeyDecrypt([self getPrivateKeyRef],
kSecPaddingNone,
&cipherBuffer[0],
cipherBufferSize,
&plainBuffer[0],
&plainBufferSize
);
NSLog(#"decryption result code: %ld (size: %lu)", status, plainBufferSize);
NSLog(#"FINAL decrypted text: %s", plainBuffer);
}

my vb.net encryption result does not match the xcode encryption result given the same input parameters

I have set up a method in vb.net and in xcode for encrypting a string using as far as i can tell the same parameters for an AES encryption.
I've looked all over the place but cannot find information on whether they use the same encryption algorithm and settings.
this is the vb.net code:
Dim encryptAES As New AesCryptoServiceProvider()
Dim encoding As New UTF8Encoding()
Dim encryptor As ICryptoTransform
encryptAES.Key = encoding.GetBytes("12345678901234567890123456789032")
encryptAES.IV = encoding.GetBytes("1234567890123416")
encryptAES.Mode = CipherMode.CBC
encryptAES.Padding = PaddingMode.PKCS7
encryptor = encryptAES.CreateEncryptor
Dim input As Byte() = encoding.GetBytes("hello")
Dim result = encryptor.TransformFinalBlock(input, 0, input.Length)
Dim hex As String = ""
For i As Integer = 0 To result.Length - 1
hex = hex & result(i).ToString("X2")
Next
ConsoleWrite(hex)
which matches the output of this php encryption:
<?php
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$key256 = '12345678901234567890123456789032';
$iv = '1234567890123416';
$cleartext = 'hello';
printf("plainText: %s\n\n",$cleartext);
if (mcrypt_generic_init($cipher, $key256, $iv) != -1)
{
$cipherText = mcrypt_generic($cipher,$cleartext );
mcrypt_generic_deinit($cipher);
printf("256-bit encrypted result:\n%s\n\n",bin2hex($cipherText));
}
?>
And here's the mac code:
IV = [#"1234567890123416" dataUsingEncoding:NSUTF8StringEncoding];
NSLog([IV base64EncodingWithLineLength:0]);
NSData * localKey = [#"12345678901234567890123456789032" dataUsingEncoding:NSUTF8StringEncoding];
NSLog([localKey base64EncodingWithLineLength:0]);
NSLog(#"encode plaintext:");
NSLog([self.data base64EncodingWithLineLength:0]);
CCCryptorStatus result = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
localKey, kCCKeySizeAES256,
IV, //initialization vector
[self.data mutableBytes], [self.data length], //input
buffer, bufferSize, //output
&numBytesEncrypted );
self.data = [self.data initWithBytes:buffer length:numBytesEncrypted];
NSLog(#"encode end result:");
NSLog([self.data base64EncodingWithLineLength:0]);
So as far as I can tell the mac code must be incorrect as it returns a different value (the same length).
Here are the debug dumps from both the pac and mac versions.
pc (values in base64):
key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMzI=
iv: MTIzNDU2Nzg5MDEyMzQxNg==
plaintext: aGVsbG8=
encrypted: oiUIdi9StezV93+nXctCKw==
mac (values in base 64):
key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMzI=
iv: MTIzNDU2Nzg5MDEyMzQxNg==
plaintext: aGVsbG8=
encrypted: rU5MgfNIjx7zqD5Cdh2mlA==
I also had a look at endianness here is the pc encryped base64 with both endianness and neither match the mac version:
oiUIdi9StezV93+nXctCKw==
K0LLXad/99XstVIvdgglog==
I've now run a test where I've set the key to be 32 0's and the IV to be 16 0's (so endianness should hopefully not matter) and with two more tests heres the results from mac:
BfG2gjzkooiJCXgIQJKyZg== (iOS 5 on device)
n9KJXqThiuKyrNPjo7V7PA== (iOS 5 in simulator)
cCH116bIlLeUOxNDwFt2rg== (iOS 4.3 in simulator)
WTH? all parameters are identical, only change is which version to compile for and each encryption is different...
and the pc result:
wjXeJNI54DzI43fGBPymew==
When inputting the mac encrypted text into the pc to decrypt, I get a 'Padding is invalid and cannot be removed.' error.
Any information anyone could provide would be much appreciated.
Both the mac and pc versions involve first creating a byte array from the sample text, and it is this byte array that is then passed to the encryption algorithm. I would start by comparing those byte arrays to make sure you are encrypting the same data. It looks like in both cases you have utf8 and AES128, but it never hurts to be sure.
Next, the result of the encryption algorithm is also a byte array, which must then be re-encoded as a string. Looking at your results, I see not just two different values, but two completely different kinds of characters. That tells me that the re-encoding may be off. Again, you want to compare the byte arrays.
In both cases, to ensure an accurate compare, I would base64 encode the arrays.