Devos50
Devos50

Reputation: 2035

RSA encryption using SecKeyEncrypt gives error -4 (errSecUnimplemented)

I'm trying to encrypt some data with RSA using the Security framework on iOS. I want to encrypt a simple base64-encoded string as follows:

NSData *data = [[NSData alloc] initWithBase64EncodedString:@"aGFsbG8=" options:0x0];
NSData *encrypted = [pair encrypt:data];

The pair variable holds references to a private key and a public key that were succesfully generated before using SecKeyGeneratePair.

The encrypt function looks like this:

- (NSData *)encrypt:(NSData *)data {
    void *buffer = malloc([self blockSize] * sizeof(uint8_t));
    memset(buffer, 0x0, [self blockSize]);
    size_t ciphertextBufferLength = [data length];
    OSStatus res = SecKeyEncrypt([self keyRef], 0x1, [data bytes], [data length], &buffer[0], &ciphertextBufferLength);
    NSLog(@"Result of encryption: %d", res);
    return [NSData dataWithBytesNoCopy:buffer length:[self blockSize] freeWhenDone:YES];
}

The implementation of [self blockSize] is rather straightforward:

- (unsigned long)blockSize {
    return SecKeyGetBlockSize(keyRef);
}

I generate my keys with the following function:

- (BOOL)generateNewKeyPairOfSize:(unsigned int)keySize
{
    SecKeyRef privKey = [[self publicKey] keyRef];
    SecKeyRef pubKey = [[self publicKey] keyRef];

    NSDictionary *privateKeyDict = @{ (__bridge id)kSecAttrIsPermanent : @(YES), (__bridge id)kSecAttrApplicationTag : [[self privateKey] tag] };
    NSDictionary *publicKeyDict = @{ (__bridge id)kSecAttrIsPermanent : @(YES), (__bridge id)kSecAttrApplicationTag : [[self publicKey] tag] };
    NSDictionary *keyDict = @{ (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA, (__bridge id)kSecAttrKeySizeInBits : @(keySize), (__bridge id)kSecPublicKeyAttrs : publicKeyDict, (__bridge id)kSecPrivateKeyAttrs : privateKeyDict };
    OSStatus res = SecKeyGeneratePair((__bridge CFDictionaryRef)keyDict, &privKey, &pubKey);
    NSLog(@"Result of generating keys: %d", res);

    [[self publicKey] setKeyRef:pubKey];
    [[self privateKey] setKeyRef:privKey];

    return YES;
}

The problem is that the value of res is -4, meaning errSecUnimplemented according to the documentation. I'm not sure what I'm doing wrong here since I have all parameters required. I'm not sure whether there is a mistake in the parameters and where. The call the [self blockSize] return 128.

Can anyone help me with this?

Upvotes: 2

Views: 2453

Answers (2)

CommaToast
CommaToast

Reputation: 12188

In addition to the correct answer above, what solved it for me was the following knowledge:

You will get -4 if you are trying to encrypt anything using a SecKeyRef that refers to a private key.

Think about it. Nothing that was encrypted with a private key would be secure because the public key is, well, public. /facepalm

So yeah, Apple's framework does the responsible thing, and simply blocks you from encrypting something with a private key. Because if it allowed you to do something that stupid, then it would be giving you a false sense of security, which would be irresponsible.

Upvotes: 1

John Tracid
John Tracid

Reputation: 4046

From documentation:

cipherTextLen - On entry, the size of the buffer provided in the cipherText parameter. On return, the amount of data actually placed in the buffer.

You don't set any value to ciphertextBufferLength.

Update #1

In SecKeyGeneratePair() you have wrong arguments: public key argument must be first, private key is the second. I think that is the reason why you have error code -4.

Update #2

When you fix problem from Update #1 you will see error code -50 (errSecParam) because your cipher text length is wrong. Here is how correct encryption/decryption looks like:

[self generateNewKeyPairOfSize:1024];

NSData *data = [[NSData alloc] initWithBase64EncodedString:@"aGFsbG8=" options:0x0];

size_t cipherTextSize = [self blockSize];
uint8_t *cipherText = malloc(cipherTextSize);
memset(cipherText, 0, cipherTextSize);
OSStatus res = SecKeyEncrypt(_publicKey, kSecPaddingPKCS1, [data bytes], data.length, cipherText, &cipherTextSize);
NSLog(@"Result of encryption: %d", res);

size_t plainTextSize = cipherTextSize;
uint8_t *plainText = malloc(plainTextSize);
res = SecKeyDecrypt(_privateKey, kSecPaddingPKCS1, cipherText, cipherTextSize, plainText, &plainTextSize);
NSLog(@"Result of decryption: %d", res);

Upvotes: 5

Related Questions