org.cryptacular.util.CertUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Liferay SAML OpenSAML Integration
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.GeneralNamesBuilder;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.cryptacular.EncodingException;
import org.cryptacular.StreamException;
import org.cryptacular.x509.ExtensionReader;
import org.cryptacular.x509.GeneralNameType;
import org.cryptacular.x509.KeyUsageBits;
import org.cryptacular.x509.dn.NameReader;
import org.cryptacular.x509.dn.StandardAttributeType;
/**
* Utility class providing convenience methods for common operations on X.509 certificates.
*
* @author Middleware Services
*/
public final class CertUtil
{
/** Private constructor of utility class. */
private CertUtil() {}
/**
* Gets the common name attribute (CN) of the certificate subject distinguished name.
*
* @param cert Certificate to examine.
*
* @return Subject CN or null if no CN attribute is defined in the subject DN.
*
* @throws EncodingException on cert field extraction.
*/
public static String subjectCN(final X509Certificate cert) throws EncodingException
{
return new NameReader(cert).readSubject().getValue(StandardAttributeType.CommonName);
}
/**
* Gets all subject alternative names defined on the given certificate.
*
* @param cert X.509 certificate to examine.
*
* @return List of subject alternative names or null if no subject alt names are defined.
*
* @throws EncodingException on cert field extraction.
*/
public static GeneralNames subjectAltNames(final X509Certificate cert) throws EncodingException
{
return new ExtensionReader(cert).readSubjectAlternativeName();
}
/**
* Gets all subject alternative names of the given type(s) on the given cert.
*
* @param cert X.509 certificate to examine.
* @param types One or more subject alternative name types to fetch.
*
* @return List of subject alternative names of the matching type(s) or null if none found.
*
* @throws EncodingException on cert field extraction.
*/
public static GeneralNames subjectAltNames(final X509Certificate cert, final GeneralNameType... types)
throws EncodingException
{
final GeneralNamesBuilder builder = new GeneralNamesBuilder();
final GeneralNames altNames = subjectAltNames(cert);
if (altNames != null) {
for (GeneralName name : altNames.getNames()) {
for (GeneralNameType type : types) {
if (type.ordinal() == name.getTagNo()) {
builder.addName(name);
}
}
}
}
final GeneralNames names = builder.build();
if (names.getNames().length == 0) {
return null;
}
return names;
}
/**
* Gets a list of all subject names defined for the given certificate. The list includes the first common name (CN)
* specified in the subject distinguished name (if defined) and all subject alternative names.
*
* @param cert X.509 certificate to examine.
*
* @return List of subject names.
*
* @throws EncodingException on cert field extraction.
*/
public static List subjectNames(final X509Certificate cert) throws EncodingException
{
final List names = new ArrayList<>();
final String cn = subjectCN(cert);
if (cn != null) {
names.add(cn);
}
final GeneralNames altNames = subjectAltNames(cert);
if (altNames == null) {
return names;
}
for (GeneralName name : altNames.getNames()) {
names.add(name.getName().toString());
}
return names;
}
/**
* Gets a list of subject names defined for the given certificate. The list includes the first common name (CN)
* specified in the subject distinguished name (if defined) and all subject alternative names of the given type.
*
* @param cert X.509 certificate to examine.
* @param types One or more subject alternative name types to fetch.
*
* @return List of subject names.
*
* @throws EncodingException on cert field extraction.
*/
public static List subjectNames(final X509Certificate cert, final GeneralNameType... types)
throws EncodingException
{
final List names = new ArrayList<>();
final String cn = subjectCN(cert);
if (cn != null) {
names.add(cn);
}
final GeneralNames altNames = subjectAltNames(cert, types);
if (altNames == null) {
return names;
}
for (GeneralName name : altNames.getNames()) {
names.add(name.getName().toString());
}
return names;
}
/**
* Finds a certificate whose public key is paired with the given private key.
*
* @param key Private key used to find matching public key.
* @param candidates Array of candidate certificates.
*
* @return Certificate whose public key forms a keypair with the private key or null if no match is found.
*
* @throws EncodingException on cert field extraction.
*/
public static X509Certificate findEntityCertificate(final PrivateKey key, final X509Certificate... candidates)
throws EncodingException
{
return findEntityCertificate(key, Arrays.asList(candidates));
}
/**
* Finds a certificate whose public key is paired with the given private key.
*
* @param key Private key used to find matching public key.
* @param candidates Collection of candidate certificates.
*
* @return Certificate whose public key forms a keypair with the private key or null if no match is found.
*
* @throws EncodingException on cert field extraction.
*/
public static X509Certificate findEntityCertificate(
final PrivateKey key,
final Collection candidates)
throws EncodingException
{
for (X509Certificate candidate : candidates) {
if (KeyPairUtil.isKeyPair(candidate.getPublicKey(), key)) {
return candidate;
}
}
return null;
}
/**
* Reads an X.509 certificate from ASN.1 encoded format in the file at the given location.
*
* @param path Path to file containing an DER or PEM encoded X.509 certificate.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate readCertificate(final String path) throws EncodingException, StreamException
{
return readCertificate(StreamUtil.makeStream(new File(path)));
}
/**
* Reads an X.509 certificate from ASN.1 encoded format from the given file.
*
* @param file File containing an DER or PEM encoded X.509 certificate.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate readCertificate(final File file) throws EncodingException, StreamException
{
return readCertificate(StreamUtil.makeStream(file));
}
/**
* Reads an X.509 certificate from ASN.1 encoded data in the given stream.
*
* @param in Input stream containing PEM or DER encoded X.509 certificate.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate readCertificate(final InputStream in) throws EncodingException, StreamException
{
try {
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate) factory.generateCertificate(in);
} catch (CertificateException e) {
if (e.getCause() instanceof IOException) {
throw new StreamException((IOException) e.getCause());
}
throw new EncodingException("Cannot decode certificate", e);
}
}
/**
* Creates an X.509 certificate from its ASN.1 encoded form.
*
* @param encoded PEM or DER encoded ASN.1 data.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
*/
public static X509Certificate decodeCertificate(final byte[] encoded) throws EncodingException
{
return readCertificate(new ByteArrayInputStream(encoded));
}
/**
* Reads an X.509 certificate chain from ASN.1 encoded format in the file at the given location.
*
* @param path Path to file containing a sequence of PEM or DER encoded certificates or PKCS#7 certificate chain.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate[] readCertificateChain(final String path) throws EncodingException, StreamException
{
return readCertificateChain(StreamUtil.makeStream(new File(path)));
}
/**
* Reads an X.509 certificate chain from ASN.1 encoded format from the given file.
*
* @param file File containing a sequence of PEM or DER encoded certificates or PKCS#7 certificate chain.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate[] readCertificateChain(final File file) throws EncodingException, StreamException
{
return readCertificateChain(StreamUtil.makeStream(file));
}
/**
* Reads an X.509 certificate chain from ASN.1 encoded data in the given stream.
*
* @param in Input stream containing a sequence of PEM or DER encoded certificates or PKCS#7 certificate chain.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
* @throws StreamException on IO errors.
*/
public static X509Certificate[] readCertificateChain(final InputStream in) throws EncodingException, StreamException
{
try {
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
final Collection extends Certificate> certs = factory.generateCertificates(in);
return certs.toArray(new X509Certificate[certs.size()]);
} catch (CertificateException e) {
if (e.getCause() instanceof IOException) {
throw new StreamException((IOException) e.getCause());
}
throw new EncodingException("Cannot decode certificate", e);
}
}
/**
* Creates an X.509 certificate chain from its ASN.1 encoded form.
*
* @param encoded Sequence of PEM or DER encoded certificates or PKCS#7 certificate chain.
*
* @return Certificate.
*
* @throws EncodingException on cert parsing errors.
*/
public static X509Certificate[] decodeCertificateChain(final byte[] encoded) throws EncodingException
{
return readCertificateChain(new ByteArrayInputStream(encoded));
}
/**
* Determines whether the certificate allows the given basic key usages.
*
* @param cert Certificate to check.
* @param bits One or more basic key usage types to check.
*
* @return True if certificate allows all given usage types, false otherwise.
*
* @throws EncodingException on cert field extraction.
*/
public static boolean allowsUsage(final X509Certificate cert, final KeyUsageBits... bits) throws EncodingException
{
final KeyUsage usage = new ExtensionReader(cert).readKeyUsage();
for (KeyUsageBits bit : bits) {
if (!bit.isSet(usage)) {
return false;
}
}
return true;
}
/**
* Determines whether the certificate allows the given extended key usages.
*
* @param cert Certificate to check.
* @param purposes One ore more extended key usage purposes to check.
*
* @return True if certificate allows all given purposes, false otherwise.
*
* @throws EncodingException on cert field extraction.
*/
public static boolean allowsUsage(final X509Certificate cert, final KeyPurposeId... purposes) throws EncodingException
{
final List allowedUses = new ExtensionReader(cert).readExtendedKeyUsage();
for (KeyPurposeId purpose : purposes) {
if (allowedUses == null || !allowedUses.contains(purpose)) {
return false;
}
}
return true;
}
/**
* Determines whether the certificate defines all of the given certificate policies.
*
* @param cert Certificate to check.
* @param policyOidsToCheck One or more certificate policy OIDs to check.
*
* @return True if certificate defines all given policy OIDs, false otherwise.
*
* @throws EncodingException on cert field extraction.
*/
public static boolean hasPolicies(final X509Certificate cert, final String... policyOidsToCheck)
throws EncodingException
{
final List policies = new ExtensionReader(cert).readCertificatePolicies();
boolean hasPolicy;
for (String policyOid : policyOidsToCheck) {
hasPolicy = false;
if (policies != null) {
for (PolicyInformation policy : policies) {
if (policy.getPolicyIdentifier().getId().equals(policyOid)) {
hasPolicy = true;
break;
}
}
}
if (!hasPolicy) {
return false;
}
}
return true;
}
/**
* Gets the subject key identifier of the given certificate in delimited hexadecimal format, e.g.
* 25:48:2f:28:ec:5d:19:bb:1d:25:ae:94:93:b1:7b:b5:35:96:24:66
.
*
* @param cert Certificate to process.
*
* @return Subject key identifier in colon-delimited hex format.
*
* @throws EncodingException on cert field extraction.
*/
public static String subjectKeyId(final X509Certificate cert) throws EncodingException
{
return CodecUtil.hex(new ExtensionReader(cert).readSubjectKeyIdentifier().getKeyIdentifier(), true);
}
/**
* Gets the authority key identifier of the given certificate in delimited hexadecimal format, e.g.
* 25:48:2f:28:ec:5d:19:bb:1d:25:ae:94:93:b1:7b:b5:35:96:24:66
.
*
* @param cert Certificate to process.
*
* @return Authority key identifier in colon-delimited hex format.
*
* @throws EncodingException on cert field extraction.
*/
public static String authorityKeyId(final X509Certificate cert) throws EncodingException
{
return CodecUtil.hex(new ExtensionReader(cert).readAuthorityKeyIdentifier().getKeyIdentifier(), true);
}
}