Home / Class/ SslInfo Class — spring-boot Architecture

SslInfo Class — spring-boot Architecture

Architecture documentation for the SslInfo class in SslInfo.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

core/spring-boot/src/main/java/org/springframework/boot/info/SslInfo.java lines 49–333

public class SslInfo {

	private final SslBundles sslBundles;

	private final Clock clock;

	/**
	 * Creates a new instance.
	 * @param sslBundles the {@link SslBundles} to extract the info from
	 * @since 4.0.0
	 */
	public SslInfo(SslBundles sslBundles) {
		this(sslBundles, Clock.systemDefaultZone());
	}

	/**
	 * Creates a new instance.
	 * @param sslBundles the {@link SslBundles} to extract the info from
	 * @param clock the {@link Clock} to use
	 * @since 4.0.0
	 */
	public SslInfo(SslBundles sslBundles, Clock clock) {
		this.sslBundles = sslBundles;
		this.clock = clock;
	}

	/**
	 * Returns information on all SSL bundles.
	 * @return information on all SSL bundles
	 */
	public List<BundleInfo> getBundles() {
		return this.sslBundles.getBundleNames()
			.stream()
			.map((name) -> new BundleInfo(name, this.sslBundles.getBundle(name)))
			.toList();
	}

	/**
	 * Returns an SSL bundle by name.
	 * @param name the name of the SSL bundle
	 * @return the {@link BundleInfo} for the given SSL bundle
	 * @throws NoSuchSslBundleException if a bundle with the provided name does not exist
	 * @since 3.5.0
	 */
	public BundleInfo getBundle(String name) {
		SslBundle bundle = this.sslBundles.getBundle(name);
		return new BundleInfo(name, bundle);
	}

	/**
	 * Info about a single {@link SslBundle}.
	 */
	public final class BundleInfo {

		private final String name;

		private final List<CertificateChainInfo> certificateChains;

		private final List<CertificateChainInfo> trustStoreCertificateChains;

		private BundleInfo(String name, SslBundle sslBundle) {
			this.name = name;
			this.certificateChains = extractCertificateChains(sslBundle.getStores().getKeyStore());
			this.trustStoreCertificateChains = extractCertificateChains(sslBundle.getStores().getTrustStore());
		}

		private List<CertificateChainInfo> extractCertificateChains(@Nullable KeyStore keyStore) {
			if (keyStore == null) {
				return Collections.emptyList();
			}
			try {
				return Collections.list(keyStore.aliases())
					.stream()
					.map((alias) -> new CertificateChainInfo(keyStore, alias))
					.toList();
			}
			catch (KeyStoreException ex) {
				return Collections.emptyList();
			}
		}

		public String getName() {
			return this.name;
		}

		public List<CertificateChainInfo> getCertificateChains() {
			return this.certificateChains;
		}

		public List<CertificateChainInfo> getTrustStoreCertificateChains() {
			return this.trustStoreCertificateChains;
		}

	}

	/**
	 * Info about a single certificate chain.
	 */
	public final class CertificateChainInfo {

		private final String alias;

		private final List<CertificateInfo> certificates;

		CertificateChainInfo(KeyStore keyStore, String alias) {
			this.alias = alias;
			this.certificates = extractCertificates(keyStore, alias);
		}

		private List<CertificateInfo> extractCertificates(KeyStore keyStore, String alias) {
			try {
				List<CertificateInfo> certificates = readCertificateChain(keyStore, alias);
				if (certificates != null) {
					return certificates;
				}
				List<CertificateInfo> certificate = readCertificate(keyStore, alias);
				if (certificate != null) {
					return certificate;
				}
				return Collections.emptyList();
			}
			catch (KeyStoreException ex) {
				return Collections.emptyList();
			}
		}

		private @Nullable List<CertificateInfo> readCertificate(KeyStore keyStore, String alias)
				throws KeyStoreException {
			Certificate certificate = keyStore.getCertificate(alias);
			return (certificate != null) ? List.of(new CertificateInfo(certificate)) : null;
		}

		private @Nullable List<CertificateInfo> readCertificateChain(KeyStore keyStore, String alias)
				throws KeyStoreException {
			Certificate[] certificates = keyStore.getCertificateChain(alias);
			return ObjectUtils.isEmpty(certificates) ? null
					: Arrays.stream(certificates).map(CertificateInfo::new).toList();
		}

		public String getAlias() {
			return this.alias;
		}

		public List<CertificateInfo> getCertificates() {
			return this.certificates;
		}

	}

	/**
	 * Info about a certificate.
	 */
	public final class CertificateInfo {

		private final @Nullable X509Certificate certificate;

		private CertificateInfo(Certificate certificate) {
			this.certificate = (certificate instanceof X509Certificate x509Certificate) ? x509Certificate : null;
		}

		public @Nullable String getSubject() {
			return extract(X509Certificate::getSubjectX500Principal, X500Principal::getName);
		}

		public @Nullable String getIssuer() {
			return extract(X509Certificate::getIssuerX500Principal, X500Principal::getName);
		}

		public @Nullable String getSerialNumber() {
			return extract(X509Certificate::getSerialNumber, (serial) -> serial.toString(16));
		}

		public @Nullable String getVersion() {
			return extract((certificate) -> "V" + certificate.getVersion());
		}

		public @Nullable String getSignatureAlgorithmName() {
			return extract(X509Certificate::getSigAlgName);
		}

		public @Nullable Instant getValidityStarts() {
			return extract(X509Certificate::getNotBefore, Date::toInstant);
		}

		public @Nullable Instant getValidityEnds() {
			return extract(X509Certificate::getNotAfter, Date::toInstant);
		}

		public @Nullable CertificateValidityInfo getValidity() {
			return extract((certificate) -> {
				Instant starts = getValidityStarts();
				Instant ends = getValidityEnds();
				Assert.state(starts != null, "Validity start not found");
				Assert.state(ends != null, "Validity end not found");
				CertificateValidityInfo.Status validity = checkValidity(starts, ends);
				return switch (validity) {
					case VALID -> CertificateValidityInfo.VALID;
					case EXPIRED -> new CertificateValidityInfo(Status.EXPIRED, "Not valid after %s", ends);
					case NOT_YET_VALID ->
						new CertificateValidityInfo(Status.NOT_YET_VALID, "Not valid before %s", starts);
				};
			});
		}

		private CertificateValidityInfo.Status checkValidity(Instant starts, Instant ends) {
			Instant now = SslInfo.this.clock.instant();
			if (now.isBefore(starts)) {
				return CertificateValidityInfo.Status.NOT_YET_VALID;
			}
			if (now.isAfter(ends)) {
				return CertificateValidityInfo.Status.EXPIRED;
			}
			return CertificateValidityInfo.Status.VALID;
		}

		private <V, R> @Nullable R extract(Function<X509Certificate, V> valueExtractor,
				Function<V, R> resultExtractor) {
			return extract(valueExtractor.andThen(resultExtractor));
		}

		private <R> @Nullable R extract(Function<X509Certificate, R> extractor) {
			return (this.certificate != null) ? extractor.apply(this.certificate) : null;
		}

	}

	/**
	 * Certificate validity info.
	 */
	public static class CertificateValidityInfo {

		static final CertificateValidityInfo VALID = new CertificateValidityInfo(Status.VALID, null);

		private final Status status;

		private final @Nullable String message;

		CertificateValidityInfo(Status status, @Nullable String message, Object... messageArgs) {
			this.status = status;
			this.message = (message != null) ? message.formatted(messageArgs) : null;
		}

		public Status getStatus() {
			return this.status;
		}

		public @Nullable String getMessage() {
			return this.message;
		}

		/**
		 * Validity Status.
		 */
		public enum Status {

			/**
			 * The certificate is valid.
			 */
			VALID(true),

			/**
			 * The certificate's validity date range is in the future.
			 */
			NOT_YET_VALID(false),

			/**
			 * The certificate's validity date range is in the past.
			 */
			EXPIRED(false);

			private final boolean valid;

			Status(boolean valid) {
				this.valid = valid;
			}

			public boolean isValid() {
				return this.valid;
			}

		}

	}

}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free