首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >目标C:无法从PEM私钥中获取SecKeyRef

目标C:无法从PEM私钥中获取SecKeyRef
EN

Stack Overflow用户
提问于 2013-10-31 02:47:49
回答 3查看 5.3K关注 0票数 11

我对目标C& iOS编程很陌生。

我使用使用openssl生成的简单公钥/私钥(PEM格式)来加密和解密需要在服务器和客户端之间交换的数据。我在Java & Client中成功地实现了这一点。

当我在Java中使用公钥加密数据,在目标C/ iOS中使用私钥解密时,问题就开始了。我已经查看了几个例子,并将一些代码放在一起,但是当我一直调用SecItemCopyMatching作为从私钥创建SecKeyRef的一部分时,我得到了一个错误- -25300。

顺便说一句,这里不涉及证书,它只是简单的密钥。以下是我正在做的事情:

  1. 读取PEM私钥和Base64解码。
  2. 使用SecKeyRef从解码后的字符串生成SecItemCopyMatching。
  3. 使用SecKeyDecrypt解密。

我的问题是步骤2,它返回状态为-25300 (errSecItemNotFound -25300)。

找不到该项目。)可以在iOS 2.0及更高版本中获得。)

下面是生成SecKeyRef的代码:

代码语言:javascript
复制
- (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提供的密码域知识和支持,但我根本不做任何iOS编程,这是一次性的事情。

我只是需要一些方向,我可以努力使它发挥作用。

蒂娅。

EN

回答 3

Stack Overflow用户

发布于 2014-09-11 20:31:08

不幸的是,iOS上的安全框架要求私钥采用PKCS12格式,并带有密码。公钥可以是X509装甲车或PKCS12,但是私钥必须是PKCS12。您试图使用的私钥是一个PEM格式的RSA密钥。

如果您可以访问该密钥,则可以使用openssl命令行工具转换该密钥。

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

这将创建一个带有私钥的PKCS12文件,并需要一个密码。如果您没有对私钥的控制(例如,如果它来自外部源(例如服务器)),那么您就倒霉了。

但让我们假设您能够完成上述步骤,将这个麻烦的PEM私钥转换为PKCS12。从PKCS12数据中提取私钥并不太困难:

  1. 将PKCS12加载为NSData。如果这是文件系统上的一个资源,则可以使用dataWithContentsOfURL:进行此操作。
  2. 使用SecPKCS12Import导入带有密码的PKCS12数据。
  3. 从导入的项中提取SecIdentityRef
  4. SecIdentityRef复制私钥

这样做的一项职能是:

代码语言:javascript
复制
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;
}

从目标C调用它看起来像:

代码语言:javascript
复制
OSStatus status = errSecSuccess;

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

假设" data“是包含NSData数据的PKCS12实例,而" passphrase”是表示密码的NSString实例。成功时,"privateKey“由从PKCS12数据导入的私钥填充。

票数 8
EN

Stack Overflow用户

发布于 2014-09-09 13:21:58

我在使用java服务器和iPhone应用程序时也面临着同样的问题,我的工作如下所示。

  1. 在java服务器上生成p12。记住记下密码。
  2. 将p12文件的原始字节转换为基本64字符串。
  3. 将这些数据发送到iOS应用程序,不管您想怎么做。 3.1您可以将基64放在文本文件中,并将其发送到iOS。最安全的方法,在我的案子里工作也很好。 3.2您可以使用JSON字符串发送该字符串。这可能会破坏你的数据。
  4. 获得iPhone应用程序上的数据后,将基64字符串转换为NSData。NSData+Base64
  5. 使用以下方法获取私钥的SecKeyRef。
  • ( SecKeyRef )getPrivateKeyFromData:(NSData *)p12Data withPassword:(NSString *)密码{ NSMutableDictionary *options = [NSMutableDictionary alloc init];SecKeyRef privateKey = NULL;options setObject:密码forKey:(__bridge id)kSecImportExportPassphrase;CFArrayRef items = NULL;// = CFArrayCreate(NULL,0,0,NULL);OSStatus securityError =OSStatus(KSecImportExportPassphrase),(en22#)选项,&items;if (securityError == noErr && CFArrayGetCount(items) > 0) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items,0);SecIdentityRef identityApp =SecIdentityRef kSecImportItemIdentity;securityError = SecIdentityCopyPrivateKey(identityApp,& privateKey );if (securityError != noErr) {privateKey= NULL;}//NSLog(@“-”-私钥错误%d",(int)securityError);CFRelease(项目);选项=零;p12Data =0;密码=0;返回privateKey};

希望这能帮上忙!

票数 2
EN

Stack Overflow用户

发布于 2014-09-05 15:55:47

您已经将私钥和证书存储在密钥链中。否则,SecItemCopyMatching什么也不会做。您只需导入一次。

代码语言:javascript
复制
/* 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获取私钥。

代码语言:javascript
复制
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

一定要阅读苹果的证书、密钥和信任服务引用

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19697629

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档