diff --git a/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml b/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml
index aec266cb68b6d..b05c56ddaaf3c 100755
--- a/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml
+++ b/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml
@@ -141,6 +141,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JreCertificates.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JreCertificates.java
new file mode 100644
index 0000000000000..711000f049aaa
--- /dev/null
+++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JreCertificates.java
@@ -0,0 +1,168 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.security.keyvault.jca;
+
+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.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;
+
+/**
+ * This class provides the certificates from jre key store. It only provides certificates. It does not provide key entries from jre key store.
+ */
+public final class JreCertificates implements AzureCertificates {
+ /**
+ * Stores the logger.
+ */
+ private static final Logger LOGGER = Logger.getLogger(JreCertificates.class.getName());
+
+ /**
+ * Stores the jre key store aliases.
+ */
+ private final List aliases;
+
+ /**
+ * Stores the jre key store certificates.
+ */
+ private final Map certs;
+
+ /**
+ * Stores the jre key store keys
+ */
+ private final Map keys;
+
+ /**
+ * Stores the singleton
+ */
+ private static final JreCertificates INSTANCE = new JreCertificates();
+
+ /**
+ * Private constructor
+ */
+ private JreCertificates() {
+ KeyStore jreKeyStore = JREKeyStore.getDefault();
+ aliases = Optional.ofNullable(jreKeyStore)
+ .map(a -> {
+ try {
+ return Collections.unmodifiableList(Collections.list(a.aliases()));
+ } catch (KeyStoreException e) {
+ LOGGER.log(WARNING, "Unable to load the jre key store aliases.", e);
+ }
+ return null;
+ })
+ .orElseGet(Collections::emptyList);
+ certs = aliases.stream()
+ .collect(
+ HashMap::new,
+ (m, v) -> {
+ try {
+ m.put(v, jreKeyStore.getCertificate(v));
+ } catch (KeyStoreException e) {
+ LOGGER.log(WARNING, "Unable to get the jre key store certificate.", e);
+ }
+ },
+ HashMap::putAll);
+ keys = Collections.emptyMap();
+ }
+
+ /**
+ *
+ * @return the singleton.
+ */
+ public static JreCertificates getInstance() {
+ return INSTANCE;
+ }
+
+
+ @Override
+ public List getAliases() {
+ return aliases;
+ }
+
+ @Override
+ public Map getCertificates() {
+ return certs;
+ }
+
+ @Override
+ public Map getCertificateKeys() {
+ return keys;
+ }
+
+ @Override
+ 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 value = System.getProperty(theProp, "");
+ return (value.isEmpty()) ? defaultVal : value;
+ });
+ }
+ }
+}
diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java
index a84597b0a3b42..3704aaaae1c97 100644
--- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java
+++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java
@@ -28,9 +28,9 @@
import java.util.List;
import java.util.Collection;
import java.util.Optional;
+import java.util.Arrays;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@@ -65,6 +65,16 @@ public final class KeyVaultKeyStore extends KeyStoreSpi {
*/
private final KeyVaultCertificates keyVaultCertificates;
+ /**
+ * Stores the Jre key store certificates.
+ */
+ private final JreCertificates jreCertificates;
+
+ /**
+ * Stores all the certificates.
+ */
+ private final List allCertificates;
+
/**
* Stores the creation date.
*/
@@ -111,14 +121,17 @@ public KeyVaultKeyStore() {
.orElse(false);
keyVaultCertificates = new KeyVaultCertificates(refreshInterval, keyVaultClient);
classpathCertificates = new ClasspathCertificates();
+ jreCertificates = JreCertificates.getInstance();
+ allCertificates = Arrays.asList(keyVaultCertificates, classpathCertificates, jreCertificates);
}
@Override
public Enumeration engineAliases() {
- List aliasList = Stream.of(keyVaultCertificates, classpathCertificates)
+ List aliasList = allCertificates.stream()
.map(AzureCertificates::getAliases)
.flatMap(Collection::stream)
- .distinct().collect(Collectors.toList());
+ .distinct()
+ .collect(Collectors.toList());
return Collections.enumeration(aliasList);
}
@@ -141,7 +154,7 @@ public boolean engineEntryInstanceOf(String alias, Class extends KeyStore.Entr
@Override
public Certificate engineGetCertificate(String alias) {
- Certificate certificate = Stream.of(keyVaultCertificates, classpathCertificates)
+ Certificate certificate = allCertificates.stream()
.map(AzureCertificates::getCertificates)
.filter(a -> a.containsKey(alias))
.findFirst()
@@ -159,7 +172,7 @@ public Certificate engineGetCertificate(String alias) {
public String engineGetCertificateAlias(Certificate cert) {
String alias = null;
if (cert != null) {
- List aliasList = Stream.of(keyVaultCertificates, classpathCertificates)
+ List aliasList = allCertificates.stream()
.map(AzureCertificates::getAliases)
.flatMap(Collection::stream)
.distinct()
@@ -202,7 +215,7 @@ public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter
@Override
public Key engineGetKey(String alias, char[] password) {
- return Stream.of(keyVaultCertificates, classpathCertificates)
+ return allCertificates.stream()
.map(AzureCertificates::getCertificateKeys)
.filter(a -> a.containsKey(alias))
.findFirst()
@@ -212,7 +225,7 @@ public Key engineGetKey(String alias, char[] password) {
@Override
public boolean engineIsCertificateEntry(String alias) {
- return Stream.of(keyVaultCertificates, classpathCertificates)
+ return allCertificates.stream()
.map(AzureCertificates::getAliases)
.flatMap(Collection::stream)
.distinct()
@@ -284,7 +297,7 @@ public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) {
@Override
public int engineSize() {
- return Stream.of(keyVaultCertificates, classpathCertificates)
+ return allCertificates.stream()
.map(AzureCertificates::getAliases)
.flatMap(Collection::stream)
.distinct()
diff --git a/sdk/keyvault/azure-security-test-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JreKeyStoreTest.java b/sdk/keyvault/azure-security-test-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JreKeyStoreTest.java
new file mode 100644
index 0000000000000..65dd4ae18fc55
--- /dev/null
+++ b/sdk/keyvault/azure-security-test-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JreKeyStoreTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.security.keyvault.jca;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+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.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@EnabledIfEnvironmentVariable(named = "AZURE_KEYVAULT_CERTIFICATE_NAME", matches = "myalias")
+public class JreKeyStoreTest {
+ @BeforeAll
+ public static void init() {
+ /*
+ * Add JCA provider.
+ */
+ KeyVaultJcaProvider provider = new KeyVaultJcaProvider();
+ Security.addProvider(provider);
+ /*
+ * Set system properties.
+ */
+
+ PropertyConvertorUtils.putEnvironmentPropertyToSystemProperty(
+ Arrays.asList("AZURE_KEYVAULT_URI",
+ "AZURE_KEYVAULT_TENANT_ID",
+ "AZURE_KEYVAULT_CLIENT_ID",
+ "AZURE_KEYVAULT_CLIENT_SECRET")
+ );
+ }
+
+ @Test
+ public void testJreKsEntries() {
+ JreCertificates jreCertificates = JreCertificates.getInstance();
+ assertNotNull(jreCertificates);
+ assertNotNull(jreCertificates.getAliases());
+ Map certs = jreCertificates.getCertificates();
+ assertTrue(certs.containsKey("globalsignr2ca [jdk]"));
+ assertNotNull(certs.get("globalsignr2ca [jdk]"));
+ assertNotNull(jreCertificates.getCertificateKeys());
+ }
+
+ @Test
+ public void testJreKsTrustPeer() throws Exception {
+
+ KeyStore ks = KeyStore.getInstance("AzureKeyVault");
+ ks.load(null);
+ /*
+ * Setup client side
+ *
+ * - Create an SSL context.
+ * - Create SSL connection factory.
+ * - Set hostname verifier to trust any hostname.
+ */
+
+ SSLContext sslContext = SSLContexts
+ .custom()
+ .loadTrustMaterial(ks, null)
+ .build();
+
+ SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
+ sslContext, (hostname, session) -> true);
+
+ PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
+ RegistryBuilder.create()
+ .register("https", sslConnectionSocketFactory)
+ .build());
+
+ /*
+ * And now execute the test.
+ */
+ String result = null;
+
+ try (CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).build()) {
+ HttpGet httpGet = new HttpGet("https://google.com:443");
+ ResponseHandler responseHandler = (HttpResponse response) -> {
+ int status = response.getStatusLine().getStatusCode();
+ String result1 = null;
+ if (status == 200) {
+ result1 = "Success";
+ }
+ return result1;
+ };
+ result = client.execute(httpGet, responseHandler);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+
+ /*
+ * And verify all went well.
+ */
+ assertEquals("Success", result);
+ }
+}