Search code examples
androidcryptographybouncycastle

Generating ECPublicKey from byte array


I get a 64 uncompressed public key and need to run ECDH to generate a shared secret. In order to call ECDH I need to convert the byte array to PublicKey and I am using the following code I have found in this forum:

public static void setOtherPublicKey(byte[] publicKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException
{       

    try {
        //EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        KeyFactory generator = KeyFactory.getInstance("EC");
        //PrivateKey privateKey = generator.generatePrivate(privateKeySpec);

        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        blePubKey = generator.generatePublic(publicKeySpec);
    } catch (Exception e) {
        throw new IllegalArgumentException("Failed to create KeyPair from provided encoded keys", e);
    }
}

This code throws an InvalidKeySpecException.

As example, the public key of the other party is:

9b5e9a5a971877530c9cadbbea93c2ee2483d65052678f745bad79f110173520
54019832e11376537a76c4defd0b3dfdc667a974239147f323cdcfd2baa39892

Adding the code after getting the answers below:

public static void setOtherPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException
{
    // first generate key pair of your own   
    ECPublicKey pubKey = (ECPublicKey) SecPage.g_kpA.getPublic();
    ECParameterSpec params = pubKey.getParams();
    int keySizeBytes = params.getOrder().bitLength() / Byte.SIZE;

    // get the other party 64 bytes
    //byte [] otherPub = crypto.getBlePubKeyBytes();

    byte[] otherPub = hexStringToByteArray("ac2bdd28fce5c7b181b34f098b0934742281246ed907a5f646940c1edcb724e7c7358356aebea810322a8e324cc77f376df4cabd754110ad41ec178c0a6b8e5f");
    ByteArrayBuffer xBytes = new ByteArrayBuffer(33);
    ByteArrayBuffer yBytes = new ByteArrayBuffer(33);

    byte[] zero = {(byte)0x00};
    xBytes.append(zero, 0, 1);
    xBytes.append(otherPub, 0, 32);
    yBytes.append(zero, 0, 1);
    yBytes.append(otherPub, 32, 32);


    // generate the public key point    
    BigInteger x = new BigInteger(xBytes.buffer());
    BigInteger y = new BigInteger(yBytes.buffer());

    ECPoint w  = new ECPoint(x, y);

    // generate the key of the other side
    ECPublicKeySpec otherKeySpec = new ECPublicKeySpec(w  , params);
    KeyFactory keyFactory = KeyFactory.getInstance("EC");
    blePubKey = (ECPublicKey) keyFactory.generatePublic(otherKeySpec);
}

Solution

  • Well, whaty'know, you can actually do this... explanation in the comments.

    public class ECDHPub {
    
        private static ECPublicKey decodeECPublicKey(ECParameterSpec params,
                final byte[] pubkey) throws NoSuchAlgorithmException,
                InvalidKeySpecException {
            int keySizeBytes = params.getOrder().bitLength() / Byte.SIZE;
    
            int offset = 0;
            BigInteger x = new BigInteger(1, Arrays.copyOfRange(pubkey, offset,
                    offset + keySizeBytes));
            offset += keySizeBytes;
            BigInteger y = new BigInteger(1, Arrays.copyOfRange(pubkey, offset,
                    offset + keySizeBytes));
            ECPoint w = new ECPoint(x, y);
    
            ECPublicKeySpec otherKeySpec = new ECPublicKeySpec(w, params);
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            ECPublicKey otherKey = (ECPublicKey) keyFactory
                    .generatePublic(otherKeySpec);
            return otherKey;
        }
    
        private static byte[] encodeECPublicKey(ECPublicKey pubKey) {
            int keyLengthBytes = pubKey.getParams().getOrder().bitLength()
                    / Byte.SIZE;
            byte[] publicKeyEncoded = new byte[2 * keyLengthBytes];
    
            int offset = 0;
    
            BigInteger x = pubKey.getW().getAffineX();
            byte[] xba = x.toByteArray();
            if (xba.length > keyLengthBytes + 1 || xba.length == keyLengthBytes + 1
                    && xba[0] != 0) {
                throw new IllegalStateException(
                        "X coordinate of EC public key has wrong size");
            }
    
            if (xba.length == keyLengthBytes + 1) {
                System.arraycopy(xba, 1, publicKeyEncoded, offset, keyLengthBytes);
            } else {
                System.arraycopy(xba, 0, publicKeyEncoded, offset + keyLengthBytes
                        - xba.length, xba.length);
            }
            offset += keyLengthBytes;
    
            BigInteger y = pubKey.getW().getAffineY();
            byte[] yba = y.toByteArray();
            if (yba.length > keyLengthBytes + 1 || yba.length == keyLengthBytes + 1
                    && yba[0] != 0) {
                throw new IllegalStateException(
                        "Y coordinate of EC public key has wrong size");
            }
    
            if (yba.length == keyLengthBytes + 1) {
                System.arraycopy(yba, 1, publicKeyEncoded, offset, keyLengthBytes);
            } else {
                System.arraycopy(yba, 0, publicKeyEncoded, offset + keyLengthBytes
                        - yba.length, yba.length);
            }
    
            return publicKeyEncoded;
        }
    
        public static void main(String[] args) throws Exception {
    
            // (only) required for named curves other than those used in JCE
            Security.addProvider(new BouncyCastleProvider());
    
            // create local and remote key
            KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", "BC");
            ECGenParameterSpec genspec = new ECGenParameterSpec("brainpoolp256r1");
            kpgen.initialize(genspec);
            KeyPair localKeyPair = kpgen.generateKeyPair();
            KeyPair remoteKeyPair = kpgen.generateKeyPair();
    
            // test generation
            byte[] encodedRemotePublicKey = encodeECPublicKey((ECPublicKey) remoteKeyPair
                    .getPublic());
            // test creation
            ECPublicKey remoteKey = decodeECPublicKey(
                    ((ECPublicKey) localKeyPair.getPublic()).getParams(),
                    encodedRemotePublicKey);
    
            // local key agreement
            KeyAgreement localKA = KeyAgreement.getInstance("ECDH");
            localKA.init(localKeyPair.getPrivate());
            localKA.doPhase(remoteKey, true);
            byte[] localSecret = localKA.generateSecret();
    
            // remote key agreement
            KeyAgreement remoteKA = KeyAgreement.getInstance("ECDH");
            remoteKA.init(remoteKeyPair.getPrivate());
            remoteKA.doPhase((ECPublicKey) localKeyPair.getPublic(), true);
            byte[] remoteSecret = localKA.generateSecret();
    
            // validation
            System.out.println(Arrays.equals(localSecret, remoteSecret));
        }
    }