diff --git a/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManager.java b/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManager.java index 8c73ed32..fbd8312b 100644 --- a/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManager.java +++ b/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManager.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2023 Hydrologic Engineering Center + * Copyright (c) 2026 Hydrologic Engineering Center * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,27 +24,24 @@ package mil.army.usace.hec.cwms.http.client.auth; -import javax.net.ssl.X509KeyManager; import java.net.Socket; -import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.Principal; import java.security.PrivateKey; -import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; +import javax.net.ssl.X509KeyManager; final class CacKeyManager implements X509KeyManager { private static final Logger LOGGER = Logger.getLogger(CacKeyManager.class.getName()); private final X509KeyManager delegate; - private final KeyStore keystore; - private final String certificateAlias; + private String certificateAlias; - CacKeyManager(X509KeyManager delegate, KeyStore keystore, String certificateAlias) { + CacKeyManager(X509KeyManager delegate) { this.delegate = delegate; - this.keystore = keystore; - this.certificateAlias = certificateAlias; } @Override @@ -97,15 +94,34 @@ private String getPivCertificate(String[] aliases) { String retVal = aliases[0]; for (String alias : aliases) { try { - Certificate cr = keystore.getCertificate(alias); - if (cr instanceof X509Certificate && CacKeyManagerUtil.isPivCertificate((X509Certificate) cr)) { - retVal = alias; - break; + var cr = delegate.getCertificateChain(alias); + for(var cert : cr) { + if (CacKeyManagerUtil.isPivCertificate(cert)) { + retVal = alias; + break; + } } - } catch (CacCertificateException | KeyStoreException e) { + } catch (CacCertificateException e) { LOGGER.log(Level.FINE, "Unable to authorize certificate for CWBI Authentication", e); } } return retVal; } + + void setCertificateAlias(String certificateAlias) { + if(certificateAlias != null) { + this.certificateAlias = certificateAlias; + } + } + + Set aliases() { + Set retval = new TreeSet<>(); + for(var keyType : new String[]{"RSA", "EC", "DSA"}) { + String[] clientAliases = delegate.getClientAliases(keyType, null); + if(clientAliases != null) { + Collections.addAll(retval, clientAliases); + } + } + return retval; + } } diff --git a/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManagerUtil.java b/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManagerUtil.java index c5ad2d93..8f1926bf 100644 --- a/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManagerUtil.java +++ b/cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManagerUtil.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2024 Hydrologic Engineering Center + * Copyright (c) 2026 Hydrologic Engineering Center * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +24,6 @@ package mil.army.usace.hec.cwms.http.client.auth; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.X509KeyManager; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -38,50 +35,54 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; -import java.util.Enumeration; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; public final class CacKeyManagerUtil { static final Pattern EDIPI_PATTERN = Pattern.compile("\\d{10}@", Pattern.CASE_INSENSITIVE); private static final Logger LOGGER = Logger.getLogger(CacKeyManagerUtil.class.getName()); - private static KeyStore WINDOWS_KEY_STORE; + private static CacKeyManager WINDOWS_KEY_STORE; private CacKeyManagerUtil() { throw new AssertionError("Utility class"); } public static KeyManager createKeyManager() throws CacCertificateException { - return getKeyManagerFromWindowsKeyStore(null); + return loadWindowsKeyStore(null); } public static KeyManager createKeyManager(String certificateAlias) throws CacCertificateException { - return getKeyManagerFromWindowsKeyStore(certificateAlias); + return loadWindowsKeyStore(certificateAlias); } - private static synchronized KeyStore loadWindowsKeyStore() throws CertificateException, IOException, NoSuchAlgorithmException, KeyStoreException { + private static synchronized CacKeyManager loadWindowsKeyStore(String certificateAlias) + throws CacCertificateException { if (WINDOWS_KEY_STORE == null) { - KeyStore keystore = KeyStore.getInstance("WINDOWS-MY"); - keystore.load(null, null); - WINDOWS_KEY_STORE = keystore; + WINDOWS_KEY_STORE = getKeyManagerFromWindowsKeyStore(); } + WINDOWS_KEY_STORE.setCertificateAlias(certificateAlias); return WINDOWS_KEY_STORE; } - private static CacKeyManager getKeyManagerFromWindowsKeyStore(String certificateAlias) throws CacCertificateException { + private static CacKeyManager getKeyManagerFromWindowsKeyStore() throws CacCertificateException { try { - KeyStore keystore = loadWindowsKeyStore(); + KeyStore keystore = KeyStore.getInstance("WINDOWS-MY"); + keystore.load(null, null); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keystore, null); KeyManager[] kms = kmf.getKeyManagers(); for (KeyManager km : kms) { if (km instanceof X509KeyManager) { - return new CacKeyManager((X509KeyManager) km, keystore, certificateAlias); + return new CacKeyManager((X509KeyManager) km); } } throw new CacCertificateException("Failed to get X509KeyManager from Windows OS"); @@ -95,19 +96,18 @@ private static CacKeyManager getKeyManagerFromWindowsKeyStore(String certificate public static List getCertificateAliases() { Set aliases = new TreeSet<>(); try { - KeyStore keystore = loadWindowsKeyStore(); - Enumeration keystoreAliases = keystore.aliases(); - while (keystoreAliases.hasMoreElements()) { - String alias = keystoreAliases.nextElement(); + var keystore = loadWindowsKeyStore(null); + var keystoreAliases = keystore.aliases(); + for (String alias : keystoreAliases) { Certificate[] certificateChain = keystore.getCertificateChain(alias); - if (certificateChain != null && certificateChain.length > 1 && certificateChain[0] instanceof X509Certificate) { - if (isPivCertificate((X509Certificate) certificateChain[0])) { - aliases.add(alias); - } + if (certificateChain != null + && certificateChain.length > 1 + && certificateChain[0] instanceof X509Certificate + && isPivCertificate((X509Certificate) certificateChain[0])) { + aliases.add(alias); } } - } catch (IOException | NoSuchAlgorithmException | CertificateException | CacCertificateException | - KeyStoreException e) { + } catch (CacCertificateException e) { LOGGER.log(Level.WARNING, "Error reading certificates from WINDOWS-MY keystore", e); } return new ArrayList<>(aliases); @@ -115,6 +115,9 @@ public static List getCertificateAliases() { public static boolean isPivCertificate(X509Certificate cr) throws CacCertificateException { try { + if(new Date().after(cr.getNotAfter())) { + return false; + } Collection> subjectAlternativeNames = cr.getSubjectAlternativeNames(); if (subjectAlternativeNames == null) { return false; diff --git a/cwms-http-client/src/testFixtures/java/mil/army/usace/hec/cwms/http/client/auth/KeyManagerTestUtil.java b/cwms-http-client/src/testFixtures/java/mil/army/usace/hec/cwms/http/client/auth/KeyManagerTestUtil.java index fa84b43d..38f61de2 100644 --- a/cwms-http-client/src/testFixtures/java/mil/army/usace/hec/cwms/http/client/auth/KeyManagerTestUtil.java +++ b/cwms-http-client/src/testFixtures/java/mil/army/usace/hec/cwms/http/client/auth/KeyManagerTestUtil.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2023 Hydrologic Engineering Center + * Copyright (c) 2026 Hydrologic Engineering Center * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,15 +24,15 @@ package mil.army.usace.hec.cwms.http.client.auth; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.X509KeyManager; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; public final class KeyManagerTestUtil { @@ -46,7 +46,7 @@ public static CacKeyManager getKeyManagerFromJreKeyStore() throws CacCertificate KeyManager[] kms = kmf.getKeyManagers(); for (KeyManager km : kms) { if (km instanceof X509KeyManager) { - return new CacKeyManager((X509KeyManager) km, keystore, null); + return new CacKeyManager((X509KeyManager) km); } } throw new CacCertificateException("Failed to get X509KeyManager from type: " + defaultType);