use*_*505 10 security encryption objective-c pem ios
我是Objective C&iOS编程的新手.
我正在使用使用openssl生成的简单公钥/私钥(PEM格式)来加密和解密需要在服务器和客户端之间交换的数据.我在Java Server&Client中成功地使用了它.
当我使用Java中的公钥加密数据并使用Objective C/iOS中的私钥进行解密时,问题就出现了.我查看了一些示例并将一些代码放在一起,但是当我从私钥创建SecKeyRef时始终调用SecItemCopyMatching时,我收到错误-25300.
顺便说一句,这里没有涉及证书,它只是简单的密钥.这是我正在做的事情:
我的问题是步骤#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);
}
Run Code Online (Sandbox Code Playgroud)
我已经打破了几天了,我觉得我需要在这里得到一些帮助.任何一个指针?我可以花更多的时间获得iOS提供的加密域知识和支持,但我根本不做任何iOS编程,这是一次性的事情.
我只是需要一些方向,我可以努力使其发挥作用.
TIA.
不幸的是,iOS上的安全框架要求私钥采用PKCS12格式,并带有密码.公钥可以是X509装甲DER或PKCS12,但私钥必须是PKCS12.您尝试使用的私钥是PEM格式的RSA密钥.
如果您有权访问该密钥,则可以使用openssl命令行工具进行转换:
openssl pkcs12 -export -nocerts -inkey privatekey.pem -out privatekey.p12
这将使用私钥创建PKCS12文件,并需要密码.如果您无法控制私钥(例如,如果它来自外部源,如服务器),那么您就不幸了.
但是我们假设您能够执行上述步骤将该烦人的PEM RSA私钥转换为PKCS12.从PKCS12数据中提取私钥并不太难:
NSData.dataWithContentsOfURL:如果这是文件系统上的资源,则可以使用此方法.SecPKCS12Import密码短语导入PKCS12数据.SecIdentityRef从导入的项目中提取.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;
}
Run Code Online (Sandbox Code Playgroud)
从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
}
Run Code Online (Sandbox Code Playgroud)
假设"data"是NSData包含PKCS12数据的NSString实例,"passphrase"是表示密码的实例.成功时,将使用从PKCS12数据导入的私钥填充"privateKey".
您已将私钥和证书存储在钥匙串中。否则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);
Run Code Online (Sandbox Code Playgroud)
然后您可以稍后使用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);
Run Code Online (Sandbox Code Playgroud)
始终检查 OSStatus 错误,因为您肯定会遇到errSecDuplicateItem.
请务必阅读 Apple 的证书、密钥和信任服务参考。