Android Pay:公共,私钥对生成(带NISTP-256的椭圆曲线)

Android Pay问题

在Android Pay中,从信用卡生成令牌的过程如下:

生成公钥和私钥(下面的调用使用带有NISTP-256算法的椭圆曲线返回键)

为此,我打电话给…

public static KeyPair generateKeyPair() { KeyPair pair =null; try { ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("prime256v1"); java.security.KeyPairGenerator g = KeyPairGenerator.getInstance("EC"); g.initialize(ecGenSpec, new SecureRandom()); pair = g.generateKeyPair(); pair.getPrivate(); pair.getPublic(); }catch (Throwable e ){ e.printStackTrace(); } return pair; } 

…这成功返回公钥和私钥,但我不确定密钥的格式/编码是什么。我找不到任何关于此的文档。

问题1:这是为Android Pay生成公钥和私钥的正确方法吗?

将base64编码格式的公钥传递给Android Pay createMaskedWalletRequet方法(详情请参阅Android Pay文档)

 String publicKey = String (Base64.encodeBase64(pair.getPublic().getEncoded())); PaymentMethodTokenizationParameters parameters = PaymentMethodTokenizationParameters.newBuilder().setPaymentMethodTokenizationType(PaymentMethodTokenizationType.NETWORK_TOKEN).addParameter("publicKey", publicKey).build(); 

在这里,我得到以下exception:

03-30 17:02:06.459 3786-15263 /? E / WalletClient:validationMaskedWalletRequest.paymentMethodTokenizationParameters时出错:参数“publicKey”的第一个字节必须是0x04(表示未压缩的点格式)

问题2:能帮助我理解我做错了什么。 我认为这可能与格式不匹配有关,但不确定,也不确定如何修复它。

感谢你的帮助!!

答案1:

根据Android Pay文档,您可以通过OpenSSL生成公钥,如下所示: https : //developers.google.com/android-pay/integration/gateway-processor-integration#example-using-openssl-to-generate-and-format -a-public-key 在此输入链接描述

然后生成私钥使用

 openssl pkcs8 -topk8 -inform PEM -outform PEM -in merchant-key.pem -nocrypt 

或者您可以使用shell脚本文件。(例如: Android中的 genkey.sh 支付快速入门 )
使用以下代码(将代码复制到.sh文件并双击),您可以获得私钥。

 #!/bin/bash # Generate key.pem file: openssl ecparam -name prime256v1 -genkey -noout -out key.pem # Print public and private key in hex form: openssl ec -in key.pem -text -noout openssl pkcs8 -topk8 -inform PEM -outform PEM -in key.pem -nocrypt sleep 2m 

然后在终端中复制pub字符串并替换此代码,然后保存此代码以再次创建.sh文件

 #!/bin/bash KEY="04:a9:9b:54:81:b0:67:0d:d3:50:84:e0:d4:d2:29: a5:3a:d6:5c:21:ae:5e:dd:58:75:f0:27:63:44:e8: a9:86:8d:cf:17:64:63:96:54:34:ed:16:37:c4:37: e6:b7:27:ad:06:af:b0:07:d1:b5:66:0a:2a:85:c0: 71:9e:cc:39:54" echo $KEY | xxd -r -p | base64 sleep 2m 

然后获取公钥。

答案2:您可以通过以下键进行测试:

公钥:BKmbVIGwZw3TUITg1NIppTrWXCGuXt1YdfAnY0ToqYaNzxdkY5ZUNO0WN8Q35rcnrQavsAfRtWYKKoXAcZ7MOVQ =

(此公钥可以作为字符串直接传递给MaskedWalletRequet)

私钥:MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTA / wqrlbeVddorTlaT1AqhALrIBwS + DUdV3N1K1gImqhRANCAASpm1SBsGcN01CE4NTSKaU61lwhrl7dWHXwJ2NE6KmGjc8XZGOWVDTtFjfEN + a3J60Gr7AH0bVmCiqFwHGezDlU

问题在于生成密钥。 我遇到过类似的问题。 为了解决这个问题,我从java代码调用shell命令来生成密钥。 PFB。

 String pemPath = "/path for storing pem path"; //To generate pem file executeSH("openssl ecparam -name prime256v1 -genkey -noout -out "+pemPath+"key.pem",true); //To obtain publicKeyString from pem file String publicKeyString = executeSH(new String[]{"/bin/sh", "-c", "openssl ec -in key.pem -pubout -text -noout | grep -A5 pub: | tail -5 | xxd -r -p | base64"},false); //To obtain privateKeyString from pem file String privateKeyString = executeSH("openssl pkcs8 -topk8 -inform PEM -outform PEM -in key.pem -nocrypt",false); privateKeyString=privateKeyString.substring(27, 211); //Deleteing PEM file executeSH("rm "+ pemPath+"key.pem",false); //executeSH function overload this function with commands[] parameter private String executeSH(String command, boolean waitFor) throws IOException, InterruptedException { Process process = null; InputStream inputStream = null; InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; String line,output=""; try { Runtime runtime = Runtime.getRuntime(); process = runtime.exec(command); inputStream = process.getInputStream(); inputStreamReader = new InputStreamReader(inputStream); bufferedReader = new BufferedReader(inputStreamReader); while((line=bufferedReader.readLine())!=null){ output += line; } if(waitFor){ process.waitFor(); } return output; } finally { if(bufferedReader!=null) bufferedReader.close(); if(inputStreamReader!=null) inputStreamReader.close(); if(inputStream!=null) inputStream.close(); if(process!=null) process.destroy(); } } 

我还没有解决问题,但我做了一些研究并制作了打码,以编程方式生成AndroidPay所需的EC密钥。

在下面的代码中,“AndroidPay的publicKey”的输出可以成功应用到AndroidPay移动站点API中,用于创建“PaymentRequest”,然后使用“encryptedMessage”,“ephemeralPublicKey”和“tag”生成“paymentMethodToken”。

问题是我无法解密“encryptedMessage”。 我使用NetworkTokenDecryptionUtil(AndroidPay提供的令牌解密工具)来读取我的EC私钥(以PKCS#8格式存储)来解密密文,但我甚至无法通过MAC(标签)validation。 虽然我忽略了validation,但解密后的值是不可读的。

我相信它接近于以编程方式生成AndroidPay所需的EC密钥的答案。 它只需要一点点推动。 请分享你的想法。 谢谢。

 package test; import java.io.StringWriter; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.Security; import javax.crypto.Cipher; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.KDF2BytesGenerator; import org.bouncycastle.crypto.kems.ECIESKeyEncapsulation; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.io.pem.PemObject; public class TestECKey { public static void main(String[] args) throws Throwable { // add instance of provider class Security.addProvider(new BouncyCastleProvider()); // Get domain parameters for example curve String name = "prime256v1"; // AndroidPay used ECNamedCurveParameterSpec ecp = ECNamedCurveTable.getParameterSpec(name); // when use prime256v1 // Buffer for ECIESKeyEncapsulation output byte[] kemOut = new byte[65]; // when use prime256v1 or secp256r1 ECDomainParameters domainParams = new ECDomainParameters(ecp.getCurve(), ecp.getG(), ecp.getN(), ecp.getH(), ecp.getSeed()); // Generate a private key and a public key System.out.println("name: " + name); AsymmetricCipherKeyPair keyPair; ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom()); ECKeyPairGenerator generator = new ECKeyPairGenerator(); generator.init(keyGenParams); keyPair = generator.generateKeyPair(); ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); System.out.println("====KEM===="); // Set ECIES-KEM parameters SecureRandom rnd = new SecureRandom(); ECIESKeyEncapsulation kem; // Test basic ECIES-KEM KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA256Digest()); kem = new ECIESKeyEncapsulation(kdf, rnd); kem.init(publicKey); kem.encrypt(kemOut, 128); System.out.println("AndroidPay's publicKey:"); System.out.println("out(Base64)[" + new String(Base64.encode(kemOut)) + "]"); System.out.println("====PKCS#8 (for private key)===="); KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); kpg.initialize(ecp); // Key pair to store public and private key KeyPair keyPair4Cipher = kpg.generateKeyPair(); JcaPKCS8Generator gen2 = new JcaPKCS8Generator(keyPair4Cipher.getPrivate(), null); PemObject obj2 = gen2.generate(); StringWriter sw2 = new StringWriter(); try (JcaPEMWriter pw = new JcaPEMWriter(sw2)) { pw.writeObject(obj2); } String pkcs8Key2 = sw2.toString(); System.out.println(pkcs8Key2); System.out.println("====CIPHER (encryption)===="); Cipher iesCipher = Cipher.getInstance("ECIES", BouncyCastleProvider.PROVIDER_NAME); iesCipher.init(Cipher.ENCRYPT_MODE, keyPair4Cipher.getPublic()); byte[] plain = "plaintext".getBytes(); byte[] cipher = iesCipher.doFinal(plain); System.out.println("cipher:" + new String(Base64.encode(cipher))); System.out.println("====CIPHER (decryption)===="); Cipher iesCipher2 = Cipher.getInstance("ECIES", BouncyCastleProvider.PROVIDER_NAME); iesCipher2.init(Cipher.DECRYPT_MODE, keyPair4Cipher.getPrivate()); byte[] plain2 = iesCipher2.doFinal(cipher); System.out.println("plain2:" + new String(plain2)); } } 

参考:

[1] https://developers.google.com/android-pay/integration/payment-token-cryptography#retrieving-the-encrypted-payload

[2] 使用BouncyCastle在Java中使用ECIES进行加密

[3] 使用给定私钥在椭圆曲线算法中生成公钥的代码

[4] http://www.bouncycastle.org/wiki/display/JA1/Elliptic+Curve+Key+Pair+Generation+and+Key+Factories

[5] http://techxperiment.blogspot.tw/2016/10/create-and-read-pkcs-8-format-private.html

[6]以及……