2013-03-15 23 views
5

Ich versuche AES-Kryptographie zwischen einer iOS-App und einem Java-Servlet zu implementieren. Java Servlet verwendet die BouncyCastle-Bibliothek, während die iOS-Anwendung OpenSSL verwendet. Obwohl ich für beide Seiten dieselben öffentlichen/privaten Schlüsselpaare und Domänenparameter verwendet habe, unterscheidet sich das von OpenSSL generierte Shared Secret manchmal von dem, was auf der Serverseite von BouncyCastle generiert wird.ECDH Shared Secret von OpenSSL und BouncyCastle sind nicht immer gleich, obwohl Konstanten und Domain-Parameter für beide gleich sind

Das Verfahren ist wie folgt;

  1. ein öffentliches/privates Schlüsselpaar erzeugte in Server mit angegebener Domäne Parametern (etwa server_public_key, server_private_key)
  2. server_public_key wird in IOS app in Form eineine EC_POINT X und Y
  3. zum Laufe eingebettet Zeit iOS App, ihre eigenen öffentlichen/privaten Schlüsselpaar erzeugt (zB client_key_curve, die ein EC_KEY ist), und
  4. dann server_public_key und berechnet lädt shared secret (key_agreement) basierend auf server_public_key und client_key_curve und
  5. dann client_public_key sowie eine chiffrierten Nachricht (von client_key_curve extrahierte), die symmetrisch die gemeinsames Geheimnis abgeleitet wird verschlüsselt (key_agreement) ist
  6. im Server zu Server gesendet - Seite wieder gemeinsames Geheimnis verwendet, berechnet client_public_key und Server ECDH Parameter, die die gleichen wie Client-Seite sind, und
  7. dann chiffrierten Nachricht berechnet entschlüsselte unter Verwendung key_agreement

ABER die entschlüsselten Nachrichten sind nicht immer die gleichen wie der Client Nachrichten gesendet.

Da ich auch eine Android-App entwickelt habe, die das gleiche Verfahren verwendet, aber BouncyCastle für Kryptographie verwendet, vermute ich die Korrektheit von implementiertem Code mit OpenSSL, so dass der Code hier für andere zur Verfügung gestellt wird, um das Problem zu lösen. Was ich umgesetzt habe das gemeinsame Geheimnis zu berechnen, ist wie folgt

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

und

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 und _symmetricKey sind in den Klassen erklären

Die gleiche Kurve (benannt prime256v1 oder secp256r1) ist auf beiden Seiten verwendet, aber die Ergebnisse sind nicht immer gleich.

EDIT 1: Als Reaktion auf @PeterDettman, ich habe Server veröffentlicht - Seite Code für weitere Klärung

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

wo client_public_key_hex ist client_public_key, die auf ein Array von Byte umgewandelt wird. Das erwartete Ergebnis ist, dass server_calculatd_symmetric_key gleich symmetricKey für die ganze Zeit ist. Aber sie sind nicht immer gleich.

EDIT 2: Als Feedback zu @PeterDettman Antwort, habe ich einige Änderungen seinen Vorschlag zu reflektieren und obwohl Rate von Ungleichheit reduziert, wesentliche Vereinbarungen erzeugt (Shared Secret) auf beiden Seiten ist noch nicht gleich in allen Fällen .

Es ist möglich, eine Ungleichheits Fall zu reproduzieren mit folgenden Daten

  • Öffentlicher Schlüssel: 02E05C058C3DF6E8D63791660D9C5EA98B5A0822AB93339B0B8815322131119C4C
  • Privat-Schlüssel: 062E8AC930BD6009CF929E51B37432498075D21C335BD00086BF68CE09933ACA
  • generiert Shared Secret von OpenSSL: 51d027264f8540e5d0fde70000000000
  • generiert Shared Secret von BouncyCastle: 51d027264f8540e5d0fde700e5db0fab

So gibt es einen Fehler in der implementierten Code oder Verfahren?

Dank

+0

Ist das gemeinsame Geheimnis der auf beiden Seiten gleich berechnet werden? –

+0

@PeterDettman: Manchmal berechnete Shared Secret auf beiden Seiten sind gleich und manchmal nicht (ich würde besser sagen, die meiste Zeit sind sie nicht gleich). Sobald ich den Typ der EC-Kurve von ** prime256v1 - secp256r1 ** zu ** prime192v1 ** ändere, reduziert sich die Ungleichheitsrate des berechneten gemeinsamen Geheimnisses. Ich meine, sie sind eher auf der ** prime192v1 ** Kurve gleich und ich habe keine Ahnung warum es so ist. – anonim

+0

Vielleicht könnten Sie den Servlet-Code anzeigen, der BouncyCastle verwendet, insbesondere den Teil, der den Rohwert der Vereinbarung annimmt und daraus den symmetrischen Schlüssel ableitet. –

Antwort

3

ein Problem in dem Server-Code Es gibt in der Art und Weise, dass der ECDH Vereinbarung Wert Bytes umgewandelt wird:

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

dies stattdessen versuchen:

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

Dieser Wille stellen Sie ein Bytearray fester Größe als Ausgabe sicher, das eine Voraussetzung für das Konvertieren von EC-Feldelementen in Oktettstrings ist (Suchen Sie nach "Feldelement zu Oktettstringkonvertierungsgrundelement" für mehr d Details).

Ich empfehle Ihnen, den SHA1-Schlüsselableitungsteil zu ignorieren, bis Sie dieses Java-keyAgreement-Bytearray genau mit dem Eingang Ihrer KDF1_SHA1-Funktion abgleichen können.

+0

Sehr geehrte @ Peter Dettman, ich folgte Ihrem Vorschlag und das Feedback wurde an die Frage angehängt. Würdest du es bitte überprüfen? – anonim

+0

In der Tat ignorierte ich SHA auf berechnete Schlüssel Vereinbarung auf beiden Seiten nach Ihrem Vorschlag, aber manchmal sind sie völlig unterschiedlich – anonim

+0

Bitte melden Sie, was * Eingabe * zu KDF1_SHA1 in der iOS App gesendet wird, und den entsprechenden Wert von keyAgreement auf dem Java Seite. –

Verwandte Themen