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;
- una coppia pubblica/privata chiave generata in server con specificato dominio parametri (per esempio
server_public_key
,server_private_key
) server_public_key
è incorporato nel app iOS in forma diEC_POINT
X e Y- a run- tempo iOS genera il/coppia di chiavi privata proprio pubblico (per esempio
client_key_curve
che è unEC_KEY
), e - quindi carica
server_public_key
e calcola segreto condiviso (key_agreement
) sulla base di server_public_key e client_key_curve, e - poi
client_public_key
(estratto daclient_key_curve
), così come un messaggio cifrato che è codificato in modo simmetrico usando il derivato segreto (key_agreement
) condiviso vengono inviate al server - server - nuovo lato, segreto condiviso è calcolato utilizzando
client_public_key
e ECDH server di parametri, che sono gli stessi lato cliente, e - 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
Il segreto condiviso viene calcolato lo stesso su entrambi i lati? –
@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
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. –