2012-10-28 13 views
26

Muszę podpisać żądania przed wysłaniem ich na serwer zaplecza. Jednak klucz prywatny jest mi dany. Muszę go zaimportować, a następnie użyć go do podpisania. Mogę zaimportować i zalogować się, ale dane różnią się od tych, które otrzymuję, podpisując się za pomocą openssl. Wiem, że robi to źle, ponieważ podczas importowania klucza publicznego nie mogę go również zweryfikować. Jeśli istnieje sposób, że mogę uniknąć importowania do pęku kluczy, to też byłoby świetnie. Ciężko pracowaliśmy nad tym przez kilka dni i jest to dla nas bardzo ważne zadanie. Czy ktoś może ci pomóc. Importowanie klucza prywatnego do pęku kluczy nie działa zgodnie z oczekiwaniami w telefonie 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; 
} 

poniższy kod służy do podpisania. Część kodu z przykładu Apple (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; 

} 

użyłem przykładowy kod w http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ importować klucz publiczny i zweryfikować i jego wadą.

+0

Jaka jest wartość 'kSecPaddingPKCS1'? Czy możesz spróbować porównać moduły klucza publicznego i prywatnego? –

+0

Cześć @ Owszem, proszę wyjaśnić, co robić? Czy widzisz jakiś problem w sposobie zapisywania i pobierania klucza prywatnego. Proszę dać mi znać, jeśli dostępne są inne biblioteki, których mogę użyć. –

+0

Przepraszam, nie jestem ekspertem od ios. Trochę teraz mówię o kryptografii, więc pomyślałem, że dam ci ogólne zalecenia. Na przykład. 'kSecPaddingPKCS1' nie określa skrótu, a SHA-1 prawdopodobnie będzie domyślny. Jeśli moduł klucza prywatnego i klucza publicznego nie jest zgodny, to nie należą one do tej samej pary kluczy. W obu przypadkach weryfikacja podpisu zakończy się niepowodzeniem. –

Odpowiedz

1

Być może zamiast przechowywać wszystkie klucze w pęku kluczy, możesz po prostu przechowywać prosty łańcuch znaków w kluczowym łańcuchu (jako secret_hash). Wykonuj również bezpieczne połączenia z usługami internetowymi backendu za pomocą wspólnej, powszechnie przyjętej biblioteki AFNetworking.

Więc jeśli chcesz podpisać twoje żądania do usługi wewnętrznej bazy danych za pomocą klucza prywatnego, sugeruję, abyś zrobił to przez (a) użycie solidnej biblioteki otoki dla wywołania usługi (AFNetworking) i (b) przechowywanie klucza prywatnego jako plik .pfx w lokalizacji dostępnej dla aplikacji (zachowałbym plik .pfx w katalogu głównym projektu).

Stwórz własną podklasę AFHTTPClient i użyj podklasy do utworzenia AFHTTPRequestOperations z blokami wyzwania ustawionymi na użyj poświadczeń wyodrębnionych z pliku .pfx.

W ten sposób zamiast bezpośrednio tworzyć AFHTTPRequestOperation - utwórz je za pomocą metody MySignedAFHTTPRequestOperation podklasy AFHTTPClient. Metoda ta powinna stworzyć AFHTTPRequestOperation a następnie ustaw Block Challenge takiego ...

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

Oto więcej szczegółów na funkcji pomocnika ekstrakcji tożsamość stosowanej powyżej ...

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

Po utworzeniu AFHTTPRequest w ten sposób (tj. za pomocą metody MySignedAFHTTPRequestOperation twojej podklasy AFHTTPClient), dodaj ją do NSOperationsQueue w celu wykonania (oczywiście musisz ustawić bloki sukcesu i fail handler odpowiednio podczas tworzenia operacji)

W skrócie:

  • korzystać z solidnych ram AFNetworking aby Państwa usługa nazywa
  • sklep CERT klucz prywatny jako plik .pfx w aplikacji i wykorzystać go do tworzenia poświadczenia i klucze
  • pozwalają AFNetworking wykonać szyfrowanie, ustawiając poświadczenia w każdym tworzonym obiekcie operacji.

Mam nadzieję, że to pomoże.

+0

Przechowywanie klucza prywatnego w zwykłym pliku bez hasła brzmi jak zły pomysł. Sugerowanie, że jest on dystrybuowany jako plik z aplikacją mobilną, zdaje się ominąć wszelkie zabezpieczenia, dla których został oszukany. KeyChain jest przeznaczony dokładnie do przechowywania poufnych danych i powinien zostać użyty. –

+0

Punkt wzięty. Moje rozwiązanie koncentruje się bardziej na wywołaniu usług backendu i dopasowaniu szyfrowania do tych ram (AFNetworking). Zaktualizowałem powyższe rozwiązanie, aby rozwiązać problem, przechowując nie sam klucz, ale hasło dla klucza w pęku kluczy (konkretne połączenia z kluczem pobranie hasła z łańcucha kluczy musi zostać zastąpione w wierszu, w którym jest pobierana wiadomość mySecretHash – CoolDocMan

+0

Nawet przy tej zmianie, klucz pęku kluczy jest nadal lepszym miejscem do przechowywania poufnych informacji, takich jak klucze. Nie jest również jasne, w jaki sposób twoje sugestie pomagają plakatowi, którego pytanie wydaje się dotyczyć mechaniki generowania podpisu, a nie tworzenia żądań HTTP. –

3

Spójrz na ostatnią metodę w przyjętym odpowiedzią na: Converting NSData to SecKeyRef

Problemem jest to, że iOS obsługuje kluczy publicznych i prywatnych w nieco inny sposób, gdzie nagłówek identyfikacja że zazwyczaj istnieje i jest oczekiwany przez innych API bezpieczeństwa (w Java, na przykład) nie jest oczekiwana w systemie iOS. Więc musisz je rozebrać.

0

Jeśli chcesz tylko zapisać PEM AES-256 RSA, który masz w standardowym formacie typu base64 zgodnym z PHP, do łańcucha kluczy systemu, działa to dla mnie.

Publikuję to tutaj, ponieważ w wielu miejscach czytałem, że nie można po prostu przykleić pliku PEM bezpośrednio do iOS; albo musisz usunąć z niego kilka nagłówków; itp. Jednak, przynajmniej na iOS 9.3, to teraz działa, a gdybym to gdzieś zobaczył, zaoszczędziłoby mi to wiele czasu. (Uwaga: poniżej jest mocno zmodyfikowana wersja części Objective-C-RSA z https://github.com/ideawu/Objective-C-RSA, patrz odpowiednia licencja, nie sugeruję żadnych rekomendacji, mają też wersję Swift tutaj: https://github.com/btnguyen2k/swift-rsautils, która wydaje się o wiele więcej funkcji kompletną i może rozwiązać problemy dla wielu ludzi.)

#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; 
} 
Powiązane problemy