2015-10-16 25 views
11

W mojej bibliotece statycznej mam plik licencji. Które chcę mieć pewność, że zostały wygenerowane przez siebie (i nie zostały zmienione). Tak więc pomysł polegał na użyciu podpisu RSA z tego, co przeczytałem.Sprawdzanie podpisu RSA iOS

Szukałem w internecie i to jest to, co wymyśliłem:

pierwsze: Generowanie kluczy prywatnych i certyfikatów z podpisem własnym z informacjami znalazłem here.

// Generate private key 
openssl genrsa -out private_key.pem 2048 -sha256 

// Generate certificate request 
openssl req -new -key private_key.pem -out certificate_request.pem -sha256 

// Generate public certificate 
openssl x509 -req -days 2000 -in certificate_request.pem -signkey private_key.pem -out certificate.pem -sha256 

// Convert it to cer format so iOS kan work with it 
openssl x509 -outform der -in certificate.pem -out certificate.cer -sha256 

Następnie utworzyć plik licencji (z datą i app identyfikatora jako treści) i wygenerować sygnaturę dla tego pliku jak tak na podstawie informacji znalezionych here:

// Store the sha256 of the licence in a file 
openssl dgst -sha256 licence.txt > hash 

// And generate a signature file for that hash with the private key generated earlier 
openssl rsautl -sign -inkey private_key.pem -keyform PEM -in hash > signature.sig 

Który ja myślę, że wszystko działa dobrze. Nie dostaję żadnych błędów i otrzymuję klucze, certyfikaty i inne pliki zgodnie z oczekiwaniami.

Następnie kopiuję certificate.cer, i license.txt do mojej aplikacji.

Teraz chcę sprawdzić, czy podpis został podpisany przeze mnie i jest ważny dla pliku license.txt. I okazało się, że dość trudno znaleźć jakieś dobre przykłady, ale to jest to, co mam obecnie:

Seucyrity.Framework dowiedziałem się używa SecKeyRef odwoływać się klucz/certyfikat RSA i SecKeyRawVerify do weryfikacji podpisu.

Mam następujący sposób, aby załadować klucz publiczny z pliku.

- (SecKeyRef)publicKeyFromFile:(NSString *) path 
{ 
    NSData *myCertData = [[NSFileManager defaultManager] contentsAtPath:path]; 
    CFDataRef myCertDataRef = (__bridge CFDataRef) myCertData; 

    SecCertificateRef cert = SecCertificateCreateWithData (kCFAllocatorDefault, myCertDataRef); 
    CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL); 
    SecPolicyRef policy = SecPolicyCreateBasicX509(); 
    SecTrustRef trust; 
    SecTrustCreateWithCertificates(certs, policy, &trust); 
    SecTrustResultType trustResult; 
    SecTrustEvaluate(trust, &trustResult); 
    SecKeyRef pub_key_leaf = SecTrustCopyPublicKey(trust); 

    if (trustResult == kSecTrustResultRecoverableTrustFailure) 
    { 
     NSLog(@"I think this is the problem"); 
    } 
    return pub_key_leaf; 
} 

Który opiera się na this SO post.

Do weryfikacji podpisu znalazłem następującą funkcję

BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey) 
{ 
    size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey); 
    const void* signedHashBytes = [signature bytes]; 

    size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH; 
    uint8_t* hashBytes = malloc(hashBytesSize); 
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) { 
     return nil; 
    } 

    OSStatus status = SecKeyRawVerify(publicKey, 
             kSecPaddingPKCS1SHA256, 
             hashBytes, 
             hashBytesSize, 
             signedHashBytes, 
             signedHashBytesSize); 

    return status == errSecSuccess; 
} 

która jest podejmowana z here

W moim projekcie nazywam kod tak:

// Get the licence data 
NSString *licencePath = [[NSBundle mainBundle] pathForResource:@"licence" ofType:@"txt"]; 
NSData *data = [[NSFileManager defaultManager] contentsAtPath:licencePath]; 

// Get the signature data 
NSString *signaturePath = [[NSBundle mainBundle] pathForResource:@"signature" ofType:@"sig"]; 
NSData *signature = [[NSFileManager defaultManager] contentsAtPath:signaturePath]; 

// Get the public key 
NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"]; 
SecKeyRef publicKey = [self publicKeyFromFile:publicKeyPath]; 

// Check if the signature is valid with this public key for this data 
BOOL result = PKCSVerifyBytesSHA256withRSA(data, signature, publicKey); 

if (result) 
{ 
    NSLog(@"Alright All good!"); 
} 
else 
{ 
    NSLog(@"Something went wrong!"); 
} 

Obecnie to zawsze mówi : "Coś poszło nie tak!" chociaż nie jestem pewien co. Okazało się, że wynik zaufania w metodzie, która pobiera klucz publiczny, jest równy kSecTrustResultRecoverableTrustFailure, co moim zdaniem jest problemem. W pliku Apple documentation stwierdziłem, że może to być wynikiem certyfikatu, który wygasł. Chociaż tak nie jest w tym przypadku. Ale może coś jest nie tak ze sposobem generowania mojego certyfikatu?

Moje pytanie sprowadza się do tego, co robię źle, i jak mogę to naprawić? Uważam, że dokumentacja na ten temat jest dość skąpa i trudna do odczytania.

Mam uploaded projekt iOS z wygenerowanymi certyfikatami i kodem wymienionym tutaj. Może to może się przydać.

+1

Jeśli spojrzeć na ogłoszenia 3- 5 w dokumentacji, którą wskazałeś, zobaczysz ją z listą "AllStatusBits". Czy możesz dowiedzieć się, jakie są bity statusu, gdy napotkasz ten błąd? –

+0

Witam, kiedy próbuję włączyć ten kod, jeden z pierwszych błędów jest taki, że typ 'CSSM_TP_APPLE_CERT_STATUS' z' AllStatusBits' jest nieznany i nie mogę znaleźć działającego pliku nagłówkowego do włączenia, aby uzyskać ten typ. W Internecie stwierdziłem, że może to być "#import ", ale ten już nie istnieje (już?) Na iOS. - Mam [przesłany] (http://up.indev.nl/RTR4y0Ou0L.zip) mój projekt z kodem i certyfikatami, być może to pomaga. – Matthijn

+0

Tak, właśnie sprawdziłem dwa razy, ale zrobiłem to już (http://up.indev.nl/C5OHOFziPO.png), dodając strukturę Security. Może to zostało przeniesione gdzieś indziej w wersji na iOS? – Matthijn

Odpowiedz

8

Problem leży w sposobie tworzenia pliku podpisu; po tym samym kroku udało mi się wyprodukować binarny odpowiednik pliku signature.sig.

Patrząc wewnątrz pliku hash widzimy openssl dodać prefiks (a hex zakodować hash):

$ cat hash 
SHA256(licence.txt)= 652b23d424dd7106b66f14c49bac5013c74724c055bc2711521a1ddf23441724 

Więc signature.sig jest oparty na tym, a nie na license.txt

, używając próbkę i tworzenia pliku podpisu z:

openssl dgst -sha256 -sign certificates/private_key.pem licence.txt > signature.sig 

wycieniowanie & podpisanie ste p dostaje prawidłowe, a wyjścia przykładowe: Alright All good!


Końcowy stan mojego pliku, tylko w przypadku

- (SecKeyRef)publicKeyFromFile:(NSString *) path 
{ 
    NSData * certificateData = [[NSFileManager defaultManager] contentsAtPath:path]; 
    SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData); 
    SecPolicyRef secPolicy = SecPolicyCreateBasicX509(); 
    SecTrustRef trust; 
    SecTrustCreateWithCertificates(certificateFromFile, secPolicy, &trust); 
    SecTrustResultType resultType; 
    SecTrustEvaluate(trust, &resultType); 
    SecKeyRef publicKey = SecTrustCopyPublicKey(trust); 
    return publicKey; 
} 

BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey) 
{ 
    uint8_t digest[CC_SHA256_DIGEST_LENGTH]; 
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], digest)) 
     return NO; 

    OSStatus status = SecKeyRawVerify(publicKey, 
             kSecPaddingPKCS1SHA256, 
             digest, 
             CC_SHA256_DIGEST_LENGTH, 
             [signature bytes], 
             [signature length]); 

    return status == errSecSuccess; 
} 

PS: malloc był przeciek


Edit: Aby uzyskać bieżący plik signature.sig, wykonaj następujące czynności: s, trzeba produkować ten sam etap jak openssl (dodać przedrostek, hex mieszania i nową linią \n) przekazuje te dane do SecKeyRawVerify z kSecPaddingPKCS1 nie kSecPaddingPKCS1SHA256:

BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey) 
{ 
    uint8_t digest[CC_SHA256_DIGEST_LENGTH]; 
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], digest)) 
     return NO; 

    NSMutableString *hashFile = [NSMutableString stringWithFormat:@"SHA256(licence.txt)= "]; 
    for (NSUInteger index = 0; index < sizeof(digest); ++index) 
     [hashFile appendFormat:@"%02x", digest[index]]; 

    [hashFile appendString:@"\n"]; 
    NSData *hashFileData = [hashFile dataUsingEncoding:NSNonLossyASCIIStringEncoding]; 

    OSStatus status = SecKeyRawVerify(publicKey, 
             kSecPaddingPKCS1, 
             [hashFileData bytes], 
             [hashFileData length], 
             [signature bytes], 
             [signature length]); 

    return status == errSecSuccess; 
} 
+0

Ciągle jednak otrzymujesz 'kSecTrustResultRecoverableTrustFailure' w' SecTrustEvaluate', ale tak, to jest pomyślne sprawdzanie podpisu – Lefteris

+0

Myślę, że problem kSecTrustResultRecoverableTrustFailure jest to, że twój certyfikat jest sam podpisany, jeśli chcesz, aby poprawnie się udało cały łańcuch zaufania musi być ważne; musisz uzyskać certyfikat od zaufanego (Apple) organu. (ver * s * gn lub tym podobne) – blld

+0

lub zainstaluj certyfikat na hoście iOS (co nie wydaje się być opcją) – blld