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ć.
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? –
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
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