ECDH The shared secret obtained from OpenSSL and BouncyCastle is not always the same, although the constants and domain parameters are the same for both

I am trying to implement AES cryptography between an iOS application and a Java servo. The Java servlet uses the BouncyCastle library, while the iOS application uses OpenSSL. Although I used the same pair and domain parameters for the public and private keys for both sides, the shared secret generated by OpenSSL is sometimes different from what the BouncyCastle generated on the server side.

The procedure is as follows:

  • public / private key pair generated on the server with the specified domain parameters (say server_public_key, server_private_key)
  • server_public_keyembedded in iOS app in the form of EC_POINTX and Y
  • during application execution, iOS generates its own public / private key pair (say client_key_curve, which is EC_KEY), and
  • then loads server_public_keyand calculates the shared secret ( key_agreement) based on server_public_key and client_key_curve and
  • then client_public_key(extracted from client_key_curve), as well as an encrypted message that is encrypted symmetrically using ( key_agreement) sent to the server
  • on the server side, the shared secret is again calculated using client_public_keyboth the ECDH server parameters, which are the same as the client side and
  • then the encrypted message is decrypted using the calculated key_agreement

BUT, decrypted messages do not always match messages sent by the client.

Android, , BouncyCastle , OpenSSL, , . , ,

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

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 _symmetricKey

( prime256v1 secp256r1) , .

1: @PeterDettman Ive

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

client_public_key_hex - client_public_key, . : server_calculatd_symmetric_key symmetricKey . .

EDIT 2: @PeterDettman , , , ( ) .

  • : 02E05C058C3DF6E8D63791660D9C5EA98B5A0822AB93339B0B8815322131119C4C
  • : 062E8AC930BD6009CF929E51B37432498075D21C335BD00086BF68CE09933ACA
  • OpenSSL: 51d027264f8540e5d0fde70000000000
  • BouncyCastle: 51d027264f8540e5d0fde700e5db0fab

?

+5
1

, , ECDH :

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

:

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

, EC ( " - " ).

SHA1, Java keyAgreement, KDF1_SHA1.

+3

All Articles