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;
- ein öffentliches/privates Schlüsselpaar erzeugte in Server mit angegebener Domäne Parametern (etwa
server_public_key
,server_private_key
) server_public_key
wird in IOS app in Form eineineEC_POINT
X und Y- zum Laufe eingebettet Zeit iOS App, ihre eigenen öffentlichen/privaten Schlüsselpaar erzeugt (zB
client_key_curve
, die einEC_KEY
ist), und - dann
server_public_key
und berechnet lädt shared secret (key_agreement
) basierend auf server_public_key und client_key_curve und - dann
client_public_key
sowie eine chiffrierten Nachricht (vonclient_key_curve
extrahierte), die symmetrisch die gemeinsames Geheimnis abgeleitet wird verschlüsselt (key_agreement
) ist - 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 - 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
Ist das gemeinsame Geheimnis der auf beiden Seiten gleich berechnet werden? –
@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
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. –