2015-04-27 18 views
6

Sto cercando di implementare uno schema di chiave di licenza semplice per la mia app e sto correndo in significativi roadblock. Sto seguendo l'esempio allo OpenSSL for License Keys.Utilizzo di Security Transforms per verificare una firma RSA creata con Ruby/OpenSSL

Poiché il post di questo blog è stato scritto nel 2004 e OpenSSL è stato deprecato su OS X, sto tentando di utilizzare l'API di Security Transforms per eseguire la verifica della chiave di licenza anziché OpenSSL. Tuttavia sto generando le chiavi private e pubbliche con OpenSSL; la chiave di licenza viene generata utilizzando la chiave privata da un'app Web di Ruby utilizzando la libreria di wrapper OpenSSL di Ruby da un digest SHA-256 dell'indirizzo email dell'acquirente.

Il problema è che nulla di ciò che faccio sembra produrre una firma da Ruby usando OpenSSL che verrà verificata dall'API di Security Transforms.

Il codice Ruby sto lavorando fuori di è:

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)) 

E il codice di verifica corrispondente in 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); 

Qualcuno sa come posso fare questo lavoro? Ho letteralmente passato giorni ad arrivare al punto in cui mi trovo ora e ho esaurito la mia capacità di eseguire il debug di questo ulteriore, quindi se qualcuno ha qualche intuizione sarebbe molto apprezzato!

+0

* "Dal momento che quel post è stato scritto nel 2004 e OpenSSL è stato deprecato su OS X Sto tentando di utilizzare l'API Security Transforms per eseguire la verifica della chiave di licenza invece di OpenSSL. "* - Un'altra opzione è creare la versione di OpenSSL 1.0.2 su OS X e usalo invece. Per la configurazione e la creazione su OS X, vedere [Compilazione e installazione] (https://wiki.openssl.org/index.php/Compilation_and_Installation#Mac) nella wiki di OpenSSL. – jww

+0

Grazie per il vostro contributo! Se sapessi quello che so adesso e lo farei di nuovo, mi limiterò a collegarmi semplicemente a OpenSSL. Tuttavia, la Guida ai servizi di crittografia sollecita fortemente l'uso dell'API di Transform Transform e stavo cercando di evitare il collegamento di un'altra libreria statica alla mia app. A questo punto, dato che ho in gran parte già scritto il codice con Security Transforms, mi piacerebbe capire quale sia il pezzo mancante piuttosto che andare in una tana del coniglio a passare a OpenSSL. –

+0

* "Tuttavia, la Guida ai servizi di crittografia sollecita fortemente l'uso dell'API delle trasformazioni di sicurezza ..." * - Bene, considera ... Apple non risolve tutti i bug relativi alla sicurezza, ma OpenSSL lo fa. Ad esempio, alcune versioni di * Secure Transport * di Apple hanno ancora il bug [ECDHE-ECDSA] (https://wiki.openssl.org/index.php/SSL_OP_SAFARI_ECDHE_ECDSA_BUG). E [CVE-2015-1130 (Hidden Backdoor with Root)] (http://apple.stackexchange.com/q/180396/83961) è stato corretto solo in una revisione secondaria del sistema operativo più recente. Per quanto ne so, OpenSSL corregge tutti i loro bug (e ce ne sono molti a volte). – jww

risposta

7

In breve, stai mescolando alcuni concetti chiave. Ecco un veloce suggerimento su come funziona.

  1. Un documento (i dati di licenza/e-mail) viene assegnata con un digerire (SHA256)
  2. chiave privata di crittografare l'hash. Questa è la firma binaria .
  3. La firma binaria deve essere codificata in un formato conveniente per il trasporto, di solito in testo con base64 o qualcosa di simile.
  4. Il codificato firma è passato sopra con documento a verificare partito (l'applicazione objc) per la verifica
  5. Document (la licenza) viene assegnata di nuovo con lo stesso digerire (SHA256)
  6. Codificato firma viene decodificato indietro nel binario
  7. chiave pubblica decifra firma che rivela l'hash originale
  8. Questo hash decrittografato viene confrontato con quello calcolato, se corrispondono a il documento è verificato.

Sul lato rubino si confondono firma e documento. È necessario eseguire l'hash della licenza con SHA256 e quindi crittografarla con una chiave privata per produrre una firma. Stai solo salvando un hash del documento come firma. Prova questo fuori sul lato rubino delle cose:

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 

La documentazione rubino attorno a questo si possono trovare here.

Io non sono super familiarità con la libreria che si sta utilizzando sul lato Objective-C delle cose, ma il trucco è quello di assicurarsi che si sta usando lo stesso algoritmo del digest per hashing su entrambe le estremità (SHA256), controllare stesso algoritmo di crittografia (RSA) e la chiave pubblica e chiave privata sono compatibili (corrispondenti RSA modulo e esponenti pubblici), e stessa codifica per i dati binari firma passati avanti e indietro (base64, esadecimale, ecc.)

Sul lato rubino che stai generando una firma con SHA256 e sull'obiettivo-c sembra che tu lo stia verificando con la dimensione 256 SHA-2, così sembra ok.

Decode la firma (se si sta scrivendo binario dal rubino è possibile saltare questo)

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

Per la verifica si desidera qualcosa di simile, brillavano da 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

Grazie mille per la tua risposta dettagliata ed estremamente utile! Quindi, il lungo e corto di esso era in effetti il ​​fatto che stavo mescolando la firma e il messaggio. Una volta scambiati quei due, tutto ha funzionato perfettamente! E grazie per avermi corretto sulla terminologia. Se non è chiaro, non ho la più ferma comprensione di tutti i concetti alla base di ciò che sta accadendo nell'attuazione di questo schema. Le tue definizioni hanno reso le cose molto più chiare per me :) –

Problemi correlati