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

org.xipki.security.X509Cert Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2020 Lijun Liao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xipki.security;

import static org.xipki.util.Args.notNull;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Date;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.xipki.security.util.AlgorithmUtil;
import org.xipki.security.util.KeyUtil;
import org.xipki.security.util.X509Util;
import org.xipki.util.Hex;

/**
 * Wrapper to an {@link X509Certificate}.
 *
 * @author Lijun Liao
 * @since 5.3.8
 */

public class X509Cert {

  private final Object sync = new Object();

  private X509CertificateHolder bcInstance;

  private X509Certificate jceInstance;

  private final boolean selfSigned;

  private final X500Name issuer;

  private final BigInteger serialNumber;

  private final X500Name subject;

  private final Date notBefore;

  private final Date notAfter;

  private String issuerRfc4519Text;

  private String subjectRfc4519Text;

  private byte[] subjectKeyId;

  private byte[] authorityKeyId;

  private int basicConstrains = -2;

  private boolean keyUsageProcessed;

  private boolean[] keyUsage;

  private SubjectPublicKeyInfo subjectPublicKeyInfo;

  private PublicKey publicKey;

  private byte[] encoded;

  public X509Cert(Certificate cert) {
    this(new X509CertificateHolder(cert), null);
  }

  public X509Cert(Certificate cert, byte[] encoded) {
    this(new X509CertificateHolder(cert), encoded);
  }

  public X509Cert(X509Certificate cert) {
    this(cert, null);
  }

  public X509Cert(X509Certificate cert, byte[] encoded) {
    this.bcInstance = null;
    this.jceInstance = notNull(cert, "cert");
    this.encoded = encoded;

    this.notBefore = cert.getNotBefore();
    this.notAfter = cert.getNotAfter();
    this.serialNumber = cert.getSerialNumber();

    this.issuer = X500Name.getInstance(cert.getIssuerX500Principal().getEncoded());
    this.subject = X500Name.getInstance(cert.getSubjectX500Principal().getEncoded());

    this.selfSigned = subject.equals(issuer);
  }

  public X509Cert(X509CertificateHolder cert) {
    this(cert, null);
  }

  public X509Cert(X509CertificateHolder cert, byte[] encoded) {
    this.bcInstance = notNull(cert, "cert");
    this.jceInstance = null;
    this.encoded = encoded;

    this.notBefore = cert.getNotBefore();
    this.notAfter = cert.getNotAfter();
    this.serialNumber = cert.getSerialNumber();

    this.issuer = cert.getIssuer();
    this.subject = cert.getSubject();
    this.selfSigned = subject.equals(issuer);
  }

  /**
   * Gets the certificate constraints path length from the
   * critical {@code BasicConstraints} extension, (OID = 2.5.29.19).
   * 

* The basic constraints extension identifies whether the subject * of the certificate is a Certificate Authority (CA) and * how deep a certification path may exist through that CA. The * {@code pathLenConstraint} field (see below) is meaningful * only if {@code cA} is set to TRUE. In this case, it gives the * maximum number of CA certificates that may follow this certificate in a * certification path. A value of zero indicates that only an end-entity * certificate may follow in the path. *

* The ASN.1 definition for this is: *

   * BasicConstraints ::= SEQUENCE {
   *     cA                  BOOLEAN DEFAULT FALSE,
   *     pathLenConstraint   INTEGER (0..MAX) OPTIONAL }
   * 
* * @return the value of {@code pathLenConstraint} if the * BasicConstraints extension is present in the certificate and the * subject of the certificate is a CA, otherwise -1. * If the subject of the certificate is a CA and * {@code pathLenConstraint} does not appear, * {@code Integer.MAX_VALUE} is returned to indicate that there is no * limit to the allowed length of the certification path. */ public int getBasicConstraints() { if (basicConstrains == -2) { synchronized (sync) { if (bcInstance != null) { byte[] extnValue = getCoreExtValue(Extension.basicConstraints); if (extnValue == null) { basicConstrains = -1; } else { BasicConstraints bc = BasicConstraints.getInstance(extnValue); if (bc.isCA()) { BigInteger bn = bc.getPathLenConstraint(); basicConstrains = bn == null ? Integer.MAX_VALUE : bn.intValueExact(); } else { basicConstrains = -1; } } } else { basicConstrains = jceInstance.getBasicConstraints(); } } } return basicConstrains; } public BigInteger getSerialNumber() { return serialNumber; } public String getSerialNumberHex() { return "0x" + Hex.encode(serialNumber.toByteArray()); } public PublicKey getPublicKey() { if (publicKey == null) { synchronized (sync) { if (bcInstance != null) { try { this.publicKey = KeyUtil.generatePublicKey(bcInstance.getSubjectPublicKeyInfo()); } catch (InvalidKeySpecException ex) { throw new IllegalStateException(ex.getMessage(), ex); } } else { publicKey = jceInstance.getPublicKey(); } } } return publicKey; } public boolean[] getKeyUsage() { if (!keyUsageProcessed) { synchronized (sync) { if (bcInstance != null) { byte[] extnValue = getCoreExtValue(Extension.keyUsage); if (extnValue == null) { keyUsage = null; } else { org.bouncycastle.asn1.x509.KeyUsage bc = org.bouncycastle.asn1.x509.KeyUsage.getInstance(extnValue); keyUsage = new boolean[9]; for (KeyUsage ku : KeyUsage.values()) { keyUsage[ku.getBit()] = bc.hasUsages(ku.getBcUsage()); } } } else { keyUsage = jceInstance.getKeyUsage(); } } keyUsageProcessed = true; } return keyUsage; } public X500Name getIssuer() { return issuer; } public X500Name getSubject() { return subject; } public byte[] getSubjectKeyId() { if (subjectKeyId == null) { synchronized (sync) { byte[] extnValue = getCoreExtValue(Extension.subjectKeyIdentifier); if (extnValue != null) { subjectKeyId = ASN1OctetString.getInstance(extnValue).getOctets(); } } } return subjectKeyId; } public byte[] getAuthorityKeyId() { if (authorityKeyId == null) { synchronized (sync) { byte[] extnValue = getCoreExtValue(Extension.authorityKeyIdentifier); if (extnValue != null) { authorityKeyId = AuthorityKeyIdentifier.getInstance(extnValue).getKeyIdentifier(); } } } return authorityKeyId; } public String getSubjectRfc4519Text() { if (subjectRfc4519Text == null) { synchronized (sync) { subjectRfc4519Text = RFC4519Style.INSTANCE.toString(subject); } } return subjectRfc4519Text; } public String getIssuerRfc4519Text() { if (issuerRfc4519Text == null) { synchronized (sync) { issuerRfc4519Text = RFC4519Style.INSTANCE.toString(subject); } } return issuerRfc4519Text; } public SubjectPublicKeyInfo getSubjectPublicKeyInfo() { if (subjectPublicKeyInfo == null) { synchronized (sync) { if (bcInstance != null) { subjectPublicKeyInfo = bcInstance.getSubjectPublicKeyInfo(); } else { try { subjectPublicKeyInfo = KeyUtil.createSubjectPublicKeyInfo(jceInstance.getPublicKey()); } catch (InvalidKeyException ex) { throw new IllegalStateException("error creating SubjectPublicKeyInfo from PublicKey", ex); } } } } return subjectPublicKeyInfo; } public X509Certificate toJceCert() { if (jceInstance == null) { synchronized (sync) { encoded = getEncoded(); try { jceInstance = X509Util.parseX509Certificate(new ByteArrayInputStream(encoded)); } catch (CertificateException ex) { throw new IllegalStateException("error converting to X509Certificate", ex); } } } return jceInstance; } public X509CertificateHolder toBcCert() { if (bcInstance == null) { synchronized (sync) { try { encoded = jceInstance.getEncoded(); bcInstance = new X509CertificateHolder(encoded); } catch (CertificateEncodingException | IOException ex) { throw new IllegalStateException("error encoding certificate", ex); } } } return bcInstance; } public boolean isSelfSigned() { return selfSigned; } public Date getNotBefore() { return notBefore; } public Date getNotAfter() { return notAfter; } public byte[] getEncoded() { if (encoded == null) { synchronized (sync) { try { encoded = (bcInstance != null) ? bcInstance.getEncoded() : jceInstance.getEncoded(); } catch (CertificateEncodingException | IOException ex) { throw new IllegalStateException("error encoding certificate", ex); } } } return encoded; } public String getCommonName() { return X509Util.getCommonName(subject); } public void verify(PublicKey key) throws SignatureException, InvalidKeyException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException { if (jceInstance != null) { jceInstance.verify(key); } else { String sigName = AlgorithmUtil.getSignatureAlgoName(bcInstance.getSignatureAlgorithm()); Signature signature = Signature.getInstance(sigName); checkBcSignature(key, signature); } } public void verify(PublicKey key, Provider sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { if (sigProvider == null) { verify(key); } else { if (jceInstance != null) { jceInstance.verify(key, sigProvider); } else { String sigName = AlgorithmUtil.getSignatureAlgoName(bcInstance.getSignatureAlgorithm()); Signature signature = Signature.getInstance(sigName, sigProvider); checkBcSignature(key, signature); } } } public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { if (sigProvider == null) { verify(key); } else { if (jceInstance != null) { jceInstance.verify(key, sigProvider); } else { String sigName = AlgorithmUtil.getSignatureAlgoName(bcInstance.getSignatureAlgorithm()); Signature signature = Signature.getInstance(sigName, sigProvider); checkBcSignature(key, signature); } } } private void checkBcSignature(PublicKey key, Signature signature) throws CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException { Certificate c = bcInstance.toASN1Structure(); if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature())) { throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); } signature.initVerify(key); try { signature.update(c.getTBSCertificate().getEncoded()); } catch (IOException ex) { throw new CertificateException("error encoding TBSCertificate"); } if (!signature.verify(c.getSignature().getBytes())) { throw new SignatureException("certificate does not verify with supplied key"); } } public byte[] getExtensionCoreValue(ASN1ObjectIdentifier extnType) { if (bcInstance != null) { Extension extn = bcInstance.getExtensions().getExtension(extnType); return extn == null ? null : extn.getExtnValue().getOctets(); } else { byte[] rawValue = jceInstance.getExtensionValue(extnType.getId()); return rawValue == null ? null : ASN1OctetString.getInstance(rawValue).getOctets(); } } public boolean hasKeyusage(KeyUsage usage) { boolean[] usages = getKeyUsage(); return usages == null ? true : usages[usage.getBit()]; } @Override public int hashCode() { return Arrays.hashCode(getEncoded()); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } else if (!(obj instanceof X509Cert)) { return false; } return Arrays.equals(getEncoded(), ((X509Cert) obj).getEncoded()); } private byte[] getCoreExtValue(ASN1ObjectIdentifier extnType) { if (bcInstance != null) { Extensions extns = bcInstance.getExtensions(); if (extns == null) { return null; } Extension extn = extns.getExtension(extnType); return extn == null ? null : extn.getExtnValue().getOctets(); } else { byte[] rawValue = jceInstance.getExtensionValue(extnType.getId()); return rawValue == null ? null : ASN1OctetString.getInstance(rawValue).getOctets(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy