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

org.xbib.net.security.ssl.util.CertificateUtils Maven / Gradle / Ivy

The newest version!
package org.xbib.net.security.ssl.util;

import org.xbib.net.security.ssl.exception.GenericCertificateException;
import org.xbib.net.security.ssl.exception.GenericIOException;

import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class CertificateUtils {

    private static final String CERTIFICATE_TYPE = "X.509";
    private static final String P7B_HEADER = "-----BEGIN PKCS7-----";
    private static final String P7B_FOOTER = "-----END PKCS7-----";
    private static final String PEM_HEADER = "-----BEGIN CERTIFICATE-----";
    private static final String PEM_FOOTER = "-----END CERTIFICATE-----";
    private static final Pattern PEM_PATTERN = Pattern.compile(PEM_HEADER + "(.*?)" + PEM_FOOTER, Pattern.DOTALL);
    private static final Pattern P7B_PATTERN = Pattern.compile(P7B_HEADER + "(.*?)" + P7B_FOOTER, Pattern.DOTALL);

    private static final String EMPTY = "";

    private CertificateUtils() {}

    public static String generateAlias(Certificate certificate) {
        if (certificate instanceof X509Certificate) {
            return ((X509Certificate) certificate)
                    .getSubjectX500Principal()
                    .getName()
                    .toLowerCase(Locale.ENGLISH);
        } else {
            return UUID.randomUUID().toString().toLowerCase(Locale.ENGLISH);
        }
    }

    /**
     * Loads certificates from the classpath and maps it into a list of {@link Certificate}.
     * 
* Supported input format: PEM, P7B and DER */ public static List loadCertificate(String... certificatePaths) { return loadCertificate(certificatePath -> ValidationUtils.requireNotNull( CertificateUtils.class.getClassLoader().getResourceAsStream(certificatePath), String.format("Failed to load the certificate from the classpath for the given path: [%s]", certificatePath)), certificatePaths ); } /** * Loads certificates from the filesystem and maps it into a list of {@link Certificate}. *
* Supported input format: PEM, P7B and DER */ public static List loadCertificate(Path... certificatePaths) { return loadCertificate(certificatePath -> { try { return Files.newInputStream(certificatePath, StandardOpenOption.READ); } catch (IOException exception) { throw new GenericIOException(exception); } }, certificatePaths); } /** * Loads certificates from multiple InputStreams and maps it into a list of {@link Certificate}. *
* Supported input format: PEM, P7B and DER */ public static List loadCertificate(InputStream... certificateStreams) { return loadCertificate(certificateStream -> ValidationUtils.requireNotNull(certificateStream, "Failed to load the certificate from the provided InputStream because it is null"), certificateStreams ); } private static List loadCertificate(Function resourceMapper, T[] resources) { List certificates = new ArrayList<>(); for (T resource : resources) { try (InputStream certificateStream = resourceMapper.apply(resource)) { certificates.addAll(parseCertificate(certificateStream)); } catch (Exception e) { throw new GenericIOException(e); } } return Collections.unmodifiableList(certificates); } /** * Tries to map the InputStream to a list of {@link Certificate}. * It assumes that the content of the InputStream is either PEM, P7B or DER. * The InputStream will copied into an OutputStream so it can be read multiple times. */ private static List parseCertificate(InputStream certificateStream) { List certificates; byte[] certificateData = IOUtils.copyToByteArray(certificateStream); String certificateContent = new String(certificateData, StandardCharsets.UTF_8); if (isPemFormatted(certificateContent)) { certificates = parsePemCertificate(certificateContent); } else if(isP7bFormatted(certificateContent)) { certificates = parseP7bCertificate(certificateContent); } else { certificates = parseDerCertificate(new ByteArrayInputStream(certificateData)); } return certificates; } private static boolean isPemFormatted(String certificateContent) { return PEM_PATTERN.matcher(certificateContent).find(); } private static boolean isP7bFormatted(String certificateContent) { return P7B_PATTERN.matcher(certificateContent).find(); } /** * Parses PEM formatted certificates containing a * header as -----BEGIN CERTIFICATE----- and footer as -----END CERTIFICATE----- * or header as -----BEGIN PKCS7----- and footer as -----END PKCS7----- * with a base64 encoded data between the header and footer. */ public static List parsePemCertificate(String certificateContent) { Matcher pemMatcher = PEM_PATTERN.matcher(certificateContent); return parseCertificate(pemMatcher); } /** * Parses P7B formatted certificates containing a * header as -----BEGIN PKCS7----- and footer as -----END PKCS7----- * with a base64 encoded data between the header and footer. */ public static List parseP7bCertificate(String certificateContent) { Matcher p7bMatcher = P7B_PATTERN.matcher(certificateContent); return parseCertificate(p7bMatcher); } private static List parseCertificate(Matcher certificateMatcher) { List certificates = new ArrayList<>(); while (certificateMatcher.find()) { String certificate = certificateMatcher.group(1); String sanitizedCertificate = certificate.replaceAll("[\\n|\\r]+", EMPTY).trim(); byte[] decodedCertificate = Base64.getDecoder().decode(sanitizedCertificate); ByteArrayInputStream certificateAsInputStream = new ByteArrayInputStream(decodedCertificate); List parsedCertificates = CertificateUtils.parseDerCertificate(certificateAsInputStream); certificates.addAll(parsedCertificates); IOUtils.closeSilently(certificateAsInputStream); } return Collections.unmodifiableList(certificates); } public static List parseDerCertificate(InputStream certificateStream) { try(BufferedInputStream bufferedCertificateStream = new BufferedInputStream(certificateStream)) { CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE); return certificateFactory.generateCertificates(bufferedCertificateStream).stream() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); } catch (CertificateException | IOException e) { throw new GenericCertificateException("There is no valid certificate present to parse. Please make sure to supply a valid der formatted certificate", e); } } public static List getJdkTrustedCertificates() { return Collections.unmodifiableList( Arrays.asList( TrustManagerUtils.createTrustManagerWithJdkTrustedCertificates().getAcceptedIssuers() ) ); } public static List getSystemTrustedCertificates() { return TrustManagerUtils.createTrustManagerWithSystemTrustedCertificates() .map(X509TrustManager::getAcceptedIssuers) .map(Arrays::asList) .map(Collections::unmodifiableList) .orElseGet(Collections::emptyList); } public static Map> getCertificateAsPem(String... urls) { return getCertificateAsPem(Arrays.asList(urls)); } public static Map> getCertificateAsPem(List urls) { Map> certificates = CertificateUtils.getCertificate(urls) .entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> CertificateUtils.convertToPem(entry.getValue()))); return Collections.unmodifiableMap(certificates); } public static Map> getCertificate(String... urls) { return CertificateUtils.getCertificate(Arrays.asList(urls)); } public static Map> getCertificate(List urls) { return urls.stream() .map(url -> new AbstractMap.SimpleEntry<>(url, CertificateExtractorUtils.getInstance().getCertificateFromExternalSource(url))) .collect(Collectors.collectingAndThen(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue), Collections::unmodifiableMap)); } public static List convertToPem(List certificates) { return certificates.stream() .map(CertificateUtils::convertToPem) .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); } public static String convertToPem(Certificate certificate) { try { byte[] encodedCertificate = certificate.getEncoded(); byte[] base64EncodedCertificate = Base64.getEncoder().encode(encodedCertificate); String parsedCertificate = new String(base64EncodedCertificate); List certificateContainer = Stream.of(parsedCertificate.split("(?<=\\G.{64})")) .collect(Collectors.toCollection(ArrayList::new)); certificateContainer.add(0, PEM_HEADER); certificateContainer.add(PEM_FOOTER); if (certificate instanceof X509Certificate) { X509Certificate x509Certificate = (X509Certificate) certificate; X500Principal issuer = x509Certificate.getIssuerX500Principal(); certificateContainer.add(0, String.format("issuer=%s", issuer.getName())); X500Principal subject = x509Certificate.getSubjectX500Principal(); certificateContainer.add(0, String.format("subject=%s", subject.getName())); } return String.join(System.lineSeparator(), certificateContainer); } catch (CertificateEncodingException e) { throw new GenericCertificateException(e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy