Ejemplo n.º 1
0
  private X509Certificate[] doValidate(X509Certificate[] chain, PKIXBuilderParameters params)
      throws CertificateException {
    try {
      setDate(params);

      // do the validation
      CertPathValidator validator = CertPathValidator.getInstance("PKIX");
      CertPath path = factory.generateCertPath(Arrays.asList(chain));
      certPathLength = chain.length;
      PKIXCertPathValidatorResult result =
          (PKIXCertPathValidatorResult) validator.validate(path, params);

      return toArray(path, result.getTrustAnchor());
    } catch (GeneralSecurityException e) {
      if (e instanceof CertPathValidatorException) {
        // check cause
        Throwable cause = e.getCause();
        if (cause != null && cause instanceof OCSPResponse.UnreliableException) {
          throw new ValidatorException(ValidatorException.T_OCSP_RESPONSE_UNRELIABLE);
        }
      }
      throw new ValidatorException("PKIX path validation failed: " + e.toString(), e);
    }
  }
Ejemplo n.º 2
0
  /**
   * Uses the provided PKI method to find the corresponding public key and verify the provided
   * signature.
   *
   * @param paymentRequest Payment request to verify.
   * @param trustStore KeyStore of trusted root certificate authorities.
   * @return verification data, or null if no PKI method was specified in the {@link
   *     Protos.PaymentRequest}.
   * @throws PaymentProtocolException if payment request could not be verified.
   */
  @Nullable
  public static PkiVerificationData verifyPaymentRequestPki(
      Protos.PaymentRequest paymentRequest, KeyStore trustStore) throws PaymentProtocolException {
    List<X509Certificate> certs = null;
    try {
      final String pkiType = paymentRequest.getPkiType();
      if ("none".equals(pkiType))
        // Nothing to verify. Everything is fine. Move along.
        return null;

      String algorithm;
      if ("x509+sha256".equals(pkiType)) algorithm = "SHA256withRSA";
      else if ("x509+sha1".equals(pkiType)) algorithm = "SHA1withRSA";
      else throw new PaymentProtocolException.InvalidPkiType("Unsupported PKI type: " + pkiType);

      Protos.X509Certificates protoCerts =
          Protos.X509Certificates.parseFrom(paymentRequest.getPkiData());
      if (protoCerts.getCertificateCount() == 0)
        throw new PaymentProtocolException.InvalidPkiData(
            "No certificates provided in message: server config error");

      // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER
      // and base64.
      // The ordering of certificates is defined by the payment protocol spec to be the same as what
      // the Java
      // crypto API requires - convenient!
      CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
      certs = Lists.newArrayList();
      for (ByteString bytes : protoCerts.getCertificateList())
        certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput()));
      CertPath path = certificateFactory.generateCertPath(certs);

      // Retrieves the most-trusted CAs from keystore.
      PKIXParameters params = new PKIXParameters(trustStore);
      // Revocation not supported in the current version.
      params.setRevocationEnabled(false);

      // Now verify the certificate chain is correct and trusted. This let's us get an identity
      // linked pubkey.
      CertPathValidator validator = CertPathValidator.getInstance("PKIX");
      PKIXCertPathValidatorResult result =
          (PKIXCertPathValidatorResult) validator.validate(path, params);
      PublicKey publicKey = result.getPublicKey();
      // OK, we got an identity, now check it was used to sign this message.
      Signature signature = Signature.getInstance(algorithm);
      // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most
      // obvious
      // way to set it up, because we don't care about the constraints specified on the
      // certificates: any
      // cert that links a key to a domain name or other identity will do for us.
      signature.initVerify(publicKey);
      Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder();
      reqToCheck.setSignature(ByteString.EMPTY);
      signature.update(reqToCheck.build().toByteArray());
      if (!signature.verify(paymentRequest.getSignature().toByteArray()))
        throw new PaymentProtocolException.PkiVerificationException(
            "Invalid signature, this payment request is not valid.");

      // Signature verifies, get the names from the identity we just verified for presentation to
      // the user.
      final X509Certificate cert = certs.get(0);
      String displayName = X509Utils.getDisplayNameFromCertificate(cert, true);
      if (displayName == null)
        throw new PaymentProtocolException.PkiVerificationException(
            "Could not extract name from certificate");
      // Everything is peachy. Return some useful data to the caller.
      return new PkiVerificationData(displayName, publicKey, result.getTrustAnchor());
    } catch (InvalidProtocolBufferException e) {
      // Data structures are malformed.
      throw new PaymentProtocolException.InvalidPkiData(e);
    } catch (CertificateException e) {
      // The X.509 certificate data didn't parse correctly.
      throw new PaymentProtocolException.PkiVerificationException(e);
    } catch (NoSuchAlgorithmException e) {
      // Should never happen so don't make users have to think about it. PKIX is always present.
      throw new RuntimeException(e);
    } catch (InvalidAlgorithmParameterException e) {
      throw new RuntimeException(e);
    } catch (CertPathValidatorException e) {
      // The certificate chain isn't known or trusted, probably, the server is using an SSL root we
      // don't
      // know about and the user needs to upgrade to a new version of the software (or import a root
      // cert).
      throw new PaymentProtocolException.PkiVerificationException(e, certs);
    } catch (InvalidKeyException e) {
      // Shouldn't happen if the certs verified correctly.
      throw new PaymentProtocolException.PkiVerificationException(e);
    } catch (SignatureException e) {
      // Something went wrong during hashing (yes, despite the name, this does not mean the sig was
      // invalid).
      throw new PaymentProtocolException.PkiVerificationException(e);
    } catch (KeyStoreException e) {
      throw new RuntimeException(e);
    }
  }
Ejemplo n.º 3
0
  /**
   * Uses the provided PKI method to find the corresponding public key and verify the provided
   * signature. Returns null if no PKI method was specified in the {@link Protos.PaymentRequest}.
   */
  public @Nullable PkiVerificationData verifyPki() throws PaymentRequestException {
    try {
      if (pkiVerificationData != null) return pkiVerificationData;
      if (paymentRequest.getPkiType().equals("none"))
        // Nothing to verify. Everything is fine. Move along.
        return null;

      String algorithm;
      if (paymentRequest.getPkiType().equals("x509+sha256")) algorithm = "SHA256withRSA";
      else if (paymentRequest.getPkiType().equals("x509+sha1")) algorithm = "SHA1withRSA";
      else
        throw new PaymentRequestException.InvalidPkiType(
            "Unsupported PKI type: " + paymentRequest.getPkiType());

      Protos.X509Certificates protoCerts =
          Protos.X509Certificates.parseFrom(paymentRequest.getPkiData());
      if (protoCerts.getCertificateCount() == 0)
        throw new PaymentRequestException.InvalidPkiData(
            "No certificates provided in message: server config error");

      // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER
      // and base64.
      // The ordering of certificates is defined by the payment protocol spec to be the same as what
      // the Java
      // crypto API requires - convenient!
      CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
      List<X509Certificate> certs = Lists.newArrayList();
      for (ByteString bytes : protoCerts.getCertificateList())
        certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput()));
      CertPath path = certificateFactory.generateCertPath(certs);

      // Retrieves the most-trusted CAs from keystore.
      PKIXParameters params = new PKIXParameters(createKeyStore(trustStorePath));
      // Revocation not supported in the current version.
      params.setRevocationEnabled(false);

      // Now verify the certificate chain is correct and trusted. This let's us get an identity
      // linked pubkey.
      CertPathValidator validator = CertPathValidator.getInstance("PKIX");
      PKIXCertPathValidatorResult result =
          (PKIXCertPathValidatorResult) validator.validate(path, params);
      PublicKey publicKey = result.getPublicKey();
      // OK, we got an identity, now check it was used to sign this message.
      Signature signature = Signature.getInstance(algorithm);
      // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most
      // obvious
      // way to set it up, because we don't care about the constraints specified on the
      // certificates: any
      // cert that links a key to a domain name or other identity will do for us.
      signature.initVerify(publicKey);
      Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder();
      reqToCheck.setSignature(ByteString.EMPTY);
      signature.update(reqToCheck.build().toByteArray());
      if (!signature.verify(paymentRequest.getSignature().toByteArray()))
        throw new PaymentRequestException.PkiVerificationException(
            "Invalid signature, this payment request is not valid.");

      // Signature verifies, get the names from the identity we just verified for presentation to
      // the user.
      X500Principal principal = certs.get(0).getSubjectX500Principal();
      // At this point the Java crypto API falls flat on its face and dies - there's no clean way to
      // get the
      // different parts of the certificate name except for parsing the string. That's hard because
      // of various
      // custom escaping rules and the usual crap. So, use Bouncy Castle to re-parse the string into
      // binary form
      // again and then look for the names we want. Fail!
      org.spongycastle.asn1.x500.X500Name name = new X500Name(principal.getName());
      String entityName = null, orgName = null;
      for (RDN rdn : name.getRDNs()) {
        AttributeTypeAndValue pair = rdn.getFirst();
        if (pair.getType().equals(RFC4519Style.cn))
          entityName = ((ASN1String) pair.getValue()).getString();
        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(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;
    } catch (InvalidProtocolBufferException e) {
      // Data structures are malformed.
      throw new PaymentRequestException.InvalidPkiData(e);
    } catch (CertificateException e) {
      // The X.509 certificate data didn't parse correctly.
      throw new PaymentRequestException.PkiVerificationException(e);
    } catch (NoSuchAlgorithmException e) {
      // Should never happen so don't make users have to think about it. PKIX is always present.
      throw new RuntimeException(e);
    } catch (InvalidAlgorithmParameterException e) {
      throw new RuntimeException(e);
    } catch (CertPathValidatorException e) {
      // The certificate chain isn't known or trusted, probably, the server is using an SSL root we
      // don't
      // know about and the user needs to upgrade to a new version of the software (or import a root
      // cert).
      throw new PaymentRequestException.PkiVerificationException(e);
    } catch (InvalidKeyException e) {
      // Shouldn't happen if the certs verified correctly.
      throw new PaymentRequestException.PkiVerificationException(e);
    } catch (SignatureException e) {
      // Something went wrong during hashing (yes, despite the name, this does not mean the sig was
      // invalid).
      throw new PaymentRequestException.PkiVerificationException(e);
    } catch (IOException e) {
      throw new PaymentRequestException.PkiVerificationException(e);
    } catch (KeyStoreException e) {
      throw new RuntimeException(e);
    }
  }