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

com.yahoo.security.X509CertificateUtils Maven / Gradle / Ivy

There is a newer version: 8.411.13
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.util.io.pem.PemObject;

import javax.naming.NamingException;
import javax.naming.ldap.LdapName;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
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.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

import static com.yahoo.security.Extension.SUBJECT_ALTERNATIVE_NAMES;

/**
 * @author bjorncs
 */
public class X509CertificateUtils {

    private X509CertificateUtils() {}

    public static X509Certificate fromPem(String pem) {
        try (PEMParser parser = new PEMParser(new StringReader(pem))) {
            return toX509Certificate(parser.readObject());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        }
    }

    public static List certificateListFromPem(String pem) {
        try (PEMParser parser = new PEMParser(new StringReader(pem))) {
            List list = new ArrayList<>();
            Object pemObject;
            while ((pemObject = parser.readObject()) != null) {
                list.add(toX509Certificate(pemObject));
            }
            return list;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        }
    }

    private static X509Certificate toX509Certificate(Object pemObject) throws CertificateException {
        if (pemObject instanceof X509Certificate certificate) {
            return certificate;
        }
        if (pemObject instanceof X509CertificateHolder certificateHolder) {
            return new JcaX509CertificateConverter()
                    .setProvider(BouncyCastleProviderHolder.getInstance())
                    .getCertificate(certificateHolder);
        }
        if (pemObject instanceof PrivateKeyInfo) {
            throw new IllegalArgumentException("Expected X509 certificate, but got private key");
        }
        throw new IllegalArgumentException("Invalid type of PEM object, got " + pemObject.getClass().getName());
    }

    public static String toPem(X509Certificate certificate) {
        try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
            pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
            pemWriter.flush();
            return stringWriter.toString();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static String toPem(List certificates) {
        try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
            for (X509Certificate certificate : certificates) {
                pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
            }
            pemWriter.flush();
            return stringWriter.toString();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static List getSubjectCommonNames(X509Certificate certificate) {
        return getCommonNames(certificate.getSubjectX500Principal());
    }

    public static Optional getSubjectCommonName(X509Certificate c) {
        List names = getSubjectCommonNames(c);
        if (names.isEmpty()) return Optional.empty();
        return Optional.of(names.get(names.size() - 1));
    }

    public static List getIssuerCommonNames(X509Certificate certificate) {
        return getCommonNames(certificate.getIssuerX500Principal());
    }

    public static List getSubjectOrganizationalUnits(X509Certificate certificate) {
        return getRdns(certificate.getSubjectX500Principal(), "OU");
    }

    public static List getCommonNames(X500Principal distinguishedName) {
        return getRdns(distinguishedName, "CN");
    }

    private static List getRdns(X500Principal distinguishedName, String rdnName) {
        try {
            return new LdapName(distinguishedName.getName()).getRdns().stream()
                    .filter(rdn -> rdn.getType().equalsIgnoreCase(rdnName))
                    .map(rdn -> rdn.getValue().toString())
                    .toList();
        } catch (NamingException e) {
            throw new IllegalArgumentException("Invalid DN: " + distinguishedName.getName(), e);
        }
    }

    public static List getSubjectAlternativeNames(X509Certificate certificate) {
        try {
            byte[] extensionValue = certificate.getExtensionValue(SUBJECT_ALTERNATIVE_NAMES.getOId());
            if (extensionValue == null) return List.of();
            ASN1Encodable asn1Encodable = ASN1Primitive.fromByteArray(extensionValue);
            if (asn1Encodable instanceof ASN1OctetString) {
                asn1Encodable = ASN1Primitive.fromByteArray(((ASN1OctetString) asn1Encodable).getOctets());
            }
            GeneralNames names = GeneralNames.getInstance(asn1Encodable);
            return SubjectAlternativeName.fromGeneralNames(names);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public static boolean privateKeyMatchesPublicKey(PrivateKey privateKey, PublicKey publicKey) {
        byte[] someRandomData = new byte[64];
        new Random().nextBytes(someRandomData);

        Signature signer = SignatureUtils.createSigner(privateKey);
        Signature verifier = SignatureUtils.createVerifier(publicKey);
        try {
            signer.update(someRandomData);
            verifier.update(someRandomData);
            byte[] signature = signer.sign();
            return verifier.verify(signature);
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        }
    }

    public static X509CertificateWithKey createSelfSigned(String cn, Duration duration) {
        KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
        X500Principal subject = new X500Principal(cn);
        Instant now = Instant.now();
        X509Certificate cert =
                X509CertificateBuilder.fromKeypair(keyPair, subject, now,
                                                   now.plus(duration), SignatureAlgorithm.SHA256_WITH_ECDSA,
                                                   BigInteger.ONE)
                        .setBasicConstraints(true, true)
                        .build();
        return new X509CertificateWithKey(cert, keyPair.getPrivate());
    }

    /**
     * @return certificate SHA-1 fingerprint
     */
    public static byte[] getX509CertificateFingerPrint(X509Certificate certificate) {
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            return sha1.digest(certificate.getEncoded());
        } catch (CertificateEncodingException | NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy