Skip to content

Commit 236eb8a

Browse files
committed
CWMSVUE-743 filter out expired certificates and use keymanager for alias list
The KeyStore vs KeyManager alias list is undeterministic. The KeyStore lists aliases with a suffix like "(1)" for conflicting aliases. The list of certificates from the KeyManager then don't always match, causing a certificate miss.....sometimes...
1 parent 95cee47 commit 236eb8a

File tree

3 files changed

+54
-45
lines changed

3 files changed

+54
-45
lines changed

cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManager.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* MIT License
33
*
4-
* Copyright (c) 2023 Hydrologic Engineering Center
4+
* Copyright (c) 2026 Hydrologic Engineering Center
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -24,27 +24,21 @@
2424

2525
package mil.army.usace.hec.cwms.http.client.auth;
2626

27-
import javax.net.ssl.X509KeyManager;
2827
import java.net.Socket;
29-
import java.security.KeyStore;
30-
import java.security.KeyStoreException;
3128
import java.security.Principal;
3229
import java.security.PrivateKey;
33-
import java.security.cert.Certificate;
3430
import java.security.cert.X509Certificate;
3531
import java.util.logging.Level;
3632
import java.util.logging.Logger;
33+
import javax.net.ssl.X509KeyManager;
3734

3835
final class CacKeyManager implements X509KeyManager {
3936
private static final Logger LOGGER = Logger.getLogger(CacKeyManager.class.getName());
4037
private final X509KeyManager delegate;
41-
private final KeyStore keystore;
42-
private final String certificateAlias;
38+
private String certificateAlias;
4339

44-
CacKeyManager(X509KeyManager delegate, KeyStore keystore, String certificateAlias) {
40+
CacKeyManager(X509KeyManager delegate) {
4541
this.delegate = delegate;
46-
this.keystore = keystore;
47-
this.certificateAlias = certificateAlias;
4842
}
4943

5044
@Override
@@ -97,15 +91,27 @@ private String getPivCertificate(String[] aliases) {
9791
String retVal = aliases[0];
9892
for (String alias : aliases) {
9993
try {
100-
Certificate cr = keystore.getCertificate(alias);
101-
if (cr instanceof X509Certificate && CacKeyManagerUtil.isPivCertificate((X509Certificate) cr)) {
102-
retVal = alias;
103-
break;
94+
var cr = delegate.getCertificateChain(alias);
95+
for(var cert : cr) {
96+
if (CacKeyManagerUtil.isPivCertificate(cert)) {
97+
retVal = alias;
98+
break;
99+
}
104100
}
105-
} catch (CacCertificateException | KeyStoreException e) {
101+
} catch (CacCertificateException e) {
106102
LOGGER.log(Level.FINE, "Unable to authorize certificate for CWBI Authentication", e);
107103
}
108104
}
109105
return retVal;
110106
}
107+
108+
void setCertificateAlias(String certificateAlias) {
109+
if(certificateAlias != null) {
110+
this.certificateAlias = certificateAlias;
111+
}
112+
}
113+
114+
String[] aliases() {
115+
return delegate.getClientAliases("RSA", null);
116+
}
111117
}

cwms-http-client/src/main/java/mil/army/usace/hec/cwms/http/client/auth/CacKeyManagerUtil.java

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* MIT License
33
*
4-
* Copyright (c) 2024 Hydrologic Engineering Center
4+
* Copyright (c) 2026 Hydrologic Engineering Center
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,6 @@
2424

2525
package mil.army.usace.hec.cwms.http.client.auth;
2626

27-
import javax.net.ssl.KeyManager;
28-
import javax.net.ssl.KeyManagerFactory;
29-
import javax.net.ssl.X509KeyManager;
3027
import java.io.IOException;
3128
import java.security.KeyStore;
3229
import java.security.KeyStoreException;
@@ -38,50 +35,54 @@
3835
import java.security.cert.X509Certificate;
3936
import java.util.ArrayList;
4037
import java.util.Collection;
41-
import java.util.Enumeration;
38+
import java.util.Date;
4239
import java.util.List;
4340
import java.util.Set;
4441
import java.util.TreeSet;
4542
import java.util.logging.Level;
4643
import java.util.logging.Logger;
4744
import java.util.regex.Pattern;
45+
import javax.net.ssl.KeyManager;
46+
import javax.net.ssl.KeyManagerFactory;
47+
import javax.net.ssl.X509KeyManager;
4848

4949

5050
public final class CacKeyManagerUtil {
5151
static final Pattern EDIPI_PATTERN = Pattern.compile("\\d{10}@", Pattern.CASE_INSENSITIVE);
5252
private static final Logger LOGGER = Logger.getLogger(CacKeyManagerUtil.class.getName());
53-
private static KeyStore WINDOWS_KEY_STORE;
53+
private static CacKeyManager WINDOWS_KEY_STORE;
5454

5555
private CacKeyManagerUtil() {
5656
throw new AssertionError("Utility class");
5757
}
5858

5959
public static KeyManager createKeyManager() throws CacCertificateException {
60-
return getKeyManagerFromWindowsKeyStore(null);
60+
return loadWindowsKeyStore(null);
6161
}
6262

6363
public static KeyManager createKeyManager(String certificateAlias) throws CacCertificateException {
64-
return getKeyManagerFromWindowsKeyStore(certificateAlias);
64+
return loadWindowsKeyStore(certificateAlias);
6565
}
6666

67-
private static synchronized KeyStore loadWindowsKeyStore() throws CertificateException, IOException, NoSuchAlgorithmException, KeyStoreException {
67+
private static synchronized CacKeyManager loadWindowsKeyStore(String certificateAlias)
68+
throws CacCertificateException {
6869
if (WINDOWS_KEY_STORE == null) {
69-
KeyStore keystore = KeyStore.getInstance("WINDOWS-MY");
70-
keystore.load(null, null);
71-
WINDOWS_KEY_STORE = keystore;
70+
WINDOWS_KEY_STORE = getKeyManagerFromWindowsKeyStore();
7271
}
72+
WINDOWS_KEY_STORE.setCertificateAlias(certificateAlias);
7373
return WINDOWS_KEY_STORE;
7474
}
7575

76-
private static CacKeyManager getKeyManagerFromWindowsKeyStore(String certificateAlias) throws CacCertificateException {
76+
private static CacKeyManager getKeyManagerFromWindowsKeyStore() throws CacCertificateException {
7777
try {
78-
KeyStore keystore = loadWindowsKeyStore();
78+
KeyStore keystore = KeyStore.getInstance("WINDOWS-MY");
79+
keystore.load(null, null);
7980
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
8081
kmf.init(keystore, null);
8182
KeyManager[] kms = kmf.getKeyManagers();
8283
for (KeyManager km : kms) {
8384
if (km instanceof X509KeyManager) {
84-
return new CacKeyManager((X509KeyManager) km, keystore, certificateAlias);
85+
return new CacKeyManager((X509KeyManager) km);
8586
}
8687
}
8788
throw new CacCertificateException("Failed to get X509KeyManager from Windows OS");
@@ -95,26 +96,28 @@ private static CacKeyManager getKeyManagerFromWindowsKeyStore(String certificate
9596
public static List<String> getCertificateAliases() {
9697
Set<String> aliases = new TreeSet<>();
9798
try {
98-
KeyStore keystore = loadWindowsKeyStore();
99-
Enumeration<String> keystoreAliases = keystore.aliases();
100-
while (keystoreAliases.hasMoreElements()) {
101-
String alias = keystoreAliases.nextElement();
99+
var keystore = loadWindowsKeyStore(null);
100+
var keystoreAliases = keystore.aliases();
101+
for (String alias : keystoreAliases) {
102102
Certificate[] certificateChain = keystore.getCertificateChain(alias);
103-
if (certificateChain != null && certificateChain.length > 1 && certificateChain[0] instanceof X509Certificate) {
104-
if (isPivCertificate((X509Certificate) certificateChain[0])) {
105-
aliases.add(alias);
106-
}
103+
if (certificateChain != null
104+
&& certificateChain.length > 1
105+
&& certificateChain[0] instanceof X509Certificate
106+
&& isPivCertificate((X509Certificate) certificateChain[0])) {
107+
aliases.add(alias);
107108
}
108109
}
109-
} catch (IOException | NoSuchAlgorithmException | CertificateException | CacCertificateException |
110-
KeyStoreException e) {
110+
} catch (CacCertificateException e) {
111111
LOGGER.log(Level.WARNING, "Error reading certificates from WINDOWS-MY keystore", e);
112112
}
113113
return new ArrayList<>(aliases);
114114
}
115115

116116
public static boolean isPivCertificate(X509Certificate cr) throws CacCertificateException {
117117
try {
118+
if(new Date().after(cr.getNotAfter())) {
119+
return false;
120+
}
118121
Collection<List<?>> subjectAlternativeNames = cr.getSubjectAlternativeNames();
119122
if (subjectAlternativeNames == null) {
120123
return false;

cwms-http-client/src/testFixtures/java/mil/army/usace/hec/cwms/http/client/auth/KeyManagerTestUtil.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* MIT License
33
*
4-
* Copyright (c) 2023 Hydrologic Engineering Center
4+
* Copyright (c) 2026 Hydrologic Engineering Center
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -24,15 +24,15 @@
2424

2525
package mil.army.usace.hec.cwms.http.client.auth;
2626

27-
import javax.net.ssl.KeyManager;
28-
import javax.net.ssl.KeyManagerFactory;
29-
import javax.net.ssl.X509KeyManager;
3027
import java.io.IOException;
3128
import java.security.KeyStore;
3229
import java.security.KeyStoreException;
3330
import java.security.NoSuchAlgorithmException;
3431
import java.security.UnrecoverableKeyException;
3532
import java.security.cert.CertificateException;
33+
import javax.net.ssl.KeyManager;
34+
import javax.net.ssl.KeyManagerFactory;
35+
import javax.net.ssl.X509KeyManager;
3636

3737
public final class KeyManagerTestUtil {
3838

@@ -46,7 +46,7 @@ public static CacKeyManager getKeyManagerFromJreKeyStore() throws CacCertificate
4646
KeyManager[] kms = kmf.getKeyManagers();
4747
for (KeyManager km : kms) {
4848
if (km instanceof X509KeyManager) {
49-
return new CacKeyManager((X509KeyManager) km, keystore, null);
49+
return new CacKeyManager((X509KeyManager) km);
5050
}
5151
}
5252
throw new CacCertificateException("Failed to get X509KeyManager from type: " + defaultType);

0 commit comments

Comments
 (0)