2012-10-28 6 views
26

Devo firmare le richieste prima di inviarlo al server di back-end. Comunque la chiave privata mi è stata data. Quindi ho bisogno di importarlo e quindi usarlo per firmare. Sono in grado di importare e accedere ma i dati sono diversi da quelli che ottengo firmando usando openssl. So che è sbagliato perché quando imposto la chiave pubblica, non posso verificarlo. Se c'è un modo in cui posso evitare di importare in portachiavi, sarebbe fantastico. Abbiamo lavorato duramente su questo per un paio di giorni e questo è un ottimo lavoro per noi. Qualcuno può aiutarmi per favore. L'importazione della chiave privata in Keychain non funziona come previsto in iphone

- (SecKeyRef) getPrivateKey { 
//RSA KEY BELOW IS DUMMY. 

key = @"-----BEGIN RSA PRIVATE KEY-----\nORtMei3ImKI2ZKI636I4+uNCwFfZv9pyJzXyfr1ZNo7iaiW7A0NjLxikNxrWpr/M\n6HD8B2j/CSjRPW3bhsgDXAx/AI1aSfJFxazjiTxx2Lk2Ke3jbhE=\n-----END RSA PRIVATE KEY-----\n"; 

NSString * tag = @"adpPrivateKey"; 

    NSString *s_key = [NSString string]; 
    NSArray *a_key = [key componentsSeparatedByString:@"\n"]; 
    BOOL  f_key = FALSE; 

    for (NSString *a_line in a_key) { 
     if ([a_line isEqualToString:@"-----BEGIN RSA PRIVATE KEY-----"]) { 
      f_key = TRUE; 
     } 
     else if ([a_line isEqualToString:@"-----END RSA PRIVATE KEY-----"]) { 
      f_key = FALSE; 
     } 
     else if (f_key) { 
      s_key = [s_key stringByAppendingString:a_line]; 
     } 
    } 
    if (s_key.length == 0) return(nil); 

    // This will be base64 encoded, decode it. 
    NSData *d_key = [NSData dataFromBase64String:s_key]; 

if(d_key == nil) return nil; 

    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; 

    // Delete any old lingering key with the same tag 
    NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init]; 
    [privateKey setObject:(id) kSecClassKey forKey:(id)kSecClass]; 
    [privateKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; 
    [privateKey setObject:d_tag forKey:(id)kSecAttrApplicationTag]; 
    SecItemDelete((CFDictionaryRef)privateKey); 

    CFTypeRef persistKey = nil; 

    // Add persistent version of the key to system keychain 
    [privateKey setObject:d_key forKey:(id)kSecValueData]; 
    [privateKey setObject:(id) kSecAttrKeyClassPrivate forKey:(id) 
    kSecAttrKeyClass]; 
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(id) 
    kSecReturnPersistentRef]; 

    OSStatus secStatus = SecItemAdd((CFDictionaryRef)privateKey, &persistKey); 
    if (persistKey != nil) CFRelease(persistKey); 

    if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) { 
     [privateKey release]; 
     return(nil); 
    } 

    // Now fetch the SecKeyRef version of the key 
    SecKeyRef keyRef = nil; 

    [privateKey removeObjectForKey:(id)kSecValueData]; 
    [privateKey removeObjectForKey:(id)kSecReturnPersistentRef]; 
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef 
    ]; 
    [privateKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; 
    secStatus = SecItemCopyMatching((CFDictionaryRef)privateKey, 
            (CFTypeRef *)&keyRef); 

    if(secStatus != noErr) 
     return nil; 

    [privateKey release]; 

    return keyRef; 
} 

Il codice di seguito viene utilizzata per firmare. Una parte del codice è da Apple esempio (http://developer.apple.com/library/ios/#samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html#//apple_ref/doc/uid/DTS40008019-Classes_SecKeyWrapper_m-DontLinkElementID_17)

- (NSData *)getSignatureBytes:(NSString *)plainText { 

OSStatus sanityCheck = noErr; 
NSData * signedHash = nil; 
uint8_t * signedHashBytes = NULL; 
size_t signedHashBytesSize = 0; 
SecKeyRef privateKey = NULL; 

privateKey = [self getPrivateKey]; 

signedHashBytesSize = SecKeyGetBlockSize(privateKey); 

//Create a SHA Encoded 
NSString * shaEncoded = [self sha256:plainText]; 
NSLog(@"%@", shaEncoded); 


// Malloc a buffer to hold signature. 

signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t)); 
memset((void *)signedHashBytes, 0x0, signedHashBytesSize); 


NSData *inputData = [self getHashBytes:[plainText dataUsingEncoding:NSUTF8StringEncoding]]; 
int bytesLengthUINT8 = [inputData length]; 

sanityCheck = SecKeyRawSign (privateKey, kSecPaddingPKCS1, (const uint8_t *)inputData, CC_SHA256_DIGEST_LENGTH,(uint8_t *)signedHashBytes, &signedHashBytesSize); 


if(sanityCheck != noErr) 
    return nil; 


signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];  
NSString *string = [signedHash base64EncodedString]; 

NSLog(@"%@", string); 


if (signedHashBytes) free(signedHashBytes); 
return signedHash; 

} 

ho usato il codice di esempio in http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ importare la chiave pubblica e la verifica e la sua mancanza.

+0

Qual è il valore di 'kSecPaddingPKCS1'? Potresti provare a confrontare i moduli della chiave pubblica e privata? –

+0

Salve @owlstead, puoi per favore approfondire cosa fare? Inoltre vedi qualche problema nel modo in cui sto salvando e recuperando la chiave privata. Per favore fatemi sapere se ci sono altre librerie disponibili che posso usare. –

+0

Scusa, non sono un esperto di ios. Però adesso ho un bel po 'di crittografia, quindi ho pensato di darti alcuni consigli generali. Per esempio. 'kSecPaddingPKCS1' non specifica un hash, e SHA-1 è probabilmente l'impostazione predefinita. Se il modulo della chiave privata e della chiave pubblica non corrispondono, allora non appartengono alla stessa coppia di chiavi. In entrambi i casi la verifica della firma fallirebbe. –

risposta

1

Forse invece di memorizzare tutta la tua chiave in un portachiavi puoi semplicemente memorizzare una semplice stringa di caratteri nella catena di chiavi (come secret_hash). Inoltre, effettua chiamate sicure ai servizi Web di back-end utilizzando la libreria AFNetworking comune e ampiamente adottata.

Quindi, se è necessario firmare le richieste a un servizio di back-end utilizzando una chiave privata, suggerisco di farlo (a) utilizzando una robusta libreria di wrapper per l'invocazione del servizio (AFNetworking) e (b) l'archiviazione della chiave privata come un file .pfx in una posizione accessibile all'app (manterrei il file .pfx nella radice del progetto.)

Quindi creare la propria sottoclasse di AFHTTPClient e utilizzare la sottoclasse per creare AFHTTPRequestOperations con i blocchi di verifica impostati su usa le credenziali estratte da .pfx.

In questo modo, invece di creare direttamente AFHTTPRequestOperation, crearli utilizzando un metodo MySignedAFHTTPRequestOperation della sottoclasse AFHTTPClient. Questo metodo dovrebbe creare l'AFHTTPRequestOperation e quindi impostare il blocco sfida come questa ...

[myOperationObject setAuthenticationChallengeBlock:^(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge) 
    { 
      NSString * pfxPath = [[NSBundle mainBundle] 
          pathForResource:@“pvtKeyFile” ofType:@"pfx"]; 

      NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile: pfxPath]; 
      CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;  
      SecIdentityRef identity; 
      SecTrustRef trust; 
      myIdentityAndTrustExtractionHelper(inPKCS12Data, &identity, &trust); 

      SecCertificateRef certificate = NULL; 
      SecIdentityCopyCertificate (identity, &certificate); 

      const void *certs[] = {certificate}; 
      CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); 

      NSURLCredential *credential = [NSURLCredential 
              credentialWithIdentity:identity 
              certificates:(__bridge NSArray*)certArray 
              persistence:NSURLCredentialPersistencePermanent]; 

      [[challenge sender] useCredential:credential 
       forAuthenticationChallenge:challenge]; 
      CFRelease(certArray); 
    } 

Ecco qualche dettaglio in più sulla funzione di supporto di estrazione identità utilizzata sopra ...

OSStatus myIdentityAndTrustExtractionHelper(CFDataRef inPKCS12Data,   
           SecIdentityRef *mySecIdentityRef, 
           SecTrustRef *myTrustRef) 
{ 
    //modify to get secret-hash from keychain 
    CFStringRef mySecretHash = CFSTR(secret_hash); 
    const void *keys[] = { kSecImportExportPassphrase }; 
    const void *values[] = { mySecretHash }; 


    CFArrayRef pkscItems = CFArrayCreate(NULL, 0, 0, NULL); 
    OSStatus mySecurityError = SecPKCS12Import(inPKCS12Data, 
           CFDictionaryCreate(NULL,keys, values, 1, 
           NULL, NULL), 
           &pkscItems); 
    if (mySecurityError == 0) 
    {         
     CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (pkscItems, 0); 
     const void *tempIdentity = NULL; 
     tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, 
              kSecImportItemIdentity); 
     *mySecIdentityRef = (SecIdentityRef)tempIdentity; 
     const void *tempTrust = NULL; 
     tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust); 
     *myTrustRef = (SecTrustRef)tempTrust; 
    } 

    return mySecurityError; 
} 

Una volta creato AFHTTPRichiesta in questo modo (ad es. tramite il metodo MySignedAFHTTPRequestOperation della sottoclasse AFHTTPClient), aggiungilo a NSOperQueue per l'esecuzione (ovviamente è necessario impostare correttamente e correttamente i blocchi del gestore fallito quando si crea l'operazione)

In sintesi:

  • utilizzare il framework AFNetworking robusto per rendere il vostro webservice chiama
  • archivio la chiave privata cert come file pfx nella app e usarlo per creare le credenziali e le chiavi
  • consentono AFNetworking per fare la crittografia per te impostando le credenziali all'interno di ogni oggetto operazione che hai creato.

Spero che questo aiuti.

+0

Memorizzare una chiave privata in un file normale senza una password sembra una cattiva idea. Suggerire di essere distribuito come un file con l'applicazione (mobile) sembra sconfiggere qualsiasi scopo di sicurezza per il quale è stato truffato. KeyChain è inteso esattamente allo scopo di memorizzare dati sensibili e dovrebbe essere usato. –

+0

Punto preso. La mia soluzione si concentra maggiormente sull'invocazione dei servizi di back-end e sull'adattamento della crittografia in tale framework (AFNetworking). Ho aggiornato la soluzione di cui sopra per risolvere il problema memorizzando non la chiave stessa ma la passphrase per la chiave nel portachiavi (chiamate specifiche a recuperare la passphrase dal portachiavi deve essere sostituito nella riga in cui viene prelevato mySecretHash. – CoolDocMan

+0

Anche con quella modifica, il portachiavi è ancora un posto migliore per memorizzare informazioni sensibili come le chiavi. Inoltre non è chiaro in che modo i tuoi suggerimenti aiutano il poster La domanda sembra riguardare la meccanica del generare la firma, non quella di fare le richieste HTTP. –

3

Date un'occhiata all'ultimo metodo nella risposta accettata per: Converting NSData to SecKeyRef

Il problema è che iOS gestisce chiavi pubbliche e private in modo leggermente diverso in cui l'intestazione di identificazione che di solito esiste e che ci si aspetta da altri API di sicurezza (in Java, per esempio) non è previsto in iOS. Quindi devi spogliarli.

0

Se si desidera archiviare un PEM RSA AES-256 in formato di stringa di base 64 standard compatibile con PHP, nel portachiavi di sistema, questo funziona per me.

Sto postando questo qui perché un sacco di posti, ho letto che non si può semplicemente attaccare un file PEM direttamente in iOS; oppure devi togliere alcune intestazioni da esso; ecc. Tuttavia, almeno su iOS 9.3, ora funziona, e se avessi visto questo da qualche parte, mi avrebbe risparmiato un sacco di tempo. (Nota: la seguente è una versione pesantemente modificata di Objective-C-RSA da https://github.com/ideawu/Objective-C-RSA, vedere la licenza applicabile, non implicano alcuna approvazione.Inoltre hanno una versione Swift qui: https://github.com/btnguyen2k/swift-rsautils che sembra molto più completa di funzionalità e risolverebbe problemi per molte persone)

#define BR (__bridge id) 
#define BRD (__bridge CFDictionaryRef) 

+ (SecKeyRef)storePrivateKey:(NSString *)key inSystemKeychainWithTag:(NSString *)tag { 
    NSRange spos; 
    NSRange epos; 
    spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"]; 
    if(spos.length > 0) { 
     epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"]; 
    } 
    else { 
     spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"]; 
     epos = [key rangeOfString:@"-----END PRIVATE KEY-----"]; 
    } 
    if(spos.location != NSNotFound && epos.location != NSNotFound){ 
     NSUInteger s = spos.location + spos.length; 
     NSUInteger e = epos.location; 
     NSRange range = NSMakeRange(s, e-s); 
     key = [key substringWithRange:range]; 
    } 
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@" " withString:@""]; 

    // This will be base64 encoded, decode it. 
    NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:0]; 

    if(data == nil){ 
     return nil; 
    } 

    //a tag to read/write keychain storage 
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; 

    // Delete any old lingering key with the same tag 
    NSMutableDictionary *options = [ 
    @{ 
     BR kSecClass: BR kSecClassKey, 
     BR kSecAttrKeyType: BR kSecAttrKeyTypeRSA, 
     BR kSecAttrApplicationTag: d_tag, 
    } 
    mutableCopy]; 

    SecItemDelete(BRD options); 

    // Add persistent version of the key to system keychain 
    [options addEntriesFromDictionary: 
    @{ 
     BR kSecValueData:data, 
     BR kSecAttrKeyClass: BR kSecAttrKeyClassPrivate, 
     BR kSecReturnPersistentRef: @YES, 
    }]; 

    CFTypeRef persistKey = nil; 
    OSStatus status = SecItemAdd(BRD options, &persistKey); 
    if (persistKey != nil){ 
     CFRelease(persistKey); 
    } 
    if ((status != noErr) && (status != errSecDuplicateItem)) { 
     return nil; 
    } 

    [options removeObjectForKey:BR kSecValueData]; 
    [options removeObjectForKey:BR kSecReturnPersistentRef]; 

    [options addEntriesFromDictionary: 
    @{ 
     BR kSecReturnRef:@YES, 
     BR kSecAttrKeyType:BR kSecAttrKeyTypeRSA, 
    }]; 

    // Now fetch the SecKeyRef version of the key 
    SecKeyRef keyRef = nil; 
    status = SecItemCopyMatching(BRD options, (CFTypeRef *)&keyRef); 
    if(status != noErr){ 
     return nil; 
    } 
    return keyRef; 
}