2013-03-15 12 views
5

Sto cercando di implementare la crittografia AES tra un'app per iOS e un servlet Java. Il servlet Java utilizza la libreria BouncyCastle mentre l'app iOS utilizza OpenSSL. Sebbene io abbia usato la stessa coppia di chiavi pubblica/privata e i parametri di dominio per entrambi i lati, il segreto condiviso generato da OpenSSL a volte differisce da ciò che genera BouncyCastle sul lato server.ECDH I segreti condivisi derivati ​​da OpenSSL e BouncyCastle non sono sempre gli stessi sebbene costanti e parametri di dominio siano uguali per entrambi

La procedura è la seguente;

  1. una coppia pubblica/privata chiave generata in server con specificato dominio parametri (per esempio server_public_key, server_private_key)
  2. server_public_key è incorporato nel app iOS in forma di EC_POINT X e Y
  3. a run- tempo iOS genera il/coppia di chiavi privata proprio pubblico (per esempio client_key_curve che è un EC_KEY), e
  4. quindi carica server_public_key e calcola segreto condiviso (key_agreement) sulla base di server_public_key e client_key_curve, e
  5. poi client_public_key (estratto da client_key_curve), così come un messaggio cifrato che è codificato in modo simmetrico usando il derivato segreto (key_agreement) condiviso vengono inviate al server
  6. server - nuovo lato, segreto condiviso è calcolato utilizzando client_public_key e ECDH server di parametri, che sono gli stessi lato cliente, e
  7. messaggio cifrato poi decriptato utilizzando computerizzata key_agreement

MA i messaggi decrittografati non sono sempre uguali ai messaggi inviati dal cliente.

Da quando ho sviluppato un'app per Android che utilizza la stessa procedura ma impiega BouncyCastle per la crittografia, quindi sospetto la correttezza del codice implementato usando OpenSSL, quindi il codice viene rivelato qui per altri per aiutare a risolvere il problema. Quello che ho implementato per calcolare il segreto condiviso è la seguente

- (void)calculateSharedSecret 
{ 
    BN_CTX* bn_ctx; 

    EC_KEY*  client_key_curve = NULL; 
    EC_KEY*  server_key_curve = NULL; 
    EC_GROUP* client_key_group = NULL; 
    EC_GROUP* server_key_group = NULL; 
    EC_POINT* client_publicKey = NULL; 
    EC_POINT* server_publicKey = NULL; 
    BIGNUM*  client_privatKey = NULL; 

    BIGNUM* client_publicK_x = NULL; 
    BIGNUM* client_publicK_y = NULL; 
    BIGNUM* server_publicK_x = NULL; 
    BIGNUM* server_publicK_y = NULL; 

    NSException *p = [NSException exceptionWithName:@"" reason:@"" userInfo:nil]; 

    bn_ctx = BN_CTX_new(); 
    BN_CTX_start(bn_ctx); 

    client_publicK_x = BN_CTX_get(bn_ctx); 
    client_publicK_y = BN_CTX_get(bn_ctx); 
    client_privatKey = BN_CTX_get(bn_ctx); 
    server_publicK_x = BN_CTX_get(bn_ctx); 
    server_publicK_y = BN_CTX_get(bn_ctx); 

    // client 

    if ((client_key_curve = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) 
     @throw p; 

    if ((client_key_group = (EC_GROUP *)EC_KEY_get0_group(client_key_curve)) == NULL) 
     @throw p; 

    if (EC_KEY_generate_key(client_key_curve) != 1) 
     @throw p; 

    if ((client_publicKey = (EC_POINT *)EC_KEY_get0_public_key(client_key_curve)) == NULL) 
     @throw p; 

    if (EC_KEY_check_key(client_key_curve) != 1) 
     @throw p; 

    client_privatKey = (BIGNUM *)EC_KEY_get0_private_key(client_key_curve); 

    char *client_public_key = EC_POINT_point2hex(client_key_group, client_publicKey, POINT_CONVERSION_COMPRESSED, bn_ctx); 
    char *client_privat_key = BN_bn2hex(client_privatKey); 

    _clientPublicKey = [NSString stringWithCString:client_public_key encoding:NSUTF8StringEncoding]; 

    // server 

    NSArray* lines = [self loadServerPublicKeyXY]; 

    NSString *public_str_x = [lines objectAtIndex:0]; 
    NSString *public_str_y = [lines objectAtIndex:1]; 

    BN_dec2bn(&server_publicK_x, [public_str_x UTF8String]); 
    BN_dec2bn(&server_publicK_y, [public_str_y UTF8String]); 

    if ((server_key_curve = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) 
     @throw p; 

    if ((server_key_group = (EC_GROUP *)EC_KEY_get0_group(server_key_curve)) == NULL) 
     @throw p; 

    if (EC_KEY_generate_key(server_key_curve) != 1) 
     @throw p; 

    if ((server_publicKey = EC_POINT_new(server_key_group)) == NULL) 
     @throw p; 

    if (EC_POINT_set_affine_coordinates_GFp(server_key_group, server_publicKey, server_publicK_x, server_publicK_y, bn_ctx) != 1) 
     @throw p; 

    if (EC_KEY_check_key(server_key_curve) != 1) 
     @throw p; 

    unsigned char *key_agreement = NULL; 
    key_agreement = (unsigned char *)OPENSSL_malloc(SHA_DIGEST_LENGTH); 
    if (ECDH_compute_key(key_agreement, SHA_DIGEST_LENGTH, server_publicKey, client_key_curve, KDF1_SHA1) == 0) 
     @throw p; 
    _symmetricKey = [NSData dataWithBytes:key_agreement length:16]; 
} 

e

void *KDF1_SHA1(const void *input, size_t inlen, void *output, size_t *outlen) 
{ 
    if (*outlen < SHA_DIGEST_LENGTH) 
     return NULL; 
    else 
     *outlen = SHA_DIGEST_LENGTH; 
    return SHA1(input, inlen, output); 
} 

_clientPublicKey e _symmetricKey sono dichiarate a livello di classe

La stessa curva (denominata prime256v1 o secp256r1) è utilizzato su entrambi i lati ma i risultati non sono sempre gli stessi.

EDIT 1: In risposta a @PeterDettman, ho pubblicato assistente - codice lato per ulteriori chiarimenti

public byte[] generateAESSymmetricKey(byte[] client_public_key_hex) throws InvalidRequest{ 
    try { 
     // ECDH Private Key as well as other prime256v1 params was generated by Java "keytool" and stored in a JKS file 
     KeyStore keyStore = ...; 
     PrivateKey privateKey = (PrivateKey) keyStore.getKey("keyAlias", "keyStorePassword".toCharArray()); 
     ECPrivateKeyParameters ecdhPrivateKeyParameters = (ECPrivateKeyParameters) (PrivateKeyFactory.createKey(privateKey.getEncoded())); 

     ECCurve ecCurve = ecdhPrivateKeyParameters.getParameters().getCurve(); 
     ECDomainParameters ecDomainParameters = ecdhPrivateKeyParameters.getParameters(); 
     ECPublicKeyParameters client_public_key = new ECPublicKeyParameters(ecCurve.decodePoint(client_public_key_hex), ecDomainParameters); 

     BasicAgreement agree = new ECDHBasicAgreement(); 
     agree.init(ecdhPrivateKeyParameters); 
     byte[] keyAgreement = agree.calculateAgreement(client_public_key).toByteArray(); 

     SHA1Digest sha1Digest = new SHA1Digest(); 
     sha1Digest.update(keyAgreement, 0, keyAgreement.length); 
     byte hashKeyAgreement[] = new byte[sha1Digest.getDigestSize()]; 
     sha1Digest.doFinal(hashKeyAgreement, 0); 

     byte[] server_calculatd_symmetric_key = new byte[16]; 
     System.arraycopy(hashKeyAgreement, 0, server_calculatd_symmetric_key, 0, server_calculatd_symmetric_key.length); 
     return server_calculatd_symmetric_key; 
    } catch (Throwable ignored) { 
     return null; 
    } 
} 

dove client_public_key_hex è client_public_key che viene convertito in un array di byte. Il risultato atteso è che server_calculatd_symmetric_key è uguale a symmetricKey per tutto il tempo. MA non sono sempre gli stessi.

EDIT 2: come un feedback alla risposta @PeterDettman, ho fatto alcune modifiche per riflettere il suo suggerimento e sebbene il tasso di disuguaglianza riduce, generato accordi chiave (segreto condiviso) su entrambi i lati non sono ancora uguali in tutti i casi .

E 'possibile riprodurre uno dei casi la disuguaglianza con i seguenti dati

  • chiave pubblica: 02E05C058C3DF6E8D63791660D9C5EA98B5A0822AB93339B0B8815322131119C4C
  • Privat chiave: 062E8AC930BD6009CF929E51B37432498075D21C335BD00086BF68CE09933ACA
  • Generated segreto condiviso da OpenSSL: 51d027264f8540e5d0fde70000000000
  • generata segreto condiviso da BouncyCastle: 51d027264f8540e5d0fde700e5db0fab

Quindi non v'è alcun errore nel codice implementato o la procedura?

Grazie

+0

Il segreto condiviso viene calcolato lo stesso su entrambi i lati? –

+0

@PeterDettman: Talvolta il Segreto condiviso calcolato su entrambi i lati è uguale ea volte no (direi che la maggior parte delle volte non sono uguali). Non appena cambio il tipo di curva EC da ** prime256v1 - secp256r1 ** a ** prime192v1 ** il tasso di ineguaglianza del segreto condiviso calcolato si riduce. Voglio dire che hanno più probabilità di essere uguali sulla curva ** prime192v1 ** e non ho idea del perché sia ​​così. – anonim

+0

Forse potresti mostrare il codice servlet che usa BouncyCastle, in particolare la parte che prende il valore dell'accordo grezzo e ne ricava la chiave simmetrica. –

risposta

3

C'è un problema nel codice del server, in modo che il valore dell'accordo ECDH viene convertito in byte:

byte[] keyAgreement = agree.calculateAgreement(client_public_key).toByteArray(); 

Prova a modificare:

BigInteger agreementValue = agree.calculateAgreement(client_public_key); 
byte[] keyAgreement = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), agreementValue); 

Questa volontà assicurare un array di byte a dimensione fissa come output, che è un requisito per la conversione di elementi di campo EC in stringhe di ottetto (ricerca "Elemento di campo in primordione conversione stringa di ottetti" per ulteriori d etails).

Si consiglia di ignorare la parte di derivazione del tasto SHA1 finché non si ottiene che l'array di byte KeyAgreement Java corrisponda esattamente all'ingresso della funzione KDF1_SHA1.

+0

Gentile @PeterDettman, ho seguito il tuo suggerimento e il feedback è stato aggiunto alla domanda. Lo controlleresti per favore? – anonim

+0

In realtà ho ignorato applicando SHA sulla calcolato chiave accordo su entrambi i lati in base al vostro suggerimento però a volte sono completamente diversi – anonim

+0

Si prega di segnalare ciò * Ingresso * viene inviato al KDF1_SHA1 in app iOS, e il corrispondente valore del KeyAgreement sul Java lato. –