Skip to content

Commit 8802c2d

Browse files
committed
Add more methods to handle RSA keys.
- Check if a RSA key exists - Get a RSA Key as `SecKeyRef`.
1 parent 2c3a28b commit 8802c2d

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Example/Tests/A0SimpleKeychainSpec.m

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
#import "Specta.h"
2424
#import "A0SimpleKeychain.h"
25+
#import "A0SimpleKeychain+KeyPair.h"
2526

27+
#define kPublicKeyTag @"public"
28+
#define kPrivateKeyTag @"private"
2629

2730
SpecBegin(A0SimpleKeychain)
2831

@@ -206,6 +209,94 @@
206209
expect([keychain dataForKey:key]).notTo.beNil();
207210
});
208211
});
212+
213+
describe(@"generate key pair", ^{
214+
215+
beforeEach(^{
216+
keychain = [A0SimpleKeychain keychain];
217+
});
218+
219+
afterEach(^{
220+
[keychain deleteRSAKeyWithTag:kPublicKeyTag];
221+
[keychain deleteRSAKeyWithTag:kPrivateKeyTag];
222+
});
223+
224+
it(@"should generate a key pair", ^{
225+
[keychain generateRSAKeyPairWithLength:A0SimpleKeychainRSAKeySize1024Bits
226+
publicKeyTag:kPublicKeyTag
227+
privateKeyTag:kPrivateKeyTag];
228+
expect([keychain dataForRSAKeyWithTag:kPublicKeyTag]).notTo.beNil();
229+
expect([keychain dataForRSAKeyWithTag:kPrivateKeyTag]).notTo.beNil();
230+
});
231+
232+
sharedExamplesFor(@"failed RSA key generation", ^(NSDictionary *data) {
233+
234+
it(@"should fail with NSAssert", ^{
235+
expect(^{
236+
[keychain generateRSAKeyPairWithLength:A0SimpleKeychainRSAKeySize1024Bits
237+
publicKeyTag:data[@"publicTag"]
238+
privateKeyTag:data[@"privateTag"]];
239+
}).to.raise(NSInternalInconsistencyException);
240+
});
241+
242+
});
243+
244+
itShouldBehaveLike(@"failed RSA key generation", @{
245+
@"publicTag": kPublicKeyTag,
246+
});
247+
248+
itShouldBehaveLike(@"failed RSA key generation", @{
249+
@"privateTag": kPrivateKeyTag,
250+
});
251+
252+
itShouldBehaveLike(@"failed RSA key generation", @{});
253+
});
254+
255+
describe(@"obtain RSA key as NSData", ^{
256+
257+
beforeEach(^{
258+
keychain = [A0SimpleKeychain keychain];
259+
[keychain generateRSAKeyPairWithLength:A0SimpleKeychainRSAKeySize1024Bits
260+
publicKeyTag:kPublicKeyTag
261+
privateKeyTag:kPrivateKeyTag];
262+
});
263+
264+
afterEach(^{
265+
[keychain deleteRSAKeyWithTag:kPublicKeyTag];
266+
[keychain deleteRSAKeyWithTag:kPrivateKeyTag];
267+
});
268+
269+
it(@"should obtain keys", ^{
270+
expect([keychain dataForRSAKeyWithTag:kPublicKeyTag]).notTo.beNil();
271+
expect([keychain dataForRSAKeyWithTag:kPrivateKeyTag]).notTo.beNil();
272+
});
273+
});
274+
275+
describe(@"check if RSA key exists", ^{
276+
277+
beforeEach(^{
278+
keychain = [A0SimpleKeychain keychain];
279+
[keychain generateRSAKeyPairWithLength:A0SimpleKeychainRSAKeySize1024Bits
280+
publicKeyTag:kPublicKeyTag
281+
privateKeyTag:kPrivateKeyTag];
282+
});
283+
284+
afterEach(^{
285+
[keychain deleteRSAKeyWithTag:kPublicKeyTag];
286+
[keychain deleteRSAKeyWithTag:kPrivateKeyTag];
287+
});
288+
289+
it(@"should check if the key exists", ^{
290+
expect([keychain hasRSAKeyWithTag:kPublicKeyTag]).to.beTruthy();
291+
expect([keychain hasRSAKeyWithTag:kPrivateKeyTag]).to.beTruthy();
292+
});
293+
294+
it(@"should return NO for nonexisting key", ^{
295+
expect([keychain hasRSAKeyWithTag:@"NONEXISTENT"]).to.beFalsy();
296+
});
297+
});
298+
299+
209300
});
210301

211302
SpecEnd

Pod/Classes/A0SimpleKeychain+KeyPair.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,24 @@ typedef NS_ENUM(NSUInteger, A0SimpleKeychainRSAKeySize) {
6565
*/
6666
- (BOOL)deleteRSAKeyWithTag:(NSString *)keyTag;
6767

68+
/**
69+
* Returns a RSA key as `SecKeyRef`. You must release it when you're done with it
70+
*
71+
* @param keyTag tag of the RSA Key
72+
*
73+
* @return SecKeyRef of RSA Key
74+
*/
75+
- (SecKeyRef)keyRefOfRSAKeyWithTag:(NSString *)keyTag;
76+
77+
/**
78+
* Checks if a RSA key exists with a given tag.
79+
*
80+
* @param keyTag tag of RSA Key
81+
*
82+
* @return if the key exists or not.
83+
*/
84+
- (BOOL)hasRSAKeyWithTag:(NSString *)keyTag;
85+
6886
@end
6987

7088
@interface A0SimpleKeychain (Deprecated)

Pod/Classes/A0SimpleKeychain+KeyPair.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,21 @@ - (NSData *)dataForRSAKeyWithTag:(NSString *)keyTag {
7979
return data;
8080
}
8181

82+
- (BOOL)hasRSAKeyWithTag:(NSString *)keyTag {
83+
NSAssert(keyTag.length > 0, @"key tag should be non-empty!");
84+
85+
NSDictionary *publicKeyQuery = @{
86+
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
87+
(__bridge id)kSecAttrApplicationTag: [keyTag dataUsingEncoding:NSUTF8StringEncoding],
88+
(__bridge id)kSecAttrType: (__bridge id)kSecAttrKeyTypeRSA,
89+
(__bridge id)kSecReturnData: @NO,
90+
};
91+
92+
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKeyQuery, NULL);
93+
return status == errSecSuccess;
94+
}
95+
96+
8297
- (BOOL)deleteRSAKeyWithTag:(NSString *)keyTag {
8398
NSAssert(keyTag.length > 0, @"key tag should be non-empty!");
8499
NSDictionary *deleteKeyQuery = @{
@@ -91,6 +106,22 @@ - (BOOL)deleteRSAKeyWithTag:(NSString *)keyTag {
91106
return status == errSecSuccess;
92107
}
93108

109+
- (SecKeyRef)keyRefOfRSAKeyWithTag:(NSString *)keyTag {
110+
NSAssert(keyTag.length > 0, @"key tag should be non-empty!");
111+
NSDictionary *query = @{
112+
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
113+
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA,
114+
(__bridge id)kSecReturnRef: @YES,
115+
(__bridge id)kSecAttrApplicationTag: keyTag,
116+
};
117+
SecKeyRef privateKeyRef = NULL;
118+
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKeyRef);
119+
if (status != errSecSuccess) {
120+
return NULL;
121+
}
122+
return privateKeyRef;
123+
}
124+
94125
@end
95126

96127
@implementation A0SimpleKeychain (Deprecated)

0 commit comments

Comments
 (0)