2013-10-31 6 views
10

Ich bin neu in Objective C & iOS-Programmierung.Ziel C: SecKeyRef kann nicht aus dem privaten PEM-Schlüssel abgerufen werden

Ich verwende einen einfachen öffentlichen/privaten Schlüssel (PEM-Format) generiert mit openssl zum Verschlüsseln und Entschlüsseln von Daten, die zwischen einem Server und einem Client ausgetauscht werden müssen. Ich habe das erfolgreich in Java Server & Client arbeiten.

Der Fehler begann, als ich Daten mit dem öffentlichen Schlüssel in Java verschlüsselt und mit dem privaten Schlüssel in Objective C/iOS entschlüsselt habe. Ich habe mich ein paar Beispiele angesehen und etwas Code zusammengefügt, aber ich erhalte den Fehler -25300, wenn ich die SecItemCopyMatching die ganze Zeit als Teil des Erstellens eines SecKeyRef vom privaten Schlüssel rufe.

BTW, hier sind keine Zertifikate beteiligt und es sind nur einfache Tasten. Hier ist, was ich tue:

  1. Lesen Sie den privaten PEM-Schlüssel und Base64 dekodieren.
  2. Generieren Sie einen SecKeyRef aus der dekodierten Zeichenfolge mit SecItemCopyMatching.
  3. Mit SecKeyDecrypt entschlüsseln.

Mein Problem ist, Schritt # 2, das einen Status von -25.300 zurückgibt (errSecItemNotFound -25.300
Das Element kann nicht gefunden werden. Erhältlich in iOS 2.0 und höher.)

Hier mein Code ist die zur Erzeugung von 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); 
} 

ich habe jetzt ein paar Tagen mein Kopf war zu brechen und ich fühlte mich wie ich hier etwas Hilfe bekommen müssen. Irgendwelche Zeiger? Ich könnte mehr Zeit damit verbringen, das Crypto-Domain-Wissen und die Unterstützung zu erwerben, die iOS bietet, aber ich mache überhaupt keine iOS-Programmierung und das ist eine einmalige Sache.

Ich brauche nur eine Richtung, und ich kann kämpfen, damit es funktioniert.

TIA.

+0

Haben Sie das jemals funktioniert? Gleicher Fehler. –

+0

Ich bin immer noch auf der gleichen Seite. Hast du das funktioniert? –

+0

Sie können "SecItemCopyMatching" nur verwenden, um Elemente abzurufen, die Sie zuvor dem Schlüsselbund hinzugefügt haben. Da du nichts hinzugefügt hast, gibt es nichts zu holen. – orkoden

Antwort

0

Sie haben Ihren privaten Schlüssel und das Zertifikat im Schlüsselbund gespeichert. Sonst wird SecItemCopyMatching nichts tun. Sie müssen das nur einmal importieren.

/* 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); 

Dann können Sie später SecItemCopyMatching verwenden, um die Identität zu erhalten und SecIdentityCopyPrivateKey den privaten Schlüssel zu erhalten.

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); 

Prüfen Sie stets die OSStatus Fehler, wie Sie auf jeden Fall in errSecDuplicateItem laufen wird.

Achten Sie darauf, Apples Certificate, Key, and Trust Services Reference zu lesen.

+0

Hallo @orkoden. Danke für die Antwort, aber ich habe Schlüssel im String-Format.String wird mit base64 codiert und hat dann folgendes Format: @ "- BEGIN RSA PRIVATE KEY ----- MIICXAIBAAKBgQC0HPYiPItBtjJNky ...----- END RSA PRIVATE KEY--"; Wie erzeuge ich pkcs21 Daten aus diesen Daten? –

2

Ich habe das gleiche Problem konfrontiert, wenn ich mit Java-Server und iPhone-Anwendung arbeitete und meine Arbeit war wie folgt.

  1. Generiere p12 auf dem Java-Server. [Denken Sie daran, das Passwort notieren.]
  2. Konvertieren Sie rohe Bytes der p12-Datei in Basis 64 Zeichenfolge.
  3. Senden Sie diese Daten an iOS-Anwendung, egal wie Sie wollen.

    3.1 Sie können Base 64 in eine Textdatei einfügen und diese an iOS senden. [Sicherste Art und funktioniert in meinem Fall gut.]

    3.2 Sie können JSON-Zeichenfolge verwenden, um diese Zeichenfolge zu senden. [Dies könnte Ihre Daten korrumpieren.]

  4. Sobald Sie die Daten auf iPhone-Anwendung erhalten, konvertieren Base 64 String zu NSData. NSData+Base64
  5. Verwenden Sie die folgende Methode, um SecKeyRef Ihres privaten Schlüssels abzurufen.

    - (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; 
    } 
    

this helps !!!!!

+0

Danke für die Antwort @Pratik, aber ich habe Schlüssel im String-Format. String wird mit base64 codiert und hat dann folgendes Format: @ "- BEGIN RSA PRIVATE KEY ----- MIICXAIBAAKBgQC0HPYiPItBtjJNky ...----- END RSA PRIVATE KEY--"; Ich habe kein p12 und sein Passwort. –

+0

+1 @Pratik. Ich habe Ihren Code mit Probe .p12-Datei versucht und es funktioniert gut. –

+0

@SaurabhShukla Ich bin glücklich, jemandem zu helfen. Entschuldigung, ich konnte keine frühere Frage beantworten. –

6

Leider erfordert das Sicherheitsframework unter iOS, dass private Schlüssel im PKCS12-Format mit einer Passphrase vorliegen müssen. Öffentliche Schlüssel können in X509 gepanzertes DER oder PKCS12 sein, private Schlüssel müssen jedoch PKCS12 sein. Der private Schlüssel, den Sie verwenden möchten, ist ein PEM-formatierter RSA-Schlüssel.

Wenn Sie Zugriff auf den Schlüssel haben es die openssl command line tools mit umgewandelt werden:

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

Dies wird eine PKCS12-Datei mit dem privaten Schlüssel erstellen, und erfordert ein Passwort. Wenn Sie die Kontrolle über den privaten Schlüssel nicht haben (z. B. wenn er von einer externen Quelle wie einem Server kommt), haben Sie kein Glück.

Nehmen wir an, Sie konnten die oben genannten Schritte ausführen, um diesen privaten PES-RSA-Privatschlüssel in PKCS12 zu konvertieren. den privaten Schlüssel aus dem PKCS12 Daten extrahieren ist nicht allzu schwierig:

  1. Legen Sie die PKCS12 als NSData. Sie können dies mit dataWithContentsOfURL: tun, wenn dies eine Ressource im Dateisystem ist.
  2. Verwenden Sie SecPKCS12Import, um die PKCS12-Daten mit der Passphrase zu importieren.
  3. Extrahieren Sie die SecIdentityRef aus den importierten Elementen.
  4. Kopieren Sie die privaten Schlüssel aus dem SecIdentityRef

Eine Funktion für so tun würde:

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; 
} 

es aus Objective-C aufrufen würde wie folgt aussehen:

OSStatus status = errSecSuccess; 

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

Unter der Annahme, dass "Daten" ist eine Instanz von NSData, die die PKCS12-Daten enthält, und "Passphrase" ist eine NSString Instanzrepräsentation Nennen der Passphrase. Bei Erfolg wird "privateKey" mit dem aus den PKCS12-Daten importierten privaten Schlüssel gefüllt.

Verwandte Themen