How to genernate a ECIES function in Java?

60 views Asked by At

I am busy in a decryption of encrypted data work.

Another business provider has provided me with a Private_key.pem file, and then I should use it to decrypt the encrypted data they provided.

They have provided a demo coding in Ruby to help me do this. It works well in Ruby, but when I want to make it in Java, I find that there is a lot of difficulties.

The demo in Ruby:

def decrypt(key, encrypted_data)
  # Decode the Base64 encoded `encrypted_message` and `tag`
  ciphertext = Base64.strict_decode64(encrypted_data[:encrypted_message])
  mac = Base64.strict_decode64(encrypted_data[:tag])

  # Compute shared secret using the private key of the certificate and `ephemeral_public_key`
  ephemeral_public_key_pem = encrypted_data[:ephemeral_public_key]
  ephemeral_public_key = OpenSSL::PKey::EC.new(ephemeral_public_key_pem).public_key
  shared_secret = key.dh_compute_key(ephemeral_public_key)

  # Compute the hmac of decoded `encrypted_message` and ensure it matches the decoded value of `tag`
  cipher = OpenSSL::Cipher.new('AES-256-CTR')
  mac_digest = OpenSSL::Digest.new('SHA256')
  mac_length = mac_digest.digest_length / 2
  key_pair = OpenSSL::KDF.hkdf(
    shared_secret,
    length: cipher.key_len + mac_length,
    hash: 'SHA256',
    salt: '',
    info: ''
  )
  cipher_key = key_pair.byteslice(0, cipher.key_len)
  hmac_key = key_pair.byteslice(-mac_length, mac_length)
  computed_mac = OpenSSL::HMAC.digest(mac_digest, hmac_key, ciphertext).byteslice(0, mac_length)
  raise OpenSSL::PKey::ECError, "Invalid Message Authenticaton Code" unless OpenSSL.secure_compare(computed_mac, mac)

  # Decrypt `encrypted_message`
  cipher.decrypt
  cipher.iv = ("\x00" * 16).force_encoding(Encoding::BINARY)
  cipher.key = cipher_key
  cipher.update(ciphertext) + cipher.final
end

# the encrypted data to be decrypted
encrypted_data = {
  encrypted_message: "7oeGaIk3Tja5fkyB3P[...]urLyxgW4J9lu2x769+",
  ephemeral_public_key: "-----BEGIN PUBLIC KEY-----\nMEB3wYo[...]HKFkwjQ==\n-----END PUBLIC KEY-----\n",
  tag: "LDCZ1tbHX8t+KPO9w=="
}
# Use `fingerprint` to identify the certificate that's used for encryption
key = OpenSSL::PKey::EC.new(File.open('path/to/private-key.file').read)
# decrypt
decrypt(key, encrypted_data)

One of the difficulties is I can't read the private_key.pem properly. The code below:

public static KeyFactory keyFactory;
    static {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
        try {
            keyFactory = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
        }
    }

    public static PrivateKey getPrivateKey(String filePath) throws Exception {
        String privateKeyPEM = new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8);
        String privateKeyPEMTrimmed = privateKeyPEM
                .replace("-----BEGIN EC PRIVATE KEY-----", "")
                .replace("-----END EC PRIVATE KEY-----", "")
                .replaceAll("\n", "");
        byte[] privateKeyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(privateKeyPEMTrimmed)
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

The private_key.pem file looks like:

-----BEGIN EC PRIVATE KEY-----
MHADASDAS265546XCADAP8zAOJPkW7OXTqotoAoGCCqGSM49
AadADASDASDCLKJOI45646asdcasDASDASD122165165D4sdasdsadqDASFAGFAADy
Aufttttttttttttttttttttw==
-----END EC PRIVATE KEY-----

When I run this code, it returns an error:

java.security.spec.InvalidKeySpecException: encoded key spec not recognized: unknown object in getInstance: org.bouncycastle.asn1.DEROctetString
    at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePrivate(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePrivate(Unknown Source)

I have searched methods and one on them is changing the private_key.file to PKCS#8 format format in OpenSSL, I do so and it works without error in this code. However, the result is not equal to tag of the encrypted_data in the decrypt function.

The decrypt function is:

public static byte[] decrypt(Key key, ShopifyCreditCardMethodData encryptedPayload) throws Exception {
    // Decode the Base64 encoded `encrypted_message` and `tag`
    byte[] ciphertext = Base64.getDecoder().decode(encryptedPayload.getEncrypted_message());
    byte[] mac = Base64.getDecoder().decode(encryptedPayload.getTag());

    // Compute shared secret using the private key of the certificate and `ephemeral_public_key`
    String s = encryptedPayload.getEphemeral_public_key().replace("-----BEGIN PUBLIC KEY-----", "")
            .replace("-----END PUBLIC KEY-----", "")
            .replaceAll("\\n", "");
    byte[] ephemeralPublicKeyBytes = Base64.getDecoder().decode(s);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(ephemeralPublicKeyBytes);
    PublicKey ephemeralPublicKey = keyFactory.generatePublic(keySpec);
    KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
    keyAgreement.init((PrivateKey) key);
    keyAgreement.doPhase(ephemeralPublicKey, true);
    byte[] sharedSecret = keyAgreement.generateSecret();

    // Compute the hmac of decoded `encrypted_message` and ensure it matches the decoded value of `tag`
    Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
    MessageDigest macDigest = MessageDigest.getInstance("SHA-256", BouncyCastleProvider.PROVIDER_NAME);
    int macLength = macDigest.getDigestLength() / 2;
    byte[] keyPair = hkdf(sharedSecret, cipher.getBlockSize() + macLength, "HmacSHA256");
    byte[] cipherKey = Arrays.copyOfRange(keyPair, 0, cipher.getBlockSize());
    byte[] hmacKey = Arrays.copyOfRange(keyPair, keyPair.length - macLength, keyPair.length);
    Mac hmac = Mac.getInstance("HmacSHA256");
    hmac.init(new SecretKeySpec(hmacKey, "HmacSHA256"));
    byte[] computedMac = Arrays.copyOf(hmac.doFinal(ciphertext), macLength);
    if (!MessageDigest.isEqual(computedMac, mac)) {
        throw new Exception("Invalid Message Authenticaton Code");
    }

    // Decrypt `encrypted_message`
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(new byte[cipher.getBlockSize()]));
    return cipher.doFinal(ciphertext);
}

public static byte[] hkdf(byte[] ikm, int length, String hashAlgorithm, byte[] salt, byte[] info) throws Exception {
    Mac hmac = Mac.getInstance(hashAlgorithm);
    hmac.init(new SecretKeySpec(salt != null ? salt : new byte[hmac.getMacLength()], hashAlgorithm));
    byte[] prk = hmac.doFinal(ikm);

    byte[] t = new byte[0];
    byte[] okm = new byte[length];
    for (int i = 0; i < Math.ceil((double) length / hmac.getMacLength()); i++) {
        hmac.init(new SecretKeySpec(prk, hashAlgorithm));
        hmac.update(t);
        hmac.update(info);
        hmac.update((byte) (i + 1));
        t = hmac.doFinal();

        System.arraycopy(t, 0, okm, i * hmac.getMacLength(), Math.min(hmac.getMacLength(), length - i * hmac.getMacLength()));
    }

    return okm;
}

public static byte[] hkdf(byte[] ikm, int length, String hashAlgorithm) throws Exception {
    return hkdf(ikm, length, hashAlgorithm, null, new byte[0]);
}

I am not familiar with the asymmetric encryption system, and I try me best, but I can't find questions in my Java code with the Ruby code.

0

There are 0 answers