2015-10-16 20 views
11

Nella mia libreria statica ho un file di licenza. Che voglio essere sicuro sia stato generato da me stesso (e non sia stato alterato). Quindi l'idea era di usare una firma RSA da quello che ho letto.Verifica firma RSA iOS

Ho guardato su internet e questo è ciò che mi si avvicinò con:

Primo: La generazione delle chiavi private e certificati self firmato con le informazioni che ho trovato 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 

Dopo di che, ho creare un file di licenza (con una data e app identificatore come contenuti) e generare una firma per il file in questo modo sulla base delle informazioni trovate 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 

Che io pensa che tutto funzioni bene. Non ricevo errori e ottengo chiavi, certificati e altri file come previsto.

Successivamente, copio certificate.cer, signature.sig e license.txt alla mia applicazione.

Ora voglio controllare se la firma è stata firmata da me ed è valida per license.txt. L'ho trovato abbastanza difficile trovare dei buoni esempi, ma questo è quello che ho attualmente:

Il Seucyrity.Framework ho scoperto che utilizza un SecKeyRef fare riferimento a un RSA chiave/certificato e SecKeyRawVerify per verificare una firma.

Ho il seguente metodo per caricare la chiave pubblica da un file.

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

Quale è basato su this messaggio SO.

Per la convalida della firma ho trovato la seguente funzione

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

che è tratto da here

Nel mio progetto che io chiamo il codice in questo modo:

// 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!"); 
} 

Attualmente dice sempre : "Qualcosa è andato storto!" anche se non sono sicuro di cosa. Ho scoperto che il risultato della fiducia nel metodo che recupera la chiave pubblica equivale a kSecTrustResultRecoverableTrustFailure che penso sia il problema. Nel Apple documentation ho scoperto che potrebbe essere il risultato di un certificato che è scaduto. Anche se non sembra essere il caso qui. Ma forse c'è qualcosa di sbagliato nel modo in cui generi il mio certificato?

La mia domanda si riduce a, cosa sto sbagliando, e come potrei risolverlo? Trovo la documentazione su questo piuttosto scarso e difficile da leggere.

Ho uploaded un progetto iOS con certificati generati e il codice di riferimento qui. Forse potrebbe tornare utile.

+1

Se si guarda a Listing 3- 5 nella documentazione che hai indicato, lo vedrai elencando "' AllStatusBits' ". Riesci a capire quali sono i bit di stato quando incontri quell'errore? –

+0

Salve, quando provo a incorporare quel codice uno dei primi errori è che il tipo 'CSSM_TP_APPLE_CERT_STATUS' di' AllStatusBits' è sconosciuto e non riesco a trovare un file di intestazione funzionante da includere per ottenere quel tipo. Su internet ho scoperto che potrebbe essere "#import " ma che non esiste più (più?) Su iOS. - Ho [caricato] (http://up.indev.nl/RTR4y0Ou0L.zip) il mio progetto con il codice e i certificati, forse questo aiuta. – Matthijn

+0

Sì, ho appena controllato due volte, ma ho già [già] (http://up.indev.nl/C5OHOFziPO.png) l'aggiunta del framework Security. Forse è stato spostato da qualche altra parte in una versione iOS? – Matthijn

risposta

8

Il problema si trova nel modo in cui si crea il file di firma; seguendo lo stesso passo sono stato in grado di produrre il file binario equivalente signature.sig.

Guardando all'interno del file hash possiamo vedere OpenSSL aggiungere un po 'di prefisso (e hex codificare l'hash):

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

Così signature.sig si basa su questo e non su license.txt

Usando il campione e la creazione del file di firma con:

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

l'hash & firma ste p ottiene corretta, e le uscite del campione: Alright All good!


Lo stato finale del mio file, nel caso

- (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: la malloc stata una fuga


Edit:

Per fare in modo che il file corrente signature.sig funzioni come s, si deve produrre lo stesso passo, come OpenSSL (aggiungere il prefisso, hex-hash, e un ritorno a capo \n), quindi questi dati a SecKeyRawVerify con kSecPaddingPKCS1 e non 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

Si ottiene comunque un 'kSecTrustResultRecoverableTrustFailure' nel 'SecTrustEvaluate', ma si sta verificando con successo la firma – Lefteris

+0

Penso che il problema kSecTrustResultRecoverableTrustFailure sia che il certificato è autofirmato, se si vuole che abbia successo correttamente tutta la catena di fiducia deve essere valido; devi ottenere un certificato da un'autorità fidata (Apple). (ver * s * gn o simili) – blld

+0

o installare il certificato sul dispositivo iOS host (che non sembra essere un'opzione) – blld