I know that this question has already been asked, but it seems I can't write the code to make it work.
Lets say that we have the following keypair:
Public Key (this is what I want to generate given the private key - base64 encoded string as Public key to use for Android Pay): BKmbVIGwZw3TUITg1NIppTrWXCGuXt1YdfAnY0ToqYaNzxdkY5ZUNO0WN8Q35rcnrQavsAfRtWYKKoXAcZ7MOVQ=
Private Key (this is known):
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTA/wqrlbeVddorTlaT1AqhALrIBwS+DUdV3N1K1gImqhRANCAASpm1SBsGcN01CE4NTSKaU61lwhrl7dWHXwJ2NE6KmGjc8XZGOWVDTtFjfEN+a3J60Gr7AH0bVmCiqFwHGezDlU
-----END PRIVATE KEY-----
Here is the code that I use to generate the java.security.PrivateKey:
private static final Logger logger = LoggerFactory.getLogger(AndroidPayDecodingUtils.class);
private static final String SECURITY_PROVIDER = "BC";
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static final String KEY_AGREEMENT_ALGORITHM = "ECDH";
// OpenSSL name of the NIST P-126 Elliptic Curve
private static final String EC_CURVE = "prime256v1";
private static final String SYMMETRIC_KEY_TYPE = "AES";
private static final String SYMMETRIC_ALGORITHM = "AES/CTR/NoPadding";
private static final byte[] SYMMETRIC_IV = Hex.decode("00000000000000000000000000000000");
private static final int SYMMETRIC_KEY_BYTE_COUNT = 16;
private static final String MAC_ALGORITHM = "HmacSHA256";
private static final int MAC_KEY_BYTE_COUNT = 16;
private static final byte[] HKDF_INFO = "Android".getBytes(DEFAULT_CHARSET);
private static final byte[] HKDF_SALT = null /* equivalent to a zeroed salt of hashLen */;
public static final String EC_ASYMMETRIC_KEY_TYPE = "EC";
public static PrivateKey generatePrivateKey(String privateKey) {
try {
String pkcs8Pem = formatKey(privateKey);
// Base64 decode the result
byte[] pkcs8EncodedBytes = BaseEncoding.base64().decode(pkcs8Pem);
// extract the private key
KeyFactory asymmetricKeyFactory = KeyFactory.getInstance(EC_ASYMMETRIC_KEY_TYPE, SECURITY_PROVIDER);
return asymmetricKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedBytes));
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | NullPointerException e) {
logger.error("Failed to create private key");
}
return null;
}
private static String formatKey(String key) {
try {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(key));
String line;
while ((line = rdr.readLine()) != null) {
stringBuilder.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String toReturn = stringBuilder.toString();
// unix generated EC private key
toReturn = toReturn.replace("-----BEGIN PRIVATE KEY-----", "");
toReturn = toReturn.replace("-----END PRIVATE KEY-----", "");
// mac generated EC private key
toReturn = toReturn.replace("-----BEGIN EC PRIVATE KEY-----", "");
toReturn = toReturn.replace("-----END EC PRIVATE KEY-----", "");
//whitespace
toReturn = toReturn.replaceAll("\\s+", "");
return toReturn;
} catch (IOException | NullPointerException e) {
logger.error("Failed to read the key");
}
return null;
}
And this is the code that I use to generate the public key as Base64 encoded string, but it seems that I doing something wrong as I am not getting the desired result:
public static String generatePublicKeyFromPrivateKey(String privateKeyStr) {
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(EC_CURVE);
ECDomainParameters ecDomainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
parameterSpec.getN(), parameterSpec.getH(), parameterSpec.getSeed());
ECPrivateKey privateKey = (ECPrivateKey) generatePrivateKey(privateKeyStr);
byte[] privateKeyBytes = privateKey.getS().toByteArray();
ECPoint ecPoint = ecDomainParameters.getG().multiply(new BigInteger(privateKeyBytes));
String toReturn = new String(Base64.encode(ecPoint.getEncoded(true)));
return toReturn;
}