Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build the SSL context with jre key store to back up the KeyVaultKeyStore's own functions. #23923

Merged
Expand Up @@ -167,7 +167,7 @@
<!-- Suppress Redundant nullcheck error for JreCertificates$JREKeyStore.loadKeyStore(KeyStore). -->
<Match>
<Or>
<Class name="com.azure.security.keyvault.jca.implementation.certificates.JreCertificates$JREKeyStore"/> <!-- false positive -->
<Class name="com.azure.security.keyvault.jca.implementation.JREKeyStore"/> <!-- false positive -->
</Or>
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
</Match>
Expand Down
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.security.keyvault.jca.implementation;


import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.cert.CertificateException;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Stream;

import static java.util.logging.Level.WARNING;

/**
* This class provides a JRE key store.
*/
public final class JREKeyStore {
private static final String JAVA_HOME = privilegedGetProperty("java.home", "");
private static final Path STORE_PATH = Paths.get(JAVA_HOME).resolve("lib").resolve("security");
private static final Path DEFAULT_STORE = STORE_PATH.resolve("cacerts");
private static final Path JSSE_DEFAULT_STORE = STORE_PATH.resolve("jssecacerts");
private static final String KEY_STORE_PASSWORD = privilegedGetProperty("javax.net.ssl.keyStorePassword", "changeit");
private static final Logger LOGGER = Logger.getLogger(JREKeyStore.class.getName());
private static final KeyStore JRE_KEY_STORE = getJreKeyStore();


private JREKeyStore() {

}

/**
* This method returns the instance of JRE key store
* @return the JRE key store.
*/
public static KeyStore getDefault() {
return JRE_KEY_STORE;
}


private static KeyStore getJreKeyStore() {
KeyStore defaultKeyStore = null;
try {
defaultKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
loadKeyStore(defaultKeyStore);
} catch (KeyStoreException e) {
LOGGER.log(WARNING, "Unable to get the jre key store.", e);
}
return defaultKeyStore;
}

private static void loadKeyStore(KeyStore ks) {
try (InputStream inputStream = Files.newInputStream(getKeyStoreFile())) {
ks.load(inputStream, KEY_STORE_PASSWORD.toCharArray());
} catch (IOException | NoSuchAlgorithmException | CertificateException e) {
LOGGER.log(WARNING, "unable to load the jre key store", e);
}
}

private static Path getKeyStoreFile() {
return Stream.of(getConfiguredKeyStorePath(), JSSE_DEFAULT_STORE, DEFAULT_STORE)
.filter(Objects::nonNull)
.filter(Files::exists)
.filter(Files::isReadable)
.findFirst()
.orElse(null);
}

private static Path getConfiguredKeyStorePath() {
String configuredKeyStorePath = privilegedGetProperty("javax.net.ssl.keyStore", "");
return Optional.of(configuredKeyStorePath)
.filter(path -> !path.isEmpty())
.map(Paths::get)
.orElse(null);
}

private static String privilegedGetProperty(String theProp, String defaultVal) {
return AccessController.doPrivileged(
(PrivilegedAction<String>) () -> {
String value = System.getProperty(theProp, "");
return (value.isEmpty()) ? defaultVal : value;
});
}
}
Expand Up @@ -3,28 +3,17 @@

package com.azure.security.keyvault.jca.implementation.certificates;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import com.azure.security.keyvault.jca.implementation.JREKeyStore;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.stream.Stream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.Objects;

import static java.util.logging.Level.WARNING;

/**
Expand Down Expand Up @@ -52,7 +41,7 @@ public final class JreCertificates implements AzureCertificates {
private final Map<String, Key> keys;

/**
* Stores the singleton
* Stores the instance of JreCertificates.
*/
private static final JreCertificates INSTANCE = new JreCertificates();

Expand Down Expand Up @@ -87,7 +76,7 @@ private JreCertificates() {

/**
*
* @return the singleton.
* @return the instance of JreCertificates.
*/
public static JreCertificates getInstance() {
return INSTANCE;
Expand All @@ -113,56 +102,4 @@ public Map<String, Key> getCertificateKeys() {
public void deleteEntry(String alias) {

}

private static class JREKeyStore {
private static final String JAVA_HOME = privilegedGetProperty("java.home", "");
private static final Path STORE_PATH = Paths.get(JAVA_HOME).resolve("lib").resolve("security");
private static final Path DEFAULT_STORE = STORE_PATH.resolve("cacerts");
private static final Path JSSE_DEFAULT_STORE = STORE_PATH.resolve("jssecacerts");
private static final String KEY_STORE_PASSWORD = privilegedGetProperty("javax.net.ssl.keyStorePassword", "changeit");

private static KeyStore getDefault() {
KeyStore defaultKeyStore = null;
try {
defaultKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
loadKeyStore(defaultKeyStore);
} catch (KeyStoreException e) {
LOGGER.log(WARNING, "Unable to get the jre key store.", e);
}
return defaultKeyStore;
}

private static void loadKeyStore(KeyStore ks) {
try (InputStream inputStream = Files.newInputStream(getKeyStoreFile())) {
ks.load(inputStream, KEY_STORE_PASSWORD.toCharArray());
} catch (IOException | NoSuchAlgorithmException | CertificateException e) {
LOGGER.log(WARNING, "unable to load the jre key store", e);
}
}

private static Path getKeyStoreFile() {
return Stream.of(getConfiguredKeyStorePath(), JSSE_DEFAULT_STORE, DEFAULT_STORE)
.filter(Objects::nonNull)
.filter(Files::exists)
.filter(Files::isReadable)
.findFirst()
.orElse(null);
}

private static Path getConfiguredKeyStorePath() {
String configuredKeyStorePath = privilegedGetProperty("javax.net.ssl.keyStore", "");
return Optional.of(configuredKeyStorePath)
.filter(path -> !path.isEmpty())
.map(Paths::get)
.orElse(null);
}

private static String privilegedGetProperty(String theProp, String defaultVal) {
return AccessController.doPrivileged(
(PrivilegedAction<String>) () -> {
String value = System.getProperty(theProp, "");
return (value.isEmpty()) ? defaultVal : value;
});
}
}
}
Expand Up @@ -2,24 +2,39 @@
// Licensed under the MIT License.
package com.azure.security.keyvault.jca.implementation.utils;

import com.azure.security.keyvault.jca.implementation.JREKeyStore;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Stream;

import static java.util.logging.Level.WARNING;

/**
* The RestClient that uses the Apache HttpClient class.
*/
Expand All @@ -34,17 +49,22 @@ public final class HttpUtil {
.orElse(DEFAULT_VERSION);
public static final String USER_AGENT_VALUE = getUserAgentPrefix() + VERSION;

/**
* Stores the logger.
*/
michaelqi793 marked this conversation as resolved.
Show resolved Hide resolved
private static final Logger LOGGER = Logger.getLogger(HttpUtil.class.getName());

public static String get(String url, Map<String, String> headers) {
String result = null;
try (CloseableHttpClient client = HttpClients.createDefault()) {
try (CloseableHttpClient client = buildClient()) {
HttpGet httpGet = new HttpGet(url);
if (headers != null) {
headers.forEach(httpGet::addHeader);
}
httpGet.addHeader(USER_AGENT_KEY, USER_AGENT_VALUE);
result = client.execute(httpGet, createResponseHandler());
} catch (IOException ioe) {
ioe.printStackTrace();
LOGGER.log(WARNING, "Unable to finish the http get request.", ioe);
}
return result;
}
Expand All @@ -67,7 +87,7 @@ public static String getUserAgentPrefix() {

public static String post(String url, Map<String, String> headers, String body, String contentType) {
String result = null;
try (CloseableHttpClient client = HttpClients.createDefault()) {
try (CloseableHttpClient client = buildClient()) {
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader(USER_AGENT_KEY, USER_AGENT_VALUE);
if (headers != null) {
Expand All @@ -77,7 +97,7 @@ public static String post(String url, Map<String, String> headers, String body,
httpPost.setEntity(new StringEntity(body, ContentType.create(contentType)));
result = client.execute(httpPost, createResponseHandler());
} catch (IOException ioe) {
ioe.printStackTrace();
LOGGER.log(WARNING, "Unable to finish the http post request.", ioe);
}
return result;
}
Expand All @@ -94,4 +114,27 @@ private static ResponseHandler<String> createResponseHandler() {
return result;
};
}

private static CloseableHttpClient buildClient() {
KeyStore keyStore = JREKeyStore.getDefault();

SSLContext sslContext = null;
try {
sslContext = SSLContexts
.custom()
.loadTrustMaterial(keyStore, null)
.build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
LOGGER.log(WARNING, "Unable to build the ssl context.", e);
}

SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext, (HostnameVerifier) null);

PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionSocketFactory)
.build());
return HttpClients.custom().setConnectionManager(manager).build();
}
}