How to Get the Modulus or Exponent from a Seckeyref Object in Swift

How to find out the modulus and exponent of RSA Public Key on iPhone/Objective C

Busted my head on this, but here's a solution I've found (without using any external packages).

First, go to Apple's CryptoExercise example. Download the "SecKeyWrapper" class from there. The interesting function in that class is getPublicKeyBits.

Link to the example: http://developer.apple.com/library/ios/#samplecode/CryptoExercise/Introduction/Intro.html

The bits you will receive is a DER encoded (wiki it) public key containing both the modulus and exp. Here's a code that will decode it for you, pretty easy:

- (NSData *)getPublicKeyExp
{
NSData* pk = [self getPublicKeyBits];
if (pk == NULL) return NULL;

int iterator = 0;

iterator++; // TYPE - bit stream - mod + exp
[self derEncodingGetSizeFrom:pk at:&iterator]; // Total size

iterator++; // TYPE - bit stream mod
int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];
iterator += mod_size;

iterator++; // TYPE - bit stream exp
int exp_size = [self derEncodingGetSizeFrom:pk at:&iterator];

return [pk subdataWithRange:NSMakeRange(iterator, exp_size)];
}

- (NSData *)getPublicKeyMod
{
NSData* pk = [self getPublicKeyBits];
if (pk == NULL) return NULL;

int iterator = 0;

iterator++; // TYPE - bit stream - mod + exp
[self derEncodingGetSizeFrom:pk at:&iterator]; // Total size

iterator++; // TYPE - bit stream mod
int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];

return [pk subdataWithRange:NSMakeRange(iterator, mod_size)];
}

- (int)derEncodingGetSizeFrom:(NSData*)buf at:(int*)iterator
{
const uint8_t* data = [buf bytes];
int itr = *iterator;
int num_bytes = 1;
int ret = 0;

if (data[itr] > 0x80) {
num_bytes = data[itr] - 0x80;
itr++;
}

for (int i = 0 ; i < num_bytes; i++) ret = (ret * 0x100) + data[itr + i];

*iterator = itr + num_bytes;
return ret;
}

iOS: RSA Encrypt using public key (with modulus and exponent)

Wow, no wonder it's so hard to find an answer to this. I spent 2 days down the crypto-rabbit hole, and it's not pretty.

The easy way

Use Chilkat iOS RSA Library. One major downside: cost $189! :O

The hard way

Parse the XML, use SCZ-BasicEncodingRules-iOS to generate a public key data out of the modulus and exponent. If that works, create a dummy keychain using that public key (follow sample code here), extract the public key now in SecKeyRef format and pass it to the encryptRSA method in the question. Finally, cleanup, remove the dummy keychain. Sounds good in theory, but I have never tested this thoroughly, if you do, let me know!

Swift 3 export SecKey to String

Export Key (iOS 10 only)

var error:Unmanaged<CFError>?
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString()
}

See https://stackoverflow.com/a/30662270/5276890 and https://stackoverflow.com/a/27935528/5276890 for longer ways which probably support iOS < 10.

Reimport Key

guard let data2 = Data.init(base64Encoded: b64Key) else {
return
}

let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 512),
kSecReturnPersistentRef: true as NSObject
]

guard let publicKey = SecKeyCreateWithData(data2 as CFData, keyDict as CFDictionary, nil) else {
return
}

Note: This generates a base64 key and not a certificate. A lot of code samples online deal with how to generate a public key from a certificate using SecCertificateCreateWithData

Also: 512 bit is fast to generate but worthless. Pick a longer and secure value once you're satisfied with the results.

I got valid results back when importing the key I generated and exported, so I assume it works, but I did not try to encrypt and decrypt with it.

iOS SecKeyRef to NSData

  • use of undeclared identifier 'publicTag'

The publicTag is just some unique identifier added to the Keychain items. In the CryptoExercise sample project it is defined as

#define kPublicKeyTag "com.apple.sample.publickey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
  • Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC

This can be solved by using a temporary CFTypeRef variable:

CFTypeRef result;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
}
  • I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there ...

As far as I know, you have to store the SecKeyRef to the Keychain temporarily. SecItemAdd
has the option to return the added item as data. From the 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.

Putting all that together, the following code should do what you want:

- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {

static const uint8_t publicKeyIdentifier[] = "com.your.company.publickey";
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];

OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;

NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

// Temporarily add key to the Keychain, return as data:
NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
[attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
[attributes setObject:@YES forKey:(__bridge id)kSecReturnData];
CFTypeRef result;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);

// Remove from Keychain again:
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}

return publicKeyBits;
}

I hope that this works, I cannot test it at the moment.

  • Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.

I don't know.



Related Topics



Leave a reply



Submit