0

I am given a 120 char ECDSA generated X509 public key by an external system. I now want to use it in Ethereum by converting it to an address.

(not the real key but an example of the content (120 chars)) MFYwEAYHKoZIzj0CAQYFK4EE123456789n9DSxZh3wfq0BIL5LDF5B54e07bxFiKc89K/GaKj4qrGC/Mb/KnakQBrN4khMQHLnxm7TjaxXQPxtJMV5b+A==

I can't see an easy way of doing this with web3j, perhaps there is another way? I think, looking at the tests, org.web3j.crypto.Keys.getAddress(String) expects the 130 character hex version.

How do I convert the 120 chars to a 130 char hex representation to allow me to call the getAddress method or maybe there is a direct way of converting the 120 char pub key to Ethereum address?

skword
  • 311
  • 2
  • 11
  • Your 120 chars are apparently BASE64 coded; they represent 90 bytes, which would be represented as 180 characters in hex. Too much to get to 130 hex chars. – Erich Kitzmueller May 05 '20 at 09:09
  • think something else has to happen first. Some sort of conversion on it. – skword May 05 '20 at 09:17
  • Start with decoding the base64-encoded string and check the length of the resulting byte array (your example actually does not decode correctly; with one more char, it would decode to 88 bytes). If the resulting length is 65, you probably just have to hex-encode it. If not, it's not really suitable to become a 130 char hex string without adding or losing information. – Erich Kitzmueller May 05 '20 at 10:46
  • ok so I get 88 bytes, when I decoded the actual key using Base64.getDecoder().decode(publicKey).length. So I guess its not suitable for an Ethererum address? – skword May 05 '20 at 12:17
  • According to the internets, you have to calculate a Keccak-256 hash of your public key, then take the rightmost 20 bytes of the hash and encode them as a hexadecimal string. Or so. Please double-check, I am not compentent in all that cryptocurrency stuff and I do not want you to accidentally send virtual money to a non-existant address, thus destroying it forever. – Erich Kitzmueller May 05 '20 at 12:32
  • Yeah, so Web3j will do that bit for me. But the tricky part is getting the X509 ECDSA key into something meaningful. I have no idea why its 88 bytes. – skword May 05 '20 at 14:51

1 Answers1

2

If you had a valid Base64 encoded SECP-256k1 public key, the following code would enable you to get the Ethereum address using Web3j.

String encodedKey = "MFYwEAYHKoZIzj0CAQYFK4EE123456789n9DSxZh3wfq0BIL5LDF5B54e07bxFiKc89K/GaKj4qrGC/Mb/KnakQBrN4khMQHLnxm7TjaxXQPxtJMV5b+A==";
byte[] decoded = Base64.getDecoder().decode(encodedKey);
String address = Keys.getAddress(Numeric.toHexString(decoded));

However, as mentioned in the comments, it appears that the input encoded key is not a 128 bit public key, but an X.509 cert. Hence you'll need to do something along these lines:

String encodedKey = "MFYwEAYHKoZIzj0CAQYFK4EE123456789n9DSxZh3wfq0BIL5LDF5B54e07bxFiKc89K/GaKj4qrGC/Mb/KnakQBrN4khMQHLnxm7TjaxXQPxtJMV5b+A==";

byte[] decoded = Base64.getDecoder().decode(encodedKey);

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(decoded));

// TODO: Figure out how to convert this PublicKey into a byte array or BigInteger
byte[] publicKey = cert.getPublicKey();

String address = Keys.getAddress(Hex.toHexString(publicKey));

It's also worth mentioning that the OpenSSL command line tool can be very helpful too for converting certificates into different formats.

Conor Svensson
  • 1,151
  • 11
  • 17
  • 1
    It's extremely unlikely an 88-byte value is a certificate; it is much more likely the SubjectPublicKeyInfo _part_ of a certificate as defined by X.509 and more conveniently available in rfc5280 4.1. This is what Java calls `X509EncodedKeySpec` (see the javadoc for `java.security.Key`) and OpenSSL calls `PUBKEY`. It has an outer DER SEQUENCE and (for EC) another SEQUENCE of two OIDs and a BIT STRING 'wrapper' prefixing the actual public-point value. – dave_thompson_085 May 05 '20 at 18:36
  • This example isn't a valid base 64 encoding either – Conor Svensson May 05 '20 at 18:56
  • The Q makes clear that the posted value was mangled to hide the true value -- by someone who doesn't understand and know how to preserve correct base64 format. But OP says in comment that decoding 'the actual key' is 88 bytes. And a _valid_ 120-char base64 ending with == would indeed decode to 88 bytes. – dave_thompson_085 May 06 '20 at 16:53