2013-10-31 79 views
10

我是新来的Objective C & iOS编程。Objective C:无法从PEM私钥获取SecKeyRef

我正在使用使用openssl生成的简单的公钥/私钥(PEM格式)来加密和解密需要在服务器和客户端之间交换的数据。我在Java服务器&客户端成功运行。

当我使用Java中的公钥加密数据并使用Objective C/iOS中的私钥解密时,麻烦就开始了。我查了几个例子,并将一些代码放在一起,但是当我通过私钥调用SecKeyRef的一部分时,始终调用SecItemCopyMatching时出现错误-25300。

顺便说一句,这里没有涉及的证书,它只是普通的密钥。 以下是我在做什么:

  1. 阅读PEM私钥和Base64解码。
  2. 使用SecItemCopyMatching从已解码的字符串中生成一个SecKeyRef。
  3. 使用SecKeyDecrypt解密。

我的问题是步骤#2返回-25300状态(errSecItemNotFound -25300
的项目不能被发现。可用在IOS 2.0和更高。)

这是我的用于产生代码SecKeyRef:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
NSString *challenge = @"2KFqc46DNSWrizzv69lJN25o62xEYQw/QLcMiT2V1XLER9uJbOu+xH2qgTuNWa1HZ9SW3Lq+HovtkhFmjmf08QkVQohHmxCJXVyCgVhPBleScAgQ8AoP3tmV0RqGb2mJrb19ybeYP7uZ2piVtF4cRwU1gO3VTooCUK3cX4wS7Tc="; 
NSLog(@"challenge, %@", challenge); 

NSData *incomingData = [self base64DataFromString:challenge]; 
uint8_t *challengeBuffer = (uint8_t*)[incomingData bytes]; 
NSLog(@"challengeBuffer: %s", challengeBuffer); 

[self decryptWithPrivateKey:challengeBuffer]; 

free(challengeBuffer); 

return YES; 
} 

// Generate a SecKeyRef from the private key in the private.pem file. 
- (SecKeyRef)getPrivateKeyRef { 
NSString *startPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----"; 
NSString *endPrivateKey = @"-----END RSA PRIVATE KEY-----"; 
NSString* path = [[NSBundle mainBundle] pathForResource:@"private" 
               ofType:@"pem"]; 
NSString* content = [NSString stringWithContentsOfFile:path 
               encoding:NSUTF8StringEncoding 
               error:NULL]; 
NSLog(@"Private Key: %@", content); 

NSString *privateKey; 
NSScanner *scanner = [NSScanner scannerWithString:content]; 
[scanner scanUpToString:startPrivateKey intoString:nil]; 
[scanner scanString:startPrivateKey intoString:nil]; 
[scanner scanUpToString:endPrivateKey intoString:&privateKey]; 

NSData *privateTag = [self dataWithBase64EncodedString:privateKey]; 
NSLog(@"Decoded String: %@", privateTag); 

OSStatus status = noErr; 
SecKeyRef privateKeyReference = NULL; 

NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; 

// Set the private key query dictionary. 
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass]; 
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; 
[queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag]; 
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef]; 
//[queryPrivateKey setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef]; 


// Get the key. 
status = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference); 
NSLog(@"status: %ld", status); 

if(status != noErr) 
{ 
    privateKeyReference = NULL; 
} 

return privateKeyReference; 
} 

// Decrypt data 
- (void)decryptWithPrivateKey:(uint8_t *)cipherBuffer { 
OSStatus status = noErr; 

SecKeyRef privateKeyRef = [self getPrivateKeyRef]; 

size_t plainBufferSize = SecKeyGetBlockSize(privateKeyRef); 
uint8_t *plainBuffer = malloc(plainBufferSize); 

size_t cipherBufferSize = strlen((char *)cipherBuffer); 
NSLog(@"decryptWithPrivateKey: length of input: %lu", cipherBufferSize); 

// Error handling 
status = SecKeyDecrypt(privateKeyRef, 
         PADDING, 
         cipherBuffer, 
         cipherBufferSize, 
         &plainBuffer[0], 
         &plainBufferSize 
         ); 
NSLog(@"decryption result code: %ld (size: %lu)", status, plainBufferSize); 
NSLog(@"FINAL decrypted text: %s", plainBuffer); 
} 

现在我已经打破我的头几天,我觉得我需要在这里得到一些帮助。任何一个任何指针?我可以花更多时间获得iOS提供的Crypto领域知识和支持,但我根本不做任何iOS编程,这是一次性的。

我只是需要一些方向,我可以努力使其工作。

TIA。

+0

你有没有得到这个工作?同样的问题。 –

+0

我仍然面对同样的情况。你有这个工作吗? –

+0

您只能使用'SecItemCopyMatching'来检索以前添加到钥匙串的项目。由于您没有添加任何内容,因此无法检索。 – orkoden

回答

0

您已将私钥和证书存储在钥匙串中。否则SecItemCopyMatching将不会做任何事情。你只需要导入一次。

/* importing client identity (private key) */ 
NSData* certificateData = ... ; // decoded pkcs21 certificate from base64 pem 
NSString* passcode = @"passphrased used to encrypt the private key"; 
CFDictionaryRef optionsDictionary = (__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: passcode, kSecImportExportPassphrase, nil]; 
CFArrayRef certificates; 
OSStatus error = SecPKCS12Import((__bridge CFDataRef) certificateData, optionsDictionary, &certificates); 
CFDictionaryRef myIDs = CFArrayGetValueAtIndex(certificates, 0); 

SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(myIDs, kSecImportItemIdentity); 

NSDictionary* clientCertificateQuery = @{(__bridge id)kSecValueRef  : identity, 
             (__bridge id)kSecAttrLabel  : @"some label you can use to find the item again with SecItemCopyMatching"}; 
OSStatus err = SecItemAdd((__bridge CFDictionaryRef) clientCertificateQuery, NULL); 

然后你就可以在以后使用SecItemCopyMatching来获得身份和SecIdentityCopyPrivateKey得到私钥。

NSDictionary* clientCertificateQuery = @{(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll, 
             (__bridge id)kSecClass  : (__bridge id)kSecClassIdentity, 
             (__bridge id)kSecReturnRef : (__bridge id)kCFBooleanTrue}; 
SecIdentityRef identity = NULL; 
OSStatus errorCode = SecItemCopyMatching((__bridge CFDictionaryRef) clientCertificateQuery, &identity); 
SecKeyRef privateKeyRef; 
OSStatus err = SecIdentityCopyPrivateKey (identity, &privateKeyRef); 

经常检查OSStatus错误,你肯定会碰到errSecDuplicateItem

请务必阅读Apple的Certificate, Key, and Trust Services Reference

+0

嗨@orkoden。谢谢你的答案,但我得到了字符串格式的键。字符串使用base64进行编码,然后使用以下格式:@“ - BEGIN RSA PRIVATE KEY ----- MIICXAIBAAKBgQC0HPYiPItBtjJNky ...- - END RSA PRIVATE KEY--”;我将如何从这些数据生成pkcs21数据? –

2

我在使用java服务器和iPhone应用程序时遇到了同样的问题,我的工作如下。

  1. 在java服务器上生成p12。 [记得记下密码。]
  2. 将p12文件的原始字节转换为基本64字符串。
  3. 无论您想如何发送这些数据到iOS应用程序。

    3.1您可以将base 64放入文本文件并发送给iOS。 [最安全的方式,在我的情况下工作正常。]

    3.2您可以使用JSON字符串发送该字符串。 [这可能会破坏你的数据。]

  4. 一旦你获得了iPhone应用程序中的数据,将base 64字符串转换为NSData。 NSData+Base64
  5. 使用以下方法获取您的私钥的SecKeyRef。

    - (SecKeyRef)getPrivateKeyFromData:(NSData *)p12Data withPassword:(NSString *)password { 
        NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; 
        SecKeyRef privateKey = NULL; 
        [options setObject:password forKey:(__bridge id)kSecImportExportPassphrase]; 
        CFArrayRef items = NULL;// = CFArrayCreate(NULL, 0, 0, NULL); 
        OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)p12Data, 
                  (__bridge CFDictionaryRef)options, &items); 
        if (securityError == noErr && CFArrayGetCount(items) > 0) { 
         CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); 
         SecIdentityRef identityApp = 
         (SecIdentityRef)CFDictionaryGetValue(identityDict, 
                 kSecImportItemIdentity); 
         securityError = SecIdentityCopyPrivateKey(identityApp, &privateKey); 
        if (securityError != noErr) { 
          privateKey = NULL; 
         } 
        } 
        //NSLog(@"-------------------- Private Key Error %d",(int)securityError); 
        CFRelease(items); 
        options = nil; 
        p12Data = nil; 
        password = nil; 
        return privateKey; 
    } 
    

希望这有助于!!!!!

+0

感谢@Pratik的回答,但我得到了字符串格式的键。字符串使用base64进行编码,然后使用以下格式:@“ - BEGIN RSA PRIVATE KEY ----- MIICXAIBAAKBgQC0HPYiPItBtjJNky ...- - END RSA PRIVATE KEY--”;我没有p12和密码。 –

+0

+1 @Pratik。我试着用示例.p12文件和它的工作正常你的代码。 –

+0

@SaurabhShukla我很乐意帮助别人。对不起,我无法回答先前的问题。 –

6

不幸的是,iOS上的安全框架要求私钥采用PKCS12格式,并带有密码。公钥可以在X509铠装DER或PKCS12中,但私钥需要PKCS12。您尝试使用的私钥是PEM格式的RSA密钥。

如果你有访问密钥可以使用openssl command line tools转换:

openssl pkcs12 -export -nocerts -inkey privatekey.pem -out privatekey.p12

这将创建一个私有密钥的PKCS12文件,而且需要密码。如果您无法控制私钥(例如,如果它来自外部源(例如服务器)),那么您运气不好。

但是让我们假设您可以执行上述步骤,将令人生厌的PEM RSA私钥转换为PKCS12。 提取从PKCS12数据私钥是不是太困难:

  1. 装入PKCS12为NSData。如果这是文件系统上的资源,则可以使用dataWithContentsOfURL:执行此操作。
  2. 使用SecPKCS12Import可以使用密码导入PKCS12数据。
  3. 从导入的项目中提取SecIdentityRef
  4. 复制从SecIdentityRef

私钥这样做的功能是:

OSStatus SecKeyPrivatePKCS12Import(CFDataRef keyData, CFStringRef passphrase, SecKeyRef *privateKey){ 
    OSStatus  status    = errSecSuccess; 
    CFDictionaryRef secImportOptions = NULL; 
    CFArrayRef  secImportItems  = NULL; 

    if ((keyData != NULL) && (CFStringGetLength(passphrase) > 0)){ 
     const void *keys[] = { kSecImportExportPassphrase }; 
     const void *values[] = { passphrase }; 

     secImportOptions = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); 

     status = SecPKCS12Import((CFDataRef) keyData, (CFDictionaryRef)secImportOptions, &secImportItems); 
     if (CFArrayGetCount(secImportItems) > 0){ 
      CFDictionaryRef identityDict = CFArrayGetValueAtIndex(secImportItems, 0); 
      SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); 
      SecIdentityCopyPrivateKey(identityApp, privateKey); 
     } 
    } 

    return status; 
} 

从Objective-C的调用它看起来像:

OSStatus status = errSecSuccess; 

status = SecKeyPrivatePKCS12Import((_bridge CFDataRef)data, (_bridge CFStringRef)passphrase, &privateKey); 
if (privateKey == NULL){ 
    // Check the status value for why it failed 
} 

假设“数据”是包含PKCS12数据的NSData的实例,“密码”是NSString实例代表吟诵密码。成功时,使用从PKCS12数据导入的私钥填充“privateKey”。

相关问题