SecKeyGeneratePair returns errSecUnimplemented - ios

Im attempting to implement an RSA encryption algorithm into my iOS app, but when I attempt to generate a public and private key pair, the function returns me the errSecUnimplemented error. I am using the 5.1 SDK and targeting 5.1 at the moment.
Can I not use this function, or did I set up something wrong in attempting to generate the pair?
Here is my code for the key generation:
SecKeyRef publicKey, privateKey;
CFDictionaryRef parameters;
const void* keys[] = {kSecAttrKeyType, kSecAttrKeyTypeRSA};
int keySize = 1024;
const void *values[] = {kSecAttrKeySizeInBits, &keySize};
parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
{
NSLog(#"Key success!");
}
else
{
NSLog(#"Key Failure! %li", ret);
}

I've revised it to just complete the solution for you. 1) You need to use a CFNumberRef and not a pointer to an int for the numerical value. 2) The values need to be the values, the keys need to be the keys - you were mixing a key and value in each of "keys" and "values".
SInt32 iKeySize = 1024;
CFNumberRef keySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &iKeySize);
const void* values[] = { kSecAttrKeyTypeRSA, keySize };
const void* keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
CFDictionaryRef parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
SecKeyRef publicKey, privateKey;
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
NSLog(#"Key success!");
else
NSLog(#"Key Failure! %li", ret);

Shouldn't this be:
const void* keys[] = {kSecAttrKeyType, kSecAttrKeySizeInBits};
int keySize = 1024;
const void *values[] = {kSecAttrKeyTypeRSA, &keySize};
i.e., the keys should be the keys of the dict and the values the values, currently you have one (key,value) pair in keys and one in values.

Using kCFAllocatorSystemDefault instead of kCFAllocatorDefault return errSecSuccess.

Related

Export public key to decompressed (x,y) representation begins with 0x04 and has 65 bytes length

I have below sample code for generating private key (link to gist):
https://gist.github.com/kynwu/3a65e238fcd189d516bb2de59527a320
after generated private key, I derived public key by calling SecKeyCopyPublicKey((SecKeyRef)privateKey)
I got below value showing (x,y) of the public key.
<SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256 bits, y: 1778DB79819A67BC9211E003557CB55EA0C7A19154B1C0828B30F30AD208ABCD, x: 7A1C65A955F46B9937A12E19139DE25C3F19368A3C2DD7360791E42382C98716, addr: 0x1003019f0>
Now, I want to transfer this (x,y) to 0x04|x-32bytes|y-32byteswhich is 65 bytes binary data, thus I can output this binary represents the public key to server. How to achieve this?
Use CoreFoundation's CFDictionaryGetValue method.
const void *CFDictionaryGetValue(CFDictionaryRef theDict, const void *key);
Below function will give 65 bytes UnCompressed public key.
+ (NSData *) publicKeyBits
{
return (NSData *) CFDictionaryGetValue((CFDictionaryRef)[self lookupPublicKeyRef], kSecValueData);
}
+ (SecKeyRef) lookupPublicKeyRef
{
CFMutableDictionaryRef getPublicKeyQuery = newCFDict;
CFDictionarySetValue(getPublicKeyQuery, kSecClass, kSecClassKey);
CFDictionarySetValue(getPublicKeyQuery, kSecAttrKeyType, kSecAttrKeyTypeEC);
CFDictionarySetValue(getPublicKeyQuery, kSecAttrApplicationTag, kPublicKeyName);
CFDictionarySetValue(getPublicKeyQuery, kSecAttrKeyClass, kSecAttrKeyClassPublic);
CFDictionarySetValue(getPublicKeyQuery, kSecReturnData, kCFBooleanTrue);
CFDictionarySetValue(getPublicKeyQuery, kSecReturnPersistentRef, kCFBooleanTrue);
OSStatus status = SecItemCopyMatching(getPublicKeyQuery, (CFTypeRef *)&publicKeyRef);
if (status == errSecSuccess)
return (SecKeyRef)publicKeyRef;
else if (status == errSecItemNotFound)
return nil;
else
[NSException raise:#"Unexpected OSStatus" format:#"Status: %i", status];
return false;
}
kPublicKeyName can be any constant string to be associated as a tag to public key.
for example - #define kPublicKeyName #"com.trailer.ECKey.public"

Signing data with kSecAttrKeyTypeEC key on iOS

I'm trying to sign data and verify the signature using Elliptic Curve algorithm on iOS. Creating the keys works well enough, but attempting to sign the data returns error -1 - which is very generic.
The keys are created as follows:
publicKeyRef = NULL;
privateKeyRef = NULL;
NSDictionary * privateKeyAttr = #{(id)kSecAttrIsPermanent : #1,
(id)kSecAttrApplicationTag : privateTag};
NSDictionary * publicKeyAttr = #{(id)kSecAttrIsPermanent : #1,
(id)kSecAttrApplicationTag : privateTag};
NSDictionary * keyPairAttr = #{(id)kSecAttrKeySizeInBits : #(keySize),
(id)kSecAttrKeyType : (id)kSecAttrKeyTypeEC,
(id)kSecPrivateKeyAttrs : privateKeyAttr,
(id)kSecPublicKeyAttrs : publicKeyAttr};
OSStatus status = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
This returns status 0, so far so good. The actual signing happens like this:
- (NSData *) signData:(NSData *)dataToSign withPrivateKey:(SecKeyRef)privateKey {
NSData * digestToSign = [self sha1DigestForData:dataToSign];
size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
uint8_t * signedHashBytes = malloc( signedHashBytesSize * sizeof(uint8_t) );
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);
OSStatus signErr = SecKeyRawSign(privateKey,
kSecPaddingPKCS1,
digestToSign.bytes,
digestToSign.length,
(uint8_t *)signedHashBytes,
&signedHashBytesSize);
NSLog(#"Status: %d", signErr);
NSData * signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];
if (signedHashBytes) free(signedHashBytes);
return (signErr == noErr) ? signedHash : nil;
}
- (NSData *)sha1DigestForData:(NSData *)data {
NSMutableData *result = [[NSMutableData alloc] initWithLength:CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG) data.length, result.mutableBytes);
return result;
}
The call to SecKeyRawSign() returns -1.
This is adapted from https://forums.developer.apple.com/message/95740#95740
What is the correct way to use an EC key for signing data? There is a working solution for RSA keys here: Signing and Verifying on iOS using RSA but I was unable to adapt it to EC keys.
Seems like part of the trouble is with the correct syntax when creating the pointers and calculating the size of data for calls to SecKeyRawSign. A working example in Swift 3 looks like this:
Generate keys, stored in the Secure Enclave (and temporarily in instance variables):
func generateKeyPair() -> Bool {
if let access = SecAccessControlCreateWithFlags(nil,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
[.userPresence, .privateKeyUsage],
nil) {
let privateKeyAttr = [kSecAttrIsPermanent : 1,
kSecAttrApplicationTag : privateTag,
kSecAttrAccessControl as String: access
] as NSDictionary
let publicKeyAttr = [kSecAttrIsPermanent : 0,
kSecAttrApplicationTag : publicTag
] as NSDictionary
let keyPairAttr = [kSecAttrKeySizeInBits : 256,
kSecAttrKeyType : kSecAttrKeyTypeEC,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs : privateKeyAttr,
kSecPublicKeyAttrs : publicKeyAttr] as NSDictionary
let err = SecKeyGeneratePair(keyPairAttr, &publicKey, &privateKey)
return err == noErr
}
Sign data:
func signData(plainText: Data) -> NSData? {
guard privateKey != nil else {
print("Private key unavailable")
return nil
}
let digestToSign = self.sha1DigestForData(data: plainText as NSData) as Data
let signature = UnsafeMutablePointer<UInt8>.allocate(capacity: 128)
var signatureLength = 128
let err = SecKeyRawSign(privateKey!,
.PKCS1SHA1,
[UInt8](digestToSign),
Int(CC_SHA1_DIGEST_LENGTH),
signature,
&signatureLength)
print("Signature status: \(err)")
let sigData = NSData(bytes: signature, length: Int(signatureLength))
return sigData
}
func sha1DigestForData(data: NSData) -> NSData {
let len = Int(CC_SHA1_DIGEST_LENGTH)
let digest = UnsafeMutablePointer<UInt8>.allocate(capacity: len)
CC_SHA1(data.bytes, CC_LONG(data.length), digest)
return NSData(bytesNoCopy: UnsafeMutableRawPointer(digest), length: len)
}
Verify signature:
func verifySignature(plainText: Data, signature: NSData) -> Bool {
guard publicKey != nil else {
print("Public key unavailable")
return false
}
let digestToSign = self.sha1DigestForData(data: plainText as NSData) as Data
let signedHashBytesSize = signature.length
let err = SecKeyRawVerify(publicKey!,
.PKCS1SHA1,
[UInt8](digestToSign),
Int(CC_SHA1_DIGEST_LENGTH),
[UInt8](signature as Data),
signedHashBytesSize)
print("Verification status: \(err)")
return err == noErr
}
If you need to export the public key so that it can be used by another application or device, this can be done like this:
let parameters = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecAttrLabel as String: "Public Key",
kSecAttrIsPermanent as String: false,
kSecValueRef as String: publicKey,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecReturnData as String: true
] as CFDictionary
var data:AnyObject?
let status = SecItemAdd(parameters, &data)
print("Public key added \(status)")
if let keyData = data as? NSData {
print("This is the key, send it where it needs to go:\n\(keyData)")
}
ECDSA, unlike RSA, does not need hashed data prior to signing.
Apple released improved API in iOS 10 to address the problems of working with and calculating the size of raw data and returned generic error codes like -1. The newer ones, SecKeyCreateSignature in place of SecKeyRawSign, return data and error objects and replaced EC legacy constants for clarity. Here's an updated example:
- (NSData *) signData:(NSData *)dataToSign withPrivateKey:(SecKeyRef)privateKey {
NSData *signedData = nil;
if (dataToSign && privateKey && SecKeyCreateSignature != NULL) //Also check for iOS 10 +
{
CFErrorRef error = NULL;
CFDataRef signatureData = SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA512, (__bridge CFDataRef)dataToSign, &error);
if (signatureData)
{
if (error)
{
CFShow(error); // <-- here you get way more info than "-1"
CFRelease(signatureData);
}
else
{
signedData = (__bridge NSData *)CFAutorelease(signatureData);
}
}
if (error)
{
CFRelease(error);
}
}
return signedData;
}
iOS is quite fussy about the parameters for EC in regards to the older functions. Passing in the "wrong key" can give you either -1 or -50, especially as the engineers only focused EC support on newer APIs that use the secure enclave. Here's an updated example for key generation that generates compatible keys:
if (SecKeyCreateRandomKey != NULL && !(TARGET_IPHONE_SIMULATOR)) //iOS 10 + check, real device
{
CFErrorRef error = NULL;
SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, kSecAccessControlPrivateKeyUsage, &error);
if (error)
{
CFShow(error); // <- error instead of OSStatus
CFRelease(error);
error = NULL;
}
if (accessControl)
{
static const uint8_t identifier[] = "com.company.yourKey";
CFDataRef privateTag = CFDataCreate(kCFAllocatorDefault, identifier, sizeof(identifier));
if (privateTag)
{
const void* accessKeys[] = { kSecAttrIsPermanent, kSecAttrApplicationTag, kSecAttrAccessControl };
const void* accessValues[] = { kCFBooleanTrue, privateTag, accessControl };
CFDictionaryRef accessDictionary = CFDictionaryCreate(kCFAllocatorDefault, accessKeys, accessValues, 3, NULL, NULL);
if (accessDictionary)
{
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 7, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (parameters)
{
SInt32 keySize = 256;
CFNumberRef keySizeNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize);
if (keySizeNumber)
{
CFDictionaryAddValue(parameters, kSecAttrKeySizeInBits, keySizeNumber);
CFRelease(keySizeNumber);
}
CFDictionaryAddValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
CFDictionaryAddValue(parameters, kSecAttrTokenID, kSecAttrTokenIDSecureEnclave);
CFDictionaryAddValue(parameters, kSecPrivateKeyAttrs, accessDictionary);
SecKeyRef privateKey = SecKeyCreateRandomKey(parameters, &error); // <- pass in an error object
if (privateKey)
{
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
if (publicKey)
{
//...
CFRelease(publicKey);
}
//...
CFRelease(privateKey);
}
if (error)
{
CFRelease(error);
}
CFRelease(parameters);
}
CFRelease(accessDictionary);
}
CFRelease(privateTag);
}
CFRelease(accessControl);
}
}

iOS KeyChain secItemAdd crash

I'm trying to work with KeyChain and without a wrapper. But my code crashes when I want to read the value.
Code:
CFDictionaryRef attributes = CFDictionaryCreate(NULL, keys, values, 5, NULL, NULL);
CFDataRef result;
OSStatus status = SecItemAdd(attributes, (CFTypeRef *)&result);
if (status == errSecSuccess) {
if (result && CFGetTypeID(result) == CFDataGetTypeID()) { //crashes here
NSLog(#"Data");
}
isSuccess = YES;
} else {
fprintf(stderr, "Error while inserting into keychain osstatus:%ld\n", status);
}
Error: EXC_BAD_ACCESS
What am I doing wrong? I thought SecItemAdd can return the newly add item
Edit:
const void *keys[] = {
kSecClass
, kSecAttrAccessible
, kSecAttrService
, kSecAttrAccount
, kSecValueData
};
const void *values[] = {
kSecClassGenericPassword
, kSecAttrAccessibleWhenUnlocked
, (__bridge CFStringRef)service
, (__bridge CFStringRef)account
, data //CFDataRef
};
From documentation:
To obtain the data of the added item as an object of type CFDataRef,
specify the return type key kSecReturnData with a value of
kCFBooleanTrue.
The answer to why you're getting EXC_BAD_ACCESS, is because the dictionary that you pass into SecItemAdd has to be mutable.
Try something like this:
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(NULL, size,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(attributes, keys[x], values[x]);

Reading an Encrypted data from a txt file and decrypting it in ios

I Using AES128 Encryption and Decryption Technique for reading an encrypted text from a Text File
and Decrypting but i am unable to decrypt it.
The Data in Text file is Encrypted using AES128 in C#
I am using the following code to decrypt it
Kindly help i am new to Encryption and Decryption in AES12*
- (NSData *)doCipher:(NSData *)plainText key:(NSData *)aSymmetricKey
context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7
{
CCCryptorStatus ccStatus = kCCSuccess;
// Symmetric crypto reference.
CCCryptorRef thisEncipher = NULL;
// Cipher Text container.
NSData * cipherOrPlainText = nil;
// Pointer to output buffer.
uint8_t * bufferPtr = NULL;
// Total size of the buffer.
size_t bufferPtrSize = 0;
// Remaining bytes to be performed on.
size_t remainingBytes = 0;
// Number of bytes moved to buffer.
size_t movedBytes = 0;
// Length of plainText buffer.
size_t plainTextBufferSize = 0;
// Placeholder for total written.
size_t totalBytesWritten = 0;
// A friendly helper pointer.
uint8_t * ptr;
// Initialization vector; dummy in this case 0's.
uint8_t iv[kChosenCipherBlockSize];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
NSLog(#"doCipher: plaintext: %#", plainText);
NSLog(#"doCipher: key length: %d", [aSymmetricKey length]);
//LOGGING_FACILITY(plainText != nil, #"PlainText object cannot be nil." );
//LOGGING_FACILITY(aSymmetricKey != nil, #"Symmetric key object cannot be nil." );
//LOGGING_FACILITY(pkcs7 != NULL, #"CCOptions * pkcs7 cannot be NULL." );
//LOGGING_FACILITY([aSymmetricKey length] == kChosenCipherKeySize, #"Disjoint choices for key size." );
plainTextBufferSize = [plainText length];//+kCCBlockSizeAES128;
//LOGGING_FACILITY(plainTextBufferSize > 0, #"Empty plaintext passed in." );
NSLog(#"pkcs7: %d", *pkcs7);
// We don't want to toss padding on if we don't need to
if(encryptOrDecrypt == kCCEncrypt)
{
if(*pkcs7 != kCCOptionECBMode)
{
if((plainTextBufferSize % kChosenCipherBlockSize) == 0)
{
*pkcs7 = 0x0000;
}
else
{
*pkcs7 = kCCOptionPKCS7Padding;
}
}
}
else if(encryptOrDecrypt != kCCDecrypt)
{
NSLog(#"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7 );
}
// Create and Initialize the crypto reference.
ccStatus = CCCryptorCreate(encryptOrDecrypt,
kCCAlgorithmAES128,
*pkcs7,
(const void *)[aSymmetricKey bytes],
kChosenCipherKeySize,
(const void *)iv,
&thisEncipher
);
//LOGGING_FACILITY1( ccStatus == kCCSuccess, #"Problem creating the context, ccStatus == %d.", ccStatus );
// Calculate byte block alignment for all calls through to and including final.
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);
// Allocate buffer.
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t) );
// Zero out buffer.
memset((void *)bufferPtr, 0x0, bufferPtrSize);
// Initialize some necessary book keeping.
ptr = bufferPtr;
// Set up initial size.
remainingBytes = bufferPtrSize;
// Actually perform the encryption or decryption.
ccStatus = CCCryptorUpdate(thisEncipher,
(const void *) [plainText bytes],
plainTextBufferSize,
ptr,
remainingBytes,
&movedBytes
);
//LOGGING_FACILITY1( ccStatus == kCCSuccess, #"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus );
// Handle book keeping.
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
/* From CommonCryptor.h:
#enum CCCryptorStatus
#abstract Return values from CommonCryptor operations.
#constant kCCSuccess Operation completed normally.
#constant kCCParamError Illegal parameter value.
#constant kCCBufferTooSmall Insufficent buffer provided for specified operation.
#constant kCCMemoryFailure Memory allocation failure.
#constant kCCAlignmentError Input size was not aligned properly.
#constant kCCDecodeError Input data did not decode or decrypt properly.
#constant kCCUnimplemented Function not implemented for the current algorithm.
enum {
kCCSuccess = 0,
kCCParamError = -4300,
kCCBufferTooSmall = -4301,
kCCMemoryFailure = -4302,
kCCAlignmentError = -4303,
kCCDecodeError = -4304,
kCCUnimplemented = -4305
};
typedef int32_t CCCryptorStatus;
*/
// Finalize everything to the output buffer.
ccStatus = CCCryptorFinal(thisEncipher,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if(thisEncipher) {
(void) CCCryptorRelease(thisEncipher);
thisEncipher = NULL;
}
//LOGGING_FACILITY1( ccStatus == kCCSuccess, #"Problem with encipherment ccStatus == %d", ccStatus );
if (ccStatus == kCCSuccess)
cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
else
cipherOrPlainText = nil;
if(bufferPtr) free(bufferPtr);
NSString *string = [[NSString alloc] initWithData:cipherOrPlainText encoding:NSUTF8StringEncoding];
return cipherOrPlainText;
/*
Or the corresponding one-shot call:
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
typeOfSymmetricOpts,
(const void *)[self getSymmetricKeyBytes],
kChosenCipherKeySize,
iv,
(const void *) [plainText bytes],
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes
);
*/
}

Accessing certificates pushed by MDM

I wanted to know is there a way to access certificate pushed by MDM server through your app?
A few years later - is there still no way to access the MDM issued X.509 certificates?
I'm using this code, but getting zero results. And as far as google helps, there is also no way to see if there is any cert at all?
CFTypeRef certificateRef = NULL; // 1
const char *certLabelString = "XenMobile MDM";
CFStringRef certLabel = CFStringCreateWithCString(
NULL, certLabelString,
kCFStringEncodingUTF8);
const void *keys[] = { kSecClass, kSecAttrLabel, kSecReturnRef };
const void *values[] = { kSecClassCertificate, certLabel, kCFBooleanTrue };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys,
values, 3,
NULL, NULL);
status = SecItemCopyMatching(dict, &certificateRef);
if (status == errSecItemNotFound) {
_UILabelINFO.text = #"error The item cannot be found (errSecItemNotFound) :";
_UILabelINFO.text = [_UILabelINFO.text stringByAppendingString:(__bridge NSString *)(certLabel)];
} else {
_UILabelINFO.text = #"retrieved keychain reference";
}

Resources