PaymentMethodTokenRecipient { public String unseal(final String sealedMessage) throws GeneralSecurityException { try { if (protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1)) { return unsealECV1(sealedMessage); } else if (protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2)) { return unsealECV2(sealedMessage); } else if (protocolVersion.equals( PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY)) { return unsealECV2SigningOnly(sealedMessage); } throw new IllegalArgumentException("unsupported version: " + protocolVersion); } catch (JSONException e) { throw new GeneralSecurityException("cannot unseal; invalid JSON message"); } } PaymentMethodTokenRecipient(
      String protocolVersion,
      List<SenderVerifyingKeysProvider> senderVerifyingKeysProviders,
      String senderId,
      List<ECPrivateKey> recipientPrivateKeys,
      List<PaymentMethodTokenRecipientKem> recipientKems,
      String recipientId); private  PaymentMethodTokenRecipient(Builder builder); String unseal(final String sealedMessage);  }
@Test public void testShouldDecryptECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldDecryptECV1WhenUsingCustomKem() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientKem( new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldDecryptECV1WhenFetchingSenderVerifyingKeys() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .fetchSenderVerifyingKeysWith( new GooglePaymentsPublicKeysManager.Builder() .setHttpTransport( new MockHttpTransport.Builder() .setLowLevelHttpResponse( new MockLowLevelHttpResponse() .setContent(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON)) .build()) .build()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldTryAllKeysToDecryptECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldTryAllCustomKemsToDecryptECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientKem( new MyPaymentMethodTokenRecipientKem(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) .addRecipientKem( new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldFailIfDecryptingWithDifferentKeyECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot decrypt", e.getMessage()); } }
@Test public void testShouldFailIfDecryptingWithDifferentCustomKemECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientKem( new MyPaymentMethodTokenRecipientKem(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot decrypt", e.getMessage()); } }
@Test public void testShouldFailIfVerifyingWithDifferentKeyECV1() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldTryAllKeysToVerifySignatureECV1() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); JSONArray keys = trustedKeysJson.getJSONArray("keys"); JSONObject correctKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()); JSONObject wrongKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); trustedKeysJson.put("keys", new JSONArray().put(wrongKey).put(correctKey)); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testShouldFailIfSignedECV1WithKeyForWrongProtocolVersion() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); JSONArray keys = trustedKeysJson.getJSONArray("keys"); JSONObject correctKeyButWrongProtocol = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()) .put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv2"); JSONObject wrongKeyButRightProtocol = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY) .put( PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1); trustedKeysJson.put( "keys", new JSONArray().put(correctKeyButWrongProtocol).put(wrongKeyButRightProtocol)); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfNoSigningKeysForProtocolVersion() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); JSONArray keys = trustedKeysJson.getJSONArray("keys"); JSONObject key1 = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()) .put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv2"); JSONObject key2 = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1).toString()) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY) .put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv3"); trustedKeysJson.put("keys", new JSONArray().put(key1).put(key2)); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldFailIfSignedMessageWasChangedInECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(CIPHERTEXT_EC_V1); payload.put( PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, payload.getString(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY) + " "); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfWrongRecipientInECV1() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId("not " + RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfECV1SetsWrongProtocolVersion() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(CIPHERTEXT_EC_V1); String invalidVersion = "ECv2"; payload.put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, invalidVersion); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("invalid version: " + invalidVersion, e.getMessage()); } }
@Test public void testShouldFailIfProtocolSetToAnInt() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(CIPHERTEXT_EC_V1); payload.put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, 1); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { } }
@Test public void testShouldFailIfProtocolSetToAnFloat() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(CIPHERTEXT_EC_V1); payload.put(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, 1.1); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { } }
@Test public void testShouldSucceedIfMessageIsNotExpired() throws Exception { PaymentMethodTokenSender sender = new PaymentMethodTokenSender.Builder() .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) .recipientId(RECIPIENT_ID) .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) .build(); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); String ciphertext = sender.seal( new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())) .put("someKey", "someValue") .toString()); assertEquals("someValue", new JSONObject(recipient.unseal(ciphertext)).getString("someKey")); }
@Test public void testShouldFailIfMessageIsExpired() throws Exception { PaymentMethodTokenSender sender = new PaymentMethodTokenSender.Builder() .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) .recipientId(RECIPIENT_ID) .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) .build(); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); String ciphertext = sender.seal( new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())) .toString()); try { recipient.unseal(ciphertext); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("expired payload", e.getMessage()); } }
@Test public void testShouldFailIfTrustedKeyIsExpiredInECV1() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1) .put( "keyExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(CIPHERTEXT_EC_V1); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldSucceedIfKeyExpirationIsMissingInTrustedKeyIsExpiredForECV1() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V1) .remove("keyExpiration"); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); }
@Test public void testUnsealECV2WithCustomKem() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientKem( new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) .build(); assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); }
@Test public void testShouldFailIfSignedMessageWasChangedInECV2() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(sealECV2(PLAINTEXT)); payload.put( PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, payload.getString(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY) + " "); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldThrowIfECV2UseWrongSenderId() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .senderId("not-" + PaymentMethodTokenConstants.GOOGLE_SENDER_ID) .build(); try { recipient.unseal(sealECV2(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfVerifyingWithDifferentKeyECV2() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(sealECV2(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfTrustedKeyIsExpiredInECV2() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2) .put( "keyExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(sealECV2(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldFailIfKeyExpirationIsMissingInTrustedKeyECV2() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2) .remove("keyExpiration"); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(sealECV2(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldTryAllKeysToVerifySignatureECV2() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); JSONArray keys = trustedKeysJson.getJSONArray("keys"); JSONObject correctKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2).toString()); JSONObject wrongKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2).toString()) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); trustedKeysJson.put("keys", new JSONArray().put(wrongKey).put(correctKey)); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); }
@Test public void testShouldFailIfSignedKeyWasChangedInECV2() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(sealECV2(PLAINTEXT)); payload .getJSONObject("intermediateSigningKey") .put( "signedKey", payload.getJSONObject("intermediateSigningKey").getString("signedKey") + " "); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldThrowIfSignatureForSignedKeyIsIncorrectInECV2() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(sealECV2(PLAINTEXT)); JSONArray signatures = payload.getJSONObject("intermediateSigningKey").getJSONArray("signatures"); String correctSignature = signatures.getString(0); byte[] wrongSignatureBytes = Base64.decode(correctSignature); wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; payload .getJSONObject("intermediateSigningKey") .put("signatures", new JSONArray().put(Base64.encode(wrongSignatureBytes))); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldTryVerifyingAllSignaturesForSignedKeyInECV2() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); JSONObject payload = new JSONObject(sealECV2(PLAINTEXT)); JSONArray signatures = payload.getJSONObject("intermediateSigningKey").getJSONArray("signatures"); String correctSignature = signatures.getString(0); byte[] wrongSignatureBytes = Base64.decode(correctSignature); wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; payload .getJSONObject("intermediateSigningKey") .put( "signatures", new JSONArray().put(Base64.encode(wrongSignatureBytes)).put(correctSignature)); assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); }
@Test public void testShouldThrowIfECV2UseWrongRecipientId() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId("not" + RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); try { recipient.unseal(sealECV2(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldAcceptNonExpiredECV2Message() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); String plaintext = new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())) .toString(); assertEquals(plaintext, recipient.unseal(sealECV2(plaintext))); }
@Test public void testShouldFailIfECV2MessageIsExpired() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); String ciphertext = sealECV2( new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())) .toString()); try { recipient.unseal(ciphertext); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("expired payload", e.getMessage()); } }
@Test public void testShouldFailIfIntermediateSigningKeyIsExpiredInECV2() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) .build(); PaymentMethodTokenSender sender = new PaymentMethodTokenSender.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .senderIntermediateSigningKey( GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) .senderIntermediateCert( new SenderIntermediateCertFactory.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) .senderIntermediateSigningKey( GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) .expiration(Instant.now().minus(Duration.standardDays(1)).getMillis()) .build() .create()) .recipientId(RECIPIENT_ID) .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) .build(); try { recipient.unseal(sender.seal(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("expired intermediateSigningKey", e.getMessage()); } }
@Test public void testVerifyECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); }
@Test public void testShouldFailIfSignedMessageWasChangedInECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); JSONObject payload = new JSONObject(signECV2SigningOnly(PLAINTEXT)); payload.put( PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, payload.getString(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY) + " "); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldThrowIfECV2SigningOnlyUseWrongSenderId() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .senderId("not-" + PaymentMethodTokenConstants.GOOGLE_SENDER_ID) .build(); try { recipient.unseal(signECV2SigningOnly(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfVerifyingWithDifferentKeyECV2SigningOnly() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .build(); try { recipient.unseal(signECV2SigningOnly(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldFailIfTrustedKeyIsExpiredInECV2SigningOnly() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY) .put( "keyExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .build(); try { recipient.unseal(signECV2SigningOnly(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldFailIfKeyExpirationIsMissingInTrustedKeyECV2SigningOnly() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); trustedKeysJson .getJSONArray("keys") .getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY) .remove("keyExpiration"); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .build(); try { recipient.unseal(signECV2SigningOnly(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("no trusted keys are available for this protocol version", e.getMessage()); } }
@Test public void testShouldTryAllKeysToVerifySignatureECV2SigningOnly() throws Exception { JSONObject trustedKeysJson = new JSONObject(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON); JSONArray keys = trustedKeysJson.getJSONArray("keys"); JSONObject correctKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).toString()); JSONObject wrongKey = new JSONObject(keys.getJSONObject(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).toString()) .put("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); trustedKeysJson.put("keys", new JSONArray().put(wrongKey).put(correctKey)); PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(trustedKeysJson.toString()) .recipientId(RECIPIENT_ID) .build(); assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); }
@Test public void testShouldFailIfSignedKeyWasChangedInECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); JSONObject payload = new JSONObject(signECV2SigningOnly(PLAINTEXT)); payload .getJSONObject("intermediateSigningKey") .put( "signedKey", payload.getJSONObject("intermediateSigningKey").getString("signedKey") + " "); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldThrowIfSignatureForSignedKeyIsIncorrectInECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); JSONObject payload = new JSONObject(signECV2SigningOnly(PLAINTEXT)); JSONArray signatures = payload.getJSONObject("intermediateSigningKey").getJSONArray("signatures"); String correctSignature = signatures.getString(0); byte[] wrongSignatureBytes = Base64.decode(correctSignature); wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; payload .getJSONObject("intermediateSigningKey") .put("signatures", new JSONArray().put(Base64.encode(wrongSignatureBytes))); try { recipient.unseal(payload.toString()); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldTryVerifyingAllSignaturesForSignedKeyInECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); JSONObject payload = new JSONObject(signECV2SigningOnly(PLAINTEXT)); JSONArray signatures = payload.getJSONObject("intermediateSigningKey").getJSONArray("signatures"); String correctSignature = signatures.getString(0); byte[] wrongSignatureBytes = Base64.decode(correctSignature); wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; payload .getJSONObject("intermediateSigningKey") .put( "signatures", new JSONArray().put(Base64.encode(wrongSignatureBytes)).put(correctSignature)); assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); }
@Test public void testShouldThrowIfECV2SigningOnlyUseWrongRecipientId() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId("not" + RECIPIENT_ID) .build(); try { recipient.unseal(signECV2SigningOnly(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("cannot verify signature", e.getMessage()); } }
@Test public void testShouldAcceptNonExpiredECV2SigningOnlyMessage() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); String plaintext = new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())) .toString(); assertEquals(plaintext, recipient.unseal(signECV2SigningOnly(plaintext))); }
@Test public void testShouldFailIfECV2SigningOnlyMessageIsExpired() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); String ciphertext = signECV2SigningOnly( new JSONObject() .put( "messageExpiration", String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())) .toString()); try { recipient.unseal(ciphertext); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("expired payload", e.getMessage()); } }
@Test public void testShouldFailIfIntermediateSigningKeyIsExpiredInECV2SigningOnly() throws Exception { PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) .recipientId(RECIPIENT_ID) .build(); PaymentMethodTokenSender sender = new PaymentMethodTokenSender.Builder() .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .senderIntermediateSigningKey( GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) .senderIntermediateCert( new SenderIntermediateCertFactory.Builder() .protocolVersion( PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64) .senderIntermediateSigningKey( GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) .expiration(Instant.now().minus(Duration.standardDays(1)).getMillis()) .build() .create()) .recipientId(RECIPIENT_ID) .build(); try { recipient.unseal(sender.seal(PLAINTEXT)); fail("Expected GeneralSecurityException"); } catch (GeneralSecurityException e) { assertEquals("expired intermediateSigningKey", e.getMessage()); } }