2015-04-27 32 views
6

Próbuję zaimplementować prosty schemat kluczy licencyjnych dla mojej aplikacji i uruchamiam ważne blokady dróg. Podążam za przykładem pod adresem OpenSSL for License Keys.Korzystanie z funkcji Transformacje zabezpieczeń w celu zweryfikowania podpisu RSA utworzonego przy pomocy Ruby/OpenSSL

Ponieważ ten wpis na blogu został napisany w 2004 roku, a OpenSSL został wycofany w systemie OS X, próbuję użyć interfejsu API Security Transforms do przeprowadzenia weryfikacji klucza licencji zamiast OpenSSL. Generuję klucze prywatne i publiczne z OpenSSL; klucz licencyjny jest generowany przy użyciu klucza prywatnego przez aplikację internetową Ruby z wykorzystaniem biblioteki otworkowej Ruby OpenSSL z streszczenia SHA-256 adresu e-mail nabywcy.

Problem polega na tym, że nic, co robię, nie wydaje się tworzyć podpisu z Ruby przy użyciu OpenSSL, który będzie weryfikowany przez interfejs API Security Transforms.

Kod Ruby Pracuję off to:

require('openssl') 

# The email address used as the content of the license key. 
license = '[email protected]' 

# Generate the public/private keypair. 
`openssl genrsa -out private_key.pem 2048` 
`openssl rsa -in conductor.pem -out public_key.data -pubout` 

# Get the private key and a hash of the license. 
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem')) 
signature = OpenSSL::Digest::SHA256.digest(license) 

# The signature passed to SecVerifyTransformCreate in the OS X app. I'm not sure which of these SecVerifyTransformCreate is expecting (the binary digest, a hex representation of the digest, or the original un-digested content), but none of them work. 
signature_out = signature 
#signature_out = OpenSSL::Digest::SHA256.hexdigest(license) 
#signature_out = license 

File.write('signature.data', signature_out) 

# Sign the email address to generate the license key. Using the OpenSSL::PKey::PKey#sign method produces a license key that can only be verified on the command line by running: 
# 
# echo -n [email protected] | openssl dgst -sha256 -sign test.pem 
# 
# while using the #private_encrypt method produces a key that can only be verified on the command line by running: 
# 
# echo -n [email protected] | openssl dgst -sha256 -binary | openssl rsautl -sign -inkey test.pem 
# 
# I'm not sure what the exact difference between the two commands above is and why they correspond to the two different Ruby signing methods below. Neither approach produces something that SecVerifyTransformCreate will verify, however. 
File.write('license_key.data', 
      private_key.sign(OpenSSL::Digest::SHA256.new, license)) 
#   private_key.private_encrypt(signature)) 

i odpowiadający mu kod weryfikacyjny w Objective-C:

// Get the data. 
NSData *publicKeyData = [NSData dataWithContentsOfFile:@"public_key.data"]; 
NSData *signatureData = [NSData dataWithContentsOfFile:@"signature.data"]; 
NSData *licenseKeyData = [NSData dataWithContentsOfFile:@"license_key.data"]; 

// Import the public key. 
SecItemImportExportKeyParameters keyParameters = {}; 
SecExternalFormat format = kSecFormatOpenSSL; 
SecExternalItemType type = kSecItemTypePublicKey; 
CFArrayRef publicKeys; 

SecItemImport((__bridge CFDataRef)publicKeyData, 
       NULL, 
       &format, 
       &type, 
       0, 
       &keyParameters, 
       NULL, 
       &publicKeys); 

NSArray *publicKeysArray = (__bridge_transfer NSArray *)publicKeys; 
SecKeyRef publicKey = (__bridge SecKeyRef)publicKeysArray[0]; // TODO: How do we need to bridge this return value? 

CFErrorRef error = NULL; 

SecTransformRef verifier = SecVerifyTransformCreate(publicKey, (__bridge CFDataRef)signatureData, &error); 

SecTransformSetAttribute(verifier, kSecTransformDebugAttributeName, kCFBooleanTrue, &error); 
SecTransformSetAttribute(verifier, kSecTransformInputAttributeName, (__bridge CFDataRef)licenseKeyData, &error); 
SecTransformSetAttribute(verifier, kSecDigestTypeAttribute, kSecDigestSHA2, &error); 
SecTransformSetAttribute(verifier, kSecDigestLengthAttribute, (__bridge CFNumberRef)@256, &error); 

// I'm not sure if one of these transform attributes is necessary, but neither of them produces a verified result anyways. 
// SecTransformSetAttribute(verifier, kSecInputIsAttributeName, kSecInputIsDigest, &error); 
// SecTransformSetAttribute(verifier, kSecInputIsAttributeName, kSecInputIsRaw, &error); 

NSNumber *result = (__bridge NSNumber *)SecTransformExecute(verifier, &error); 

NSLog(@"Result: %@", result); 

Czy ktoś wie, w jaki sposób można dokonać tej pracy? Dosłownie spędziłem kilka dni, dochodząc do punktu, w którym jestem teraz i wyczerpałem moją umiejętność dalszego debagowania, więc jeśli ktokolwiek ma jakąkolwiek wiedzę, byłby ogromnie doceniony!

+0

* "Od ten post został napisany w 2004 roku, a OpenSSL został przestarzały w systemie OS X Próbuję użyć Security Transforms API, aby przeprowadzić weryfikację klucza licencji zamiast OpenSSL. "* - Inną opcją jest zbudowanie wersji OpenSSL 1.0.2 na OS X i użyj go zamiast tego. Aby skonfigurować i budować na OS X, zobacz [Kompilacja i instalacja] (https://wiki.openssl.org/index.php/Compilation_and_Installation#Mac) na wiki OpenSSL. – jww

+0

Dzięki za twój wkład! Gdybym wiedział, co wiem teraz i miałbym to zrobić jeszcze raz, zdecydowanie połączyłbym się z OpenSSL. Jednak Przewodnik po usługach kryptograficznych zdecydowanie zaleca korzystanie z interfejsu API Transform Transforms i próbowałem uniknąć łączenia innej biblioteki statycznej z moją aplikacją. W tym momencie, ponieważ w dużej mierze mam już napisany kod z Security Transforms, chciałbym dowiedzieć się, jaki jest brakujący element, a nie przejść do króliczej dziury przejścia na OpenSSL. –

+0

* "Jednakże Przewodnik po usługach kryptograficznych zdecydowanie zaleca korzystanie z API Security Transforms ..." * - Cóż, rozważ ... Apple nie naprawia wszystkich swoich błędów bezpieczeństwa, ale OpenSSL to robi. Na przykład niektóre wersje programu Apple * Secure Transport * nadal zawierają błąd [ECDHE-ECDSA] (https://wiki.openssl.org/index.php/SSL_OP_SAFARI_ECDHE_ECDSA_BUG). I [CVE-2015-1130 (Ukryty Backdoor z Rootem)] (http://apple.stackexchange.com/q/180396/83961) został naprawiony tylko w jednej niewielkiej wersji najnowszego systemu operacyjnego. O ile mi wiadomo, OpenSSL naprawia wszystkie ich błędy (i jest ich wiele). – jww

Odpowiedz

7

W skrócie mieszasz kilka kluczowych pojęć. Oto krótki wstęp do tego, jak to działa.

  1. dokument (dane licencja/e-mail) jest mieszany z strawienia (SHA256)
  2. Klucz prywatny szyfruje hash. Jest to podpis binarny podpis.
  3. Znak binarny podpis należy zakodować w formacie wygodnym do transportu, zwykle do tekstu z base64 lub podobnym.
  4. Zakodowany podpis jest przeszło z dokumentu do weryfikacji partię (Twój objc aplikacji) do weryfikacji
  5. Dokument (licencja) jest ponownie zaszyfrowaną ze sama strawienia (SHA256)
  6. kodowane podpis jest dekodowany z powrotem na kod binarny
  7. Klucz publiczny odszyfrowuje podpis, który ujawnia oryginalny hasz
  8. Ten odszyfrowany skrót jest porównywany z obliczonym, jeśli jest zgodny z dokumentem jest zweryfikowany.

Po stronie ruby ​​mylycie podpis i dokument. Musisz zahaczyć licencję SHA256, a następnie zaszyfrować kluczem prywatnym, aby utworzyć podpis. Właśnie zapisujesz skrót dokumentu jako podpis. Spróbuj tego na stronie rubinowym rzeczy:

require 'openssl' 
require 'base64' 

license = '[email protected]' 
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem')) 
digest = OpenSSL::Digest::SHA256.new 
signature = private_key.sign digest, license 
signature_out = Base64.encode64(signature) 

File.write('signature.data', signature_out) 
File.write('license_key.data', license) # no hash, no signing 

Docs rubinowe wokół tego można znaleźć here.

Nie jestem zbyt zaznajomiony z biblioteką, której używasz po stronie Objective-C, ale musisz się upewnić, że używasz tego samego algorytmu Digest do mieszania na obu końcach (SHA256), sprawdź sam algorytm szyfrowania (RSA) oraz klucz publiczny i klucz prywatny są kompatybilne (dopasowanie RSA moduł i wykładniki publiczny), a samo kodowanie binarnych danych podpisu przeszły iz powrotem (base64, hex, itp.)

Po stronie ruby ​​generujesz podpis z SHA256 i na celu-c wygląda na to, że weryfikujesz go z SHA-2 w rozmiarze 256, więc wygląda dobrze.

Decode podpis (jeśli piszesz binarny z ruby ​​można pominąć ten)

SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error); 
if (error) { CFShow(error); exit(-1); } 

SecTransformSetAttribute(decoder, 
         kSecTransformInputAttributeName, 
         signatureData, 
         &error); 
if (error) { CFShow(error); exit(-1); } 

CFDataRef signature = SecTransformExecute(decoder, &error); 
if (error) { CFShow(error); exit(-1); } 

Dla weryfikacji chcesz coś takiego, lśniły od here:

verifier = SecVerifyTransformCreate(publicKey, signature, &error); 
if (error) { CFShow(error); exit(-1); } // show your errors! 

SecTransformSetAttribute(verifier, 
         kSecTransformInputAttributeName, 
         cfLicense, // Converted from NSData 
         &error); 
if (error) { CFShow(error); exit(-1); } 

SecTransformSetAttribute(verifier, 
         kSecDigestTypeAttribute, 
         kSecDigestSHA2, 
         &error); 
if (error) { CFShow(error); exit(-1); } 

SecTransformSetAttribute(verifier, 
         kSecDigestLengthAttribute, 
         (__bridge CFNumberRef)@256, 
         &error); 
if (error) { CFShow(error); exit(-1); } 

result = SecTransformExecute(verifier, &error); 
if (error) { CFShow(error); exit(-1); } 

if (result == kCFBooleanTrue) { 
    /* Signature was valid. */ 
} else { 
    /* Signature was invalid. */ 
} 
+1

Dziękuję bardzo za szczegółową i niezwykle pomocną odpowiedź! Długie i krótkie było to, że wymieszałem podpis i wiadomość. Raz zamieniłem te dwa, wszystko działało idealnie! I dziękuję za poprawienie mnie w terminologii. Jeśli nie jest jasne, nie mam najsolidniejszego pojęcia na temat wszystkich koncepcji leżących u podstaw realizacji tego programu. Twoje definicje sprawiły, że rzeczy stały się dla mnie bardziej krystalicznie czyste :) –