From 3c86785a89c700bb6513b3888d4fca98a613f7d4 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Fri, 7 Feb 2014 18:09:54 +0100 Subject: [PATCH] Payment protocol: Expose a friendly/display name for validating CA --- .../protocols/payments/PaymentSession.java | 56 +++++++++++++++---- .../payments/PaymentSessionTest.java | 1 + .../com/google/bitcoin/tools/WalletTool.java | 1 + 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/protocols/payments/PaymentSession.java b/core/src/main/java/com/google/bitcoin/protocols/payments/PaymentSession.java index 1b1354d8..8e385afe 100644 --- a/core/src/main/java/com/google/bitcoin/protocols/payments/PaymentSession.java +++ b/core/src/main/java/com/google/bitcoin/protocols/payments/PaymentSession.java @@ -386,10 +386,49 @@ public class PaymentSession { * Information about the X509 signature's issuer and subject. */ public static class PkiVerificationData { - public String name; - public PublicKey merchantSigningKey; - public TrustAnchor rootAuthority; - public String orgName; + /** Display name of the payment requestor, could be a domain name, email address, legal name, etc */ + public final String name; + /** The "org" part of the payment requestors ID. */ + public final String orgName; + /** SSL public key that was used to sign. */ + public final PublicKey merchantSigningKey; + /** Object representing the CA that verified the merchant's ID */ + public final TrustAnchor rootAuthority; + /** String representing the display name of the CA that verified the merchant's ID */ + public final String rootAuthorityName; + + private PkiVerificationData(@Nullable String name, @Nullable String orgName, PublicKey merchantSigningKey, + TrustAnchor rootAuthority) throws PaymentRequestException.PkiVerificationException { + this.name = name; + this.orgName = orgName; + this.merchantSigningKey = merchantSigningKey; + this.rootAuthority = rootAuthority; + this.rootAuthorityName = getNameFromCert(rootAuthority); + } + + private String getNameFromCert(TrustAnchor rootAuthority) throws PaymentRequestException.PkiVerificationException { + org.spongycastle.asn1.x500.X500Name name = new X500Name(rootAuthority.getTrustedCert().getSubjectX500Principal().getName()); + String commonName = null, org = null, location = null, country = null; + for (RDN rdn : name.getRDNs()) { + AttributeTypeAndValue pair = rdn.getFirst(); + String val = ((ASN1String)pair.getValue()).getString(); + if (pair.getType().equals(RFC4519Style.cn)) + commonName = val; + else if (pair.getType().equals(RFC4519Style.o)) + org = val; + else if (pair.getType().equals(RFC4519Style.l)) + location = val; + else if (pair.getType().equals(RFC4519Style.c)) + country = val; + } + if (org != null && location != null && country != null) { + return org + ", " + location + ", " + country; + } else { + if (commonName == null) + throw new PaymentRequestException.PkiVerificationException("Could not find any identity info for root CA"); + return commonName; + } + } } /** @@ -461,13 +500,10 @@ public class PaymentSession { else if (pair.getType().equals(RFC4519Style.o)) orgName = ((ASN1String)pair.getValue()).getString(); } - + if (entityName == null && orgName == null) + throw new PaymentRequestException.PkiVerificationException("Invalid certificate, no CN or O fields"); // Everything is peachy. Return some useful data to the caller. - PkiVerificationData data = new PkiVerificationData(); - data.name = entityName; - data.orgName = orgName; - data.merchantSigningKey = publicKey; - data.rootAuthority = result.getTrustAnchor(); + PkiVerificationData data = new PkiVerificationData(entityName, orgName, publicKey, result.getTrustAnchor()); // Cache the result so we don't have to re-verify if this method is called again. pkiVerificationData = data; return data; diff --git a/core/src/test/java/com/google/bitcoin/protocols/payments/PaymentSessionTest.java b/core/src/test/java/com/google/bitcoin/protocols/payments/PaymentSessionTest.java index 63555fa3..de9458a0 100644 --- a/core/src/test/java/com/google/bitcoin/protocols/payments/PaymentSessionTest.java +++ b/core/src/test/java/com/google/bitcoin/protocols/payments/PaymentSessionTest.java @@ -108,6 +108,7 @@ public class PaymentSessionTest { MockPaymentSession paymentSession = new MockPaymentSession(paymentRequest); PaymentSession.PkiVerificationData pkiData = paymentSession.verifyPki(); assertEquals("www.bitcoincore.org", pkiData.name); + assertEquals("The USERTRUST Network, Salt Lake City, US", pkiData.rootAuthorityName); } private Protos.PaymentRequest newSimplePaymentRequest() { diff --git a/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java b/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java index 647deb48..eef9de2d 100644 --- a/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java +++ b/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java @@ -539,6 +539,7 @@ public class WalletTool { System.out.println("Pki-Verified Name: " + session.pkiVerificationData.name); if (session.pkiVerificationData.orgName != null) System.out.println("Pki-Verified Org: " + session.pkiVerificationData.orgName); + System.out.println("PKI data verified by: " + session.pkiVerificationData.rootAuthorityName); } final Wallet.SendRequest req = session.getSendRequest(); if (password != null) {