Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<String> aliases() {
Set<String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -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");
Expand All @@ -95,26 +96,28 @@ private static CacKeyManager getKeyManagerFromWindowsKeyStore(String certificate
public static List<String> getCertificateAliases() {
Set<String> aliases = new TreeSet<>();
try {
KeyStore keystore = loadWindowsKeyStore();
Enumeration<String> 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);
}

public static boolean isPivCertificate(X509Certificate cr) throws CacCertificateException {
try {
if(new Date().after(cr.getNotAfter())) {
return false;
}
Collection<List<?>> subjectAlternativeNames = cr.getSubjectAlternativeNames();
if (subjectAlternativeNames == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {

Expand All @@ -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);
Expand Down
Loading