6
votes

I am using SecKeyEncrypt with a JSON formatted string as input. If pass SecKeyEncrypt a plainTextLength of less than 246, it works. If I pass it a length of 246 or more, it fails with return value: paramErr (-50).

It could be a matter of the string itself. An example of what I might send SecKeyEncrypt is:

{"handle":"music-list","sym_key":"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALeaEO7ZrjgOFGLBzBHZtQuzH2GNDYMLWP+fIFNu5Y+59C6HECY+jt0yOXXom2mzp/WYYI/9G+Ig8OD6YiKv2nMCAwEAAQ==","app_id":"xgfdt.LibraryTestApp","api_key":"7e080f74de3625b90dd293fc8be560a5cdfafc08"}

The 245th character is '0'.

The ONLY input that is changing between this working and is the plainTextLength. SecKeyGetBlockSize() is returning 256 to me, so any input up to 256 characters long should work.

Here is my encrypt method:

+ (NSData*)encrypt:(NSString*)data usingPublicKeyWithTag:(NSString*)tag
{

    OSStatus status = noErr;

    size_t cipherBufferSize;
    uint8_t *cipherBuffer;

    // [cipherBufferSize]
    size_t dataSize = 246;//[data lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    const uint8_t* textData = [[data dataUsingEncoding:NSUTF8StringEncoding] bytes];

    SecKeyRef publicKey = [Encryption copyPublicKeyForTag:tag];

    NSAssert(publicKey, @"The public key being referenced by tag must have been stored in the keychain before attempting to encrypt data using it!");

    //  Allocate a buffer

    cipherBufferSize = SecKeyGetBlockSize(publicKey);
    // this value will not get modified, whereas cipherBufferSize may.
    const size_t fullCipherBufferSize = cipherBufferSize;
    cipherBuffer = malloc(cipherBufferSize);

    NSMutableData* accumulatedEncryptedData = [NSMutableData dataWithCapacity:0];

    //  Error handling

    for (int ii = 0; ii*fullCipherBufferSize < dataSize; ii++) {
        const uint8_t* dataToEncrypt = (textData+(ii*fullCipherBufferSize));
        const size_t subsize = (((ii+1)*fullCipherBufferSize) > dataSize) ? fullCipherBufferSize-(((ii+1)*fullCipherBufferSize) - dataSize) : fullCipherBufferSize;

        // Encrypt using the public key.
        status = SecKeyEncrypt(    publicKey,
                               kSecPaddingPKCS1,
                               dataToEncrypt,
                               subsize,
                               cipherBuffer,
                               &cipherBufferSize
                               );

        [accumulatedEncryptedData appendBytes:cipherBuffer length:cipherBufferSize];
    }

    if (publicKey) CFRelease(publicKey);

    free(cipherBuffer);

    return accumulatedEncryptedData;
}
1

1 Answers

9
votes

From the documentation:

plainTextLen
Length in bytes of the data in the plainText buffer. This must be less than or equal to the value returned by the SecKeyGetBlockSize function. When PKCS1 padding is performed, the maximum length of data that can be encrypted is 11 bytes less than the value returned by the SecKeyGetBlockSize function (secKeyGetBlockSize() - 11).

(emphasis mine)

You're using PKCS1 padding. So if the block size is 256, you can only encrypt up to 245 bytes at a time.