All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jmrtd.cert.CardVerifiableCertificate Maven / Gradle / Ivy

/*
 * JMRTD - A Java API for accessing machine readable travel documents.
 *
 * Copyright (C) 2006 - 2017  The JMRTD team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * $Id: CardVerifiableCertificate.java 1662 2017-03-20 19:13:03Z martijno $
 */

package org.jmrtd.cert;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;
import java.util.logging.Logger;

import org.ejbca.cvc.AccessRightEnum;
import org.ejbca.cvc.AlgorithmUtil;
import org.ejbca.cvc.AuthorizationRoleEnum;
import org.ejbca.cvc.CAReferenceField;
import org.ejbca.cvc.CVCertificateBody;
import org.ejbca.cvc.HolderReferenceField;
import org.ejbca.cvc.OIDField;
import org.ejbca.cvc.ReferenceField;
import org.ejbca.cvc.exception.ConstructionException;

import net.sf.scuba.data.Country;

/**
 * Card verifiable certificates as specified in TR 03110.
 *
 * Just a wrapper around org.ejbca.cvc.CVCertificate by Keijo Kurkinen of EJBCA.org,
 * so that we can subclass java.security.cert.Certificate.
 *
 * We also hide some of the internal structure (no more calls to get the "body" just to get some
 * attributes).
 *
 * @author The JMRTD team ([email protected])
 *
 * @version $Revision: 1662 $
 */
public class CardVerifiableCertificate extends Certificate {

  private static final long serialVersionUID = -3585440601605666288L;

  private static final Logger LOGGER = Logger.getLogger("org.jmrtd");

  /** The EJBCA CVC that we wrap. */
  private org.ejbca.cvc.CVCertificate cvCertificate;

  private transient KeyFactory rsaKeyFactory;

  /**
   * Constructs a wrapper.
   *
   * @param cvCertificate the EJCBA CVC to wrap
   */
  protected CardVerifiableCertificate(org.ejbca.cvc.CVCertificate cvCertificate) {
    super("CVC");
    try {
      rsaKeyFactory = KeyFactory.getInstance("RSA");
    } catch (NoSuchAlgorithmException nsae) {
      /* NOTE: never happens, RSA will be provided. */
      LOGGER.severe("Exception: " + nsae.getMessage());
    }
    this.cvCertificate = cvCertificate;
  }

  /*
   * TODO: perhaps move this to factory class (CertificateFactory, CertificateBuilder, whatever).
   * NOTE: algorithm should be one of"SHA224withECDSA", "SHA256withECDSA", "SHA384withECDSA", "SHA512withECDSA",
   * or similar with RSA.
   */
  /**
   * Constructs a certificate.
   *
   * @param authorityReference authority reference
   * @param holderReference holder reference
   * @param publicKey public key
   * @param algorithm algorithm
   * @param notBefore valid from date
   * @param notAfter valid to date
   * @param role role
   * @param permission permission
   * @param signatureData signed date
   */
  public CardVerifiableCertificate(CVCPrincipal authorityReference, CVCPrincipal holderReference,
      PublicKey publicKey,
      String algorithm,
      Date notBefore,
      Date notAfter,
      CVCAuthorizationTemplate.Role role,
      CVCAuthorizationTemplate.Permission permission,
      byte[] signatureData) {
    super("CVC");
    try {
      CAReferenceField authorityRef = new CAReferenceField(authorityReference.getCountry().toAlpha2Code(), authorityReference.getMnemonic(), authorityReference.getSeqNumber());
      HolderReferenceField  holderRef = new HolderReferenceField(holderReference.getCountry().toAlpha2Code(), holderReference.getMnemonic(), holderReference.getSeqNumber());
      AuthorizationRoleEnum authRole = CVCAuthorizationTemplate.fromRole(role);
      AccessRightEnum accessRight = CVCAuthorizationTemplate.fromPermission(permission);
      CVCertificateBody body = new CVCertificateBody(authorityRef, org.ejbca.cvc.KeyFactory.createInstance(publicKey, algorithm, authRole), holderRef, authRole, accessRight, notBefore, notAfter);
      this.cvCertificate = new org.ejbca.cvc.CVCertificate(body);
      this.cvCertificate.setSignature(signatureData);
      cvCertificate.getTBS();
    } catch(ConstructionException ce) {
      throw new IllegalArgumentException(ce.getMessage());
    }
  }

  /**
   * Gets the signature algorithm.
   *
   * @return an algorithm name
   */
  public String getSigAlgName() {
    try {
      OIDField oid = cvCertificate.getCertificateBody().getPublicKey().getObjectIdentifier();			
      String algorithm = AlgorithmUtil.getAlgorithmName(oid);
      return algorithm;
    } catch (NoSuchFieldException nsfe) {
      return null;
    }
  }

  /**
   * Gets the signature algorithm OID
   *
   * @return an OID
   */
  public String getSigAlgOID() {
    try {
      OIDField oid = cvCertificate.getCertificateBody().getPublicKey().getObjectIdentifier();
      return oid.getAsText();
    } catch (NoSuchFieldException nsfe) {
      return null;
    }
  }

  /**
   * Returns the encoded form of this certificate. It is
   * assumed that each certificate type would have only a single
   * form of encoding; for example, X.509 certificates would
   * be encoded as ASN.1 DER.
   *
   * @return the encoded form of this certificate
   *
   * @exception CertificateEncodingException if an encoding error occurs.
   */
  public byte[] getEncoded() throws CertificateEncodingException {
    try {
      return cvCertificate.getDEREncoded();
    } catch (IOException ioe) {
      throw new CertificateEncodingException(ioe.getMessage());
    }
  }

  /**
   * Gets the public key from this certificate.
   *
   * @return the public key.
   */
  public PublicKey getPublicKey() {
    try {
      org.ejbca.cvc.CVCPublicKey publicKey = cvCertificate.getCertificateBody().getPublicKey();
      if (publicKey.getAlgorithm().equals("RSA")) { // TODO: something similar for EC / ECDSA?
        RSAPublicKey rsaPublicKey = (RSAPublicKey)publicKey;
        try {
          return rsaKeyFactory.generatePublic(new RSAPublicKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent()));
        } catch (GeneralSecurityException gse) {
          LOGGER.severe("Exception: " + gse.getMessage());
          return publicKey;
        }
      }

      /* It's ECDSA... */
      return publicKey;
    } catch (NoSuchFieldException nsfe) {
      LOGGER.severe("Exception: " + nsfe.getMessage());
      return null;
    }
  }

  /**
   * Returns a string representation of this certificate.
   *
   * @return a string representation of this certificate.
   */
  public String toString() {
    return cvCertificate.toString();
  }

  /**
   * Verifies that this certificate was signed using the
   * private key that corresponds to the specified public key.
   *
   * @param key the PublicKey used to carry out the verification.
   *
   * @exception NoSuchAlgorithmException on unsupported signature
   * algorithms.
   * @exception InvalidKeyException on incorrect key.
   * @exception NoSuchProviderException if there's no default provider.
   * @exception SignatureException on signature errors.
   * @exception CertificateException on encoding errors.
   */
  public void verify(PublicKey key) throws CertificateException,
  NoSuchAlgorithmException, InvalidKeyException,
  NoSuchProviderException, SignatureException {
    Provider[] providers = Security.getProviders();
    boolean foundProvider = false;
    for (Provider provider: providers) {
      try {
        cvCertificate.verify(key, provider.getName());
        foundProvider = true;
        break;
      } catch (NoSuchAlgorithmException nse) {
        continue;
      }
    }
    if (!foundProvider) {
      throw new NoSuchAlgorithmException("Tried all security providers: None was able to provide this signature algorithm.");
    }
  }


  /**
   * Verifies that this certificate was signed using the
   * private key that corresponds to the specified public key.
   * This method uses the signature verification engine
   * supplied by the specified provider.
   *
   * @param key the PublicKey used to carry out the verification.
   * @param provider the name of the signature provider.
   *
   * @throws NoSuchAlgorithmException on unsupported signature algorithms.
   * @throws InvalidKeyException on incorrect key.
   * @throws NoSuchProviderException on incorrect provider.
   * @throws SignatureException on signature errors.
   * @throws CertificateException on encoding errors.
   */
  public void verify(PublicKey key, String provider)
      throws CertificateException, NoSuchAlgorithmException,
      InvalidKeyException, NoSuchProviderException, SignatureException {
    cvCertificate.verify(key, provider);
  }

  /**
   * The DER encoded certificate body.
   *
   * @return DER encoded certificate body
   *
   * @throws CertificateException on error
   * @throws IOException on error
   */
  public byte[] getCertBodyData() throws CertificateException, IOException {
    try {
      return cvCertificate.getCertificateBody().getDEREncoded();
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Returns 'Effective Date'.
   *
   * @return the effective date
   *
   * @throws CertificateException on error
   */
  public Date getNotBefore() throws CertificateException {
    try {
      return cvCertificate.getCertificateBody().getValidFrom();
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Returns 'Expiration Date'.
   *
   * @return the expiration date
   *
   * @throws CertificateException on error
   */
  public Date getNotAfter() throws CertificateException {
    try {
      return cvCertificate.getCertificateBody().getValidTo();
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Gets the authority reference.
   *
   * @return the authority reference
   *
   * @throws CertificateException if the authority reference field is not present
   */
  public CVCPrincipal getAuthorityReference() throws CertificateException {
    try  {
      ReferenceField rf = cvCertificate.getCertificateBody().getAuthorityReference();
      final String countryCode = rf.getCountry().toUpperCase();
      Country country = Country.getInstance(countryCode);
      return new CVCPrincipal(country, rf.getMnemonic(), rf.getSequence());
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Gets the holder reference.
   *
   * @return the holder reference
   *
   * @throws CertificateException if the authority reference field is not present
   */
  public CVCPrincipal getHolderReference() throws CertificateException {
    try  {
      ReferenceField rf = cvCertificate.getCertificateBody().getHolderReference();
      return new CVCPrincipal(Country.getInstance(rf.getCountry().toUpperCase()), rf.getMnemonic(), rf.getSequence());
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Gets the holder authorization template.
   *
   * @return the holder authorization template
   *
   * @throws CertificateException on error constructing the template
   */
  public CVCAuthorizationTemplate getAuthorizationTemplate() throws CertificateException {
    try {
      org.ejbca.cvc.CVCAuthorizationTemplate template = cvCertificate.getCertificateBody().getAuthorizationTemplate();
      return new CVCAuthorizationTemplate(template);
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Returns the signature (just the value, without the 0x5F37 tag).
   *
   * @return the signature bytes
   *
   * @throws CertificateException if certificate doesn't contain a signature
   */
  public byte[] getSignature() throws CertificateException {
    try {
      return cvCertificate.getSignature();
    } catch (NoSuchFieldException nsfe) {
      throw new CertificateException(nsfe.getMessage());
    }
  }

  /**
   * Tests for equality with respect to another object.
   *
   * @param otherObj the other object
   *
   * @return whether this certificate equals the other object
   */
  public boolean equals(Object otherObj) {
    if (otherObj == null) { return false; }
    if (this == otherObj) { return true; }
    if (!this.getClass().equals(otherObj.getClass())) { return false; }
    return this.cvCertificate.equals(((CardVerifiableCertificate)otherObj).cvCertificate);
  }

  /**
   * Gets a hash code for this object.
   *
   * @return a hash code for this object
   */
  public int hashCode() {
    return cvCertificate.hashCode() * 2 - 1030507011;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy