我正在尝试将公共和私有 RSA 密钥添加到 iPhone 的钥匙串,所以我可以使用 CommonCrypto 库,但我不完全确定如何做到这一点。MYCrypto 库目前似乎只适用于 Mac 而不是 iPhone。任何人都可以帮助并解释如何将私钥/公钥添加到钥匙串并获得 SecKeyRef 给他们吗?
4 回答
所以在iOS中,钥匙串是沙盒的,AFAIK。这意味着您放入钥匙串的任何内容都只能由您的应用程序和您的应用程序访问,除非您另有说明。您必须在项目设置的功能下启用钥匙串共享。
现在已经不碍事了,您当然可以导入数据。由于它们是NSString
对象,因此您首先必须将其转换为NSData
对象才能正确导入它们。最有可能的是,它们是用 Base64 编码的,所以你必须反过来:
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
现在已经完成了,您可以使用此方法将您的密钥保存到钥匙串并获取 SecKeyRef:
/**
* key: the data you're importing
* keySize: the length of the key (512, 1024, 2048)
* isPrivate: is this a private key or public key?
*/
- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *saveDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecValueData : key,
(__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
};
SecKeyRef savedKeyRef = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKeyRef);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKeyRef;
}
稍后,如果您想从钥匙串中检索 SecKeyRef,您可以使用以下命令:
- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
if (privateKeyRef != NULL) {
// already exists in memory, return
return privateKeyRef;
}
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
if (publicKeyRef != NULL) {
// already exists in memory, return
return publicKeyRef;
}
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *queryDict = @{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
SecKeyRef keyReference = NULL;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
if (sanityCheck != errSecSuccess) {
NSLog(@"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
}
if (isPrivate) {
privateKeyRef = keyReference;
}
else {
publicKeyRef = keyReference;
}
return keyReference;
}
关于 privateTag 和 publicTag
privateTag
并publicTag
用于标记kSecAttrApplicationTag
定义使用此密钥的应用程序的。您希望有一个单独的privateTag
并publicTag
区分您的私钥和公钥。
这有点令人费解,因为我遵循了示例代码,但是我以这种方式定义了我的privateTag
和publicTag
:
SecKeyWrapper.h
#define kPublicKeyTag "com.sample.app.publickey"
#define kPrivateKeyTag "com.sample.app.privatekey"
SecKeyWrapper.m
// just under @implementation or @synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;
- (id)init {
if (self = [super init]) {
// Tag data to search for keys.
privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
}
return self;
}
然后像我在上面提供的代码示例中那样使用privateTag
and 。publicTag
你是在 iPhone 上生成它们吗?如果是这样,此消息以及证书、密钥和信任服务编程指南中的示例代码应该会引导您朝着正确的方向前进。如果没有,我正在编写一些应该实现这一点的代码——它还没有完成。
适用于我将公钥添加到 iOS 钥匙串中的代码如下:
- (NSData *)stripPublicKeyHeader:(NSData *)d_key
{
// Skip ASN.1 public key header
if (d_key == nil) return(nil);
unsigned int len = [d_key length];
if (!len) return(nil);
unsigned char *c_key = (unsigned char *)[d_key bytes];
unsigned int idx = 0;
if (c_key[idx++] != 0x30) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
// PKCS #1 rsaEncryption szOID_RSA_RSA
static unsigned char seqiod[] =
{ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00 };
if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
idx += 15;
if (c_key[idx++] != 0x03) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
if (c_key[idx++] != '\0') return(nil);
// Now make a new NSData from this buffer
return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}
- (BOOL)addPublicKey:(NSString *)key withTag:(NSString *)tag
{
NSString *startPublicKey = @"-----BEGIN PUBLIC KEY-----";
NSString *endPublicKey = @"-----END PUBLIC KEY-----";
NSString *s_key = NULL;
NSScanner *scanner = [NSScanner scannerWithString:key];
[scanner scanUpToString:startPublicKey intoString:nil];
[scanner scanString:startPublicKey intoString:nil];
[scanner scanUpToString:endPublicKey intoString:&s_key];
// This will be base64 encoded, decode it.
NSData *d_key = [RSAKeyHandler base64DataFromString:s_key];
d_key = [self stripPublicKeyHeader:d_key];
if (d_key == nil) return(FALSE);
NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
// Delete any old lingering key with the same tag
NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
[publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
SecItemDelete((__bridge CFDictionaryRef)publicKey);
CFTypeRef persistKey = nil;
// Add persistent version of the key to system keychain
[publicKey setObject:d_key forKey:(__bridge id)kSecValueData];
[publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)
kSecAttrKeyClass];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
kSecReturnPersistentRef];
OSStatus secStatus = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
if (persistKey != nil) CFRelease(persistKey);
if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) {
NSLog(@"Release public key");
return(FALSE);
}
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
[publicKey removeObjectForKey:(__bridge id)kSecValueData];
[publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef
];
[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
secStatus = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey,
(CFTypeRef *)&keyRef);
//[publicKey release];
if (keyRef == nil) return(FALSE);
// Add to our pseudo keychain
//[keyRefs addObject:[NSValue valueWithBytes:&keyRef objCType:@encode(
// SecKeyRef)]];
return(TRUE);
}
这似乎是这个 SO question的副本。
MyCrypto 说它可以在 iPhone 上运行。你看到什么样的问题?