diff --git a/.gitignore b/.gitignore index 2599ede3cd8f..8c1e10d10e79 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,7 @@ ClientAggregatePom.xml # Anaconda virtual env venv + +# NetBeans +nbproject +nb-configuration.xml \ No newline at end of file diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 9fada7984de4..2f6ab7cc1ebf 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -63,6 +63,7 @@ org.apache.avro:avro-maven-plugin;1.9.2 org.apache.commons:commons-compress;1.20 org.apache.commons:commons-lang3;3.10 org.apache.httpcomponents:httpclient;4.5.12 +org.apache.httpcomponents.client5:httpclient5;5.0.1 org.apache.logging.log4j:log4j-api;2.13.3 org.apache.logging.log4j:log4j-core;2.13.3 org.apache.logging.log4j:log4j-slf4j-impl;2.13.3 @@ -79,6 +80,7 @@ org.powermock:powermock-api-mockito2;2.0.2 org.powermock:powermock-module-junit4;2.0.2 org.postgresql:postgresql;42.2.14 org.slf4j:slf4j-api;1.7.30 +org.slf4j:slf4j-nop;1.7.30 org.slf4j:slf4j-simple;1.7.30 ## Spring boot dependency versions @@ -232,6 +234,7 @@ org.apache.maven.plugins:maven-jxr-plugin;3.0.0 org.apache.maven.plugins:maven-project-info-reports-plugin;3.0.0 org.apache.maven.plugins:maven-release-plugin;2.5.3 org.apache.maven.plugins:maven-resources-plugin;2.4.3 +org.apache.maven.plugins:maven-shade-plugin;3.2.4 org.apache.maven.plugins:maven-site-plugin;3.7.1 org.apache.maven.plugins:maven-source-plugin;3.0.1 org.apache.maven.plugins:maven-surefire-plugin;3.0.0-M3 diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 3186e69c9c16..302912c0415b 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -45,6 +45,7 @@ com.azure:azure-search-documents;11.1.1;11.2.0-beta.3 com.azure:azure-search-perf;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-security-keyvault-administration;4.0.0-beta.2;4.0.0-beta.3 com.azure:azure-security-keyvault-certificates;4.1.2;4.2.0-beta.3 +com.azure:azure-security-keyvault-jca;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-security-keyvault-keys;4.2.2;4.3.0-beta.3 com.azure:azure-security-keyvault-secrets;4.2.2;4.3.0-beta.3 com.azure:azure-sdk-template;1.2.1-beta.2;1.2.1-beta.16 @@ -66,6 +67,7 @@ com.azure.spring:azure-spring-boot-starter-active-directory-b2c;3.0.0-beta.1;3.0 com.azure.spring:azure-spring-boot-starter-active-directory;3.0.0-beta.1;3.0.0-beta.1 com.azure.spring:azure-spring-boot-starter-cosmos;3.0.0-beta.1;3.0.0-beta.1 com.azure.spring:azure-spring-boot-starter-data-gremlin;3.0.0-beta.1;3.0.0-beta.1 +com.azure.spring:azure-spring-boot-starter-keyvault-certificates;3.0.0-beta.1;3.0.0-beta.1 com.azure.spring:azure-spring-boot-starter-keyvault-secrets;3.0.0-beta.1;3.0.0-beta.1 com.azure.spring:azure-spring-boot-starter-metrics;3.0.0-beta.1;3.0.0-beta.1 com.azure.spring:azure-spring-boot-starter-servicebus-jms;3.0.0-beta.1;3.0.0-beta.1 diff --git a/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md new file mode 100644 index 000000000000..c9a78334999c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md @@ -0,0 +1,4 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) + diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md new file mode 100644 index 000000000000..f6efb38fbe05 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -0,0 +1,127 @@ +# JCA Provider for Azure Key Vault + +The JCA Provider for Azure Key Vault is a JCA provider for certificates in +Azure Key Vault. It is built on four principles: + +1. Must be extremely thin to run within a JVM +1. Must not introduce any library version conflicts with Java app code dependencies +1. Must not introduce any class loader hierarchy conflicts with Java app code dependencies +1. Must be ready for "never trust, always verify and credential-free" Zero Trust environments. + +## Testing the version under development + +If you want to test the current version under development you will have to +build and install it into your local Maven repository. To do so use the +following command line: + +``` + mvn clean install -DskipTests=true +``` + +## Server side SSL + +If you are looking to integrate the JCA provider to create a SSLServerSocket +see the example below. + +```java + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + Security.addProvider(provider); + + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + ks.load(parameter); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, "".toCharArray()); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(kmf.getKeyManagers(), null, null); + + SSLServerSocketFactory factory = (SSLServerSocketFactory) context.getServerSocketFactory(); + SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(8765); +``` + +Note if you want to use Azure managed identity, you should set the value +of `azure.keyvault.uri`, and the rest of the parameters would be `null`. + +## Client side SSL + +If you are looking to integrate the JCA provider for client side socket +connections, see the Apache HTTP client example below. + +```java + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + Security.addProvider(provider); + + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + ks.load(parameter); + + SSLContext sslContext = SSLContexts + .custom() + .loadTrustMaterial(ks, new TrustSelfSignedStrategy()) + .build(); + + SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder + .create() + .setSslContext(sslContext) + .setHostnameVerifier((hostname, session) -> { + return true; + }) + .build(); + + PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder + .create() + .setSSLSocketFactory(sslSocketFactory) + .build(); + + String result = null; + + try ( CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build()) { + HttpGet httpGet = new HttpGet("https://localhost:8766"); + HttpClientResponseHandler responseHandler = (ClassicHttpResponse response) -> { + int status = response.getCode(); + String result1 = "Not success"; + if (status == 204) { + result1 = "Success"; + } + return result1; + }; + result = client.execute(httpGet, responseHandler); + } catch (IOException ioe) { + ioe.printStackTrace(); + } +``` + +Note if you want to use Azure managed identity, you should set the value +of `azure.keyvault.uri`, and the rest of the parameters would be `null`. + +## Spring Boot + +For Spring Boot applications see our [Spring Boot starter]. + +## Reference + +1. [Java Cryptography Architecture (JCA) Reference Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) + +# Azure KeyVault JCA client library for Java + +# Getting started + +# Key concepts + +# Examples + +# Troubleshooting + +# Next steps + +# Contributing diff --git a/sdk/keyvault/azure-security-keyvault-jca/pom.xml b/sdk/keyvault/azure-security-keyvault-jca/pom.xml new file mode 100644 index 000000000000..b6be52e4b8ca --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/pom.xml @@ -0,0 +1,232 @@ + + + + 4.0.0 + + + azure-client-sdk-parent + com.azure + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure + azure-security-keyvault-jca + 1.0.0-beta.1 + JCA Provider for Azure Key Vault + The Java Crypto Architecture (JCA) Provider for Azure KeyVault + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4.3 + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + + shade + + + + + com.fasterxml.jackson + com.azure.keyvault.jca.com.fasterxml.jackson + + + org.apache.commons + com.azure.keyvault.jca.org.apache.commons + + + org.apache.hc + com.azure.keyvault.jca.org.apache.hc + + + mozilla + com.azure.keyvault.jca.mozilla + + + org.slf4j + com.azure.keyvault.jca.org.slf4j + + + + + + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + true + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + default-prepare-agent + + prepare-agent + + + + default-report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + org.apache.httpcomponents.client5:httpclient5:[5.0.1] + com.fasterxml.jackson.core:jackson-databind:[2.11.2] + org.slf4j:slf4j-nop:[1.7.30] + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + true + + + + com.github.spotbugs + spotbugs-maven-plugin + 3.1.12.2 + + true + + + + org.revapi + revapi-maven-plugin + 0.11.2 + + true + + + + + + + + org.apache.httpcomponents.client5 + httpclient5 + 5.0.1 + true + + + + com.fasterxml.jackson.core + jackson-databind + 2.11.2 + true + + + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + + org.slf4j + slf4j-nop + 1.7.30 + + + + + it + + + + azure.keyvault.uri + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + false + + ${azure.keyvault.uri} + ${azure.tenant.id} + ${azure.client.id} + ${azure.client.secret} + + + + + + + + + UTF-8 + + diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/AuthClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/AuthClient.java new file mode 100644 index 000000000000..7fab5629bce9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/AuthClient.java @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import com.azure.security.keyvault.jca.rest.OAuthToken; + +import java.util.HashMap; +import java.util.logging.Logger; + +import static java.util.logging.Level.FINER; + +/** + * The REST client specific to getting an access token for Azure REST APIs. + */ +class AuthClient extends DelegateRestClient { + + /** + * Stores the Client ID fragment. + */ + private static final String CLIENT_ID_FRAGMENT = "&client_id="; + + /** + * Stores the Client Secret fragment. + */ + private static final String CLIENT_SECRET_FRAGMENT = "&client_secret="; + + /** + * Stores the Grant Type fragment. + */ + private static final String GRANT_TYPE_FRAGMENT = "grant_type=client_credentials"; + + /** + * Stores the Resource fragment. + */ + private static final String RESOURCE_FRAGMENT = "&resource="; + + /** + * Stores the OAuth2 token base URL. + */ + private static final String OAUTH2_TOKEN_BASE_URL = "https://login.microsoftonline.com/"; + + /** + * Stores the OAuth2 token postfix. + */ + private static final String OAUTH2_TOKEN_POSTFIX = "/oauth2/token"; + + /** + * Stores the OAuth2 managed identity URL. + */ + private static final String OAUTH2_MANAGED_IDENTITY_TOKEN_URL + = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01"; + + /** + * Stores our logger. + */ + private static final Logger LOGGER = Logger.getLogger(AuthClient.class.getName()); + + /** + * Constructor. + * + *

+ * The constructor creates a default RestClient. + *

+ */ + AuthClient() { + super(RestClientFactory.createClient()); + } + + /** + * Get an access token for a managed identity. + * + * @param resource the resource. + * @return the authorization token. + */ + public String getAccessToken(String resource) { + String result; + + if (System.getenv("WEBSITE_SITE_NAME") != null + && !System.getenv("WEBSITE_SITE_NAME").isEmpty()) { + result = getAccessTokenOnAppService(resource); + } else { + result = getAccessTokenOnOthers(resource); + } + return result; + } + + /** + * Get an access token. + * + * @param resource the resource. + * @param tenantId the tenant ID. + * @param clientId the client ID. + * @param clientSecret the client secret. + * @return the authorization token. + */ + public String getAccessToken(String resource, String tenantId, + String clientId, String clientSecret) { + LOGGER.entering("AuthClient", "getAccessToken", new Object[] { + resource, tenantId, clientId, clientSecret }); + LOGGER.info("Getting access token using client ID / client secret"); + String result = null; + + StringBuilder oauth2Url = new StringBuilder(); + oauth2Url.append(OAUTH2_TOKEN_BASE_URL) + .append(tenantId) + .append(OAUTH2_TOKEN_POSTFIX); + + StringBuilder requestBody = new StringBuilder(); + requestBody.append(GRANT_TYPE_FRAGMENT) + .append(CLIENT_ID_FRAGMENT).append(clientId) + .append(CLIENT_SECRET_FRAGMENT).append(clientSecret) + .append(RESOURCE_FRAGMENT).append(resource); + + String body = post(oauth2Url.toString(), requestBody.toString(), "application/x-www-form-urlencoded"); + if (body != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + OAuthToken token = (OAuthToken) converter.fromJson(body, OAuthToken.class); + result = token.getAccess_token(); + } + LOGGER.log(FINER, "Access token: {0}", result); + return result; + } + + /** + * Get the access token on Azure App Service. + * + * @param resource the resource. + * @return the authorization token. + */ + private String getAccessTokenOnAppService(String resource) { + LOGGER.entering("AuthClient", "getAccessTokenOnAppService", resource); + LOGGER.info("Getting access token using managed identity based on MSI_SECRET"); + String result = null; + + StringBuilder url = new StringBuilder(); + url.append(System.getenv("MSI_ENDPOINT")) + .append("?api-version=2017-09-01") + .append(RESOURCE_FRAGMENT).append(resource); + + HashMap headers = new HashMap<>(); + headers.put("Metadata", "true"); + headers.put("Secret", System.getenv("MSI_SECRET")); + String body = get(url.toString(), headers); + + if (body != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + OAuthToken token = (OAuthToken) converter.fromJson(body, OAuthToken.class); + result = token.getAccess_token(); + } + LOGGER.exiting("AuthClient", "getAccessTokenOnAppService", result); + return result; + } + + /** + * Get the authorization token on everything else but Azure App Service. + * + * @param resource the resource. + * @return the authorization token. + */ + private String getAccessTokenOnOthers(String resource) { + LOGGER.entering("AuthClient", "getAccessTokenOnOthers", resource); + LOGGER.info("Getting access token using managed identity"); + String result = null; + + StringBuilder url = new StringBuilder(); + url.append(OAUTH2_MANAGED_IDENTITY_TOKEN_URL) + .append(RESOURCE_FRAGMENT).append(resource); + + HashMap headers = new HashMap<>(); + headers.put("Metadata", "true"); + String body = get(url.toString(), headers); + + if (body != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + OAuthToken token = (OAuthToken) converter.fromJson(body, OAuthToken.class); + result = token.getAccess_token(); + } + LOGGER.exiting("AuthClient", "getAccessTokenOnOthers", result); + return result; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/DelegateRestClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/DelegateRestClient.java new file mode 100644 index 000000000000..fc75c5e99c06 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/DelegateRestClient.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.util.Map; + +/** + * A RestClient that delegates to another RestClient. + */ +class DelegateRestClient implements RestClient { + + /** + * Stores the delegate. + */ + private RestClient delegate; + + /** + * Constructor. + */ + public DelegateRestClient(RestClient delegate) { + this.delegate = delegate; + } + + @Override + public String get(String url, Map headers) { + return delegate.get(url, headers); + } + + /** + * Get the delegate. + * + * @return the delegate. + */ + public RestClient getDelegate() { + return delegate; + } + + @Override + public String post(String url, String body, String contentType) { + return delegate.post(url, body, contentType); + } + + /** + * Set the delegate. + * + * @param delegate the delegate. + */ + public void setDelegate(RestClient delegate) { + this.delegate = delegate; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JacksonJsonConverter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JacksonJsonConverter.java new file mode 100644 index 000000000000..768c2285fadf --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JacksonJsonConverter.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +/** + * The Jackson JsonConverter. + */ +class JacksonJsonConverter implements JsonConverter { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(JacksonJsonConverter.class.getName()); + + /** + * From JSON. + * + * @param string the string. + * @param resultClass the result class. + * @return the object, or null if the conversion failed. + */ + @Override + public Object fromJson(String string, Class resultClass) { + LOGGER.entering("JacksonJsonConverter", "fromJson", new Object[] { string, resultClass }); + Object result = null; + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + result = objectMapper.readValue(string, resultClass); + } catch (JsonProcessingException e) { + LOGGER.log(WARNING, "Unable to convert from JSON", e); + } + LOGGER.exiting("JacksonJsonConverter", "fromJson", result); + return result; + } + + /** + * To JSON. + * + * @param object the object. + * @return the JSON string. + */ + @Override + public String toJson(Object object) { + LOGGER.entering("JacksonJsonConverter", "toJson", object); + String result = null; + try { + ObjectMapper mapper = new ObjectMapper(); + result = mapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + LOGGER.log(WARNING, "Unable to convert to JSON", e); + } + LOGGER.exiting("JacksonJsonConverter", "toJson", result); + return result; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverter.java new file mode 100644 index 000000000000..fb4e22c1b013 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverter.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +/** + * The JSON converter API. + */ +interface JsonConverter { + + /** + * To JSON. + * + * @param object the object to transform. + * @return the JSON string. + */ + String toJson(Object object); + + /** + * From JSON. + * + * @param string the JSON string to transform. + * @param resultClass the result class. + * @return the object, or null if the conversion failed. + */ + Object fromJson(String string, Class resultClass); +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverterFactory.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverterFactory.java new file mode 100644 index 000000000000..c0e9f7d7ddb8 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/JsonConverterFactory.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +/** + * The JsonConverterFactory. + */ +final class JsonConverterFactory { + + /** + * Constructor. + */ + private JsonConverterFactory() { + } + + /** + * Static helper method to create a JsonConverter. + * + * @return the JsonConverter. + */ + static JsonConverter createJsonConverter() { + return new JacksonJsonConverter(); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultCertificate.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultCertificate.java new file mode 100644 index 000000000000..c37e93563046 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultCertificate.java @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Set; + +/** + * The KeyVault certificate. + */ +class KeyVaultCertificate extends X509Certificate { + + /** + * Stores the delegate. + */ + private final X509Certificate delegate; + + /** + * Constructor. + * + * @param delegate the delegate. + */ + KeyVaultCertificate(X509Certificate delegate) { + super(); + this.delegate = delegate; + } + + /** + * @see X509Certificate#checkValidity() + */ + @Override + public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { + delegate.checkValidity(); + } + + /** + * @see X509Certificate#checkValidity(java.util.Date) + */ + @Override + public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { + delegate.checkValidity(date); + } + + /** + * @see X509Certificate#getBasicConstraints() + */ + @Override + public int getBasicConstraints() { + return delegate.getBasicConstraints(); + } + + /** + * @see X509Certificate#getCriticalExtensionOIDs() + */ + @Override + public Set getCriticalExtensionOIDs() { + return delegate.getCriticalExtensionOIDs(); + } + + /** + * @see X509Certificate#getEncoded() + */ + @Override + public byte[] getEncoded() throws CertificateEncodingException { + return delegate.getEncoded(); + } + + /** + * @see X509Certificate#getExtensionValue(java.lang.String) + */ + @Override + public byte[] getExtensionValue(String oid) { + return delegate.getExtensionValue(oid); + } + + /** + * @see X509Certificate#getIssuerDN() + */ + @Override + public Principal getIssuerDN() { + return delegate.getIssuerDN(); + } + + /** + * @see X509Certificate#getIssuerUniqueID() + */ + @Override + public boolean[] getIssuerUniqueID() { + return delegate.getIssuerUniqueID(); + } + + /** + * @see X509Certificate#getKeyUsage() + */ + @Override + public boolean[] getKeyUsage() { + return delegate.getKeyUsage(); + } + + /** + * @see X509Certificate#getNonCriticalExtensionOIDs() + */ + @Override + public Set getNonCriticalExtensionOIDs() { + return delegate.getNonCriticalExtensionOIDs(); + } + + /** + * @see X509Certificate#getNotAfter() + */ + @Override + public Date getNotAfter() { + return delegate.getNotAfter(); + } + + /** + * @see X509Certificate#getNotBefore() + */ + @Override + public Date getNotBefore() { + return delegate.getNotBefore(); + } + + /** + * @see X509Certificate#getPublicKey() + */ + @Override + public PublicKey getPublicKey() { + return delegate.getPublicKey(); + } + + /** + * @see X509Certificate#getSerialNumber() + */ + @Override + public BigInteger getSerialNumber() { + return delegate.getSerialNumber(); + } + + /** + * @see X509Certificate#getSigAlgName() + */ + @Override + public String getSigAlgName() { + return delegate.getSigAlgName(); + } + + /** + * @see X509Certificate#getSigAlgOID() + */ + @Override + public String getSigAlgOID() { + return delegate.getSigAlgOID(); + } + + /** + * @see X509Certificate#getSigAlgParams() + */ + @Override + public byte[] getSigAlgParams() { + return delegate.getSigAlgParams(); + } + + /** + * @see X509Certificate#getSignature() + */ + @Override + public byte[] getSignature() { + return delegate.getSignature(); + } + + /** + * @see X509Certificate#getSubjectDN() + */ + @Override + public Principal getSubjectDN() { + return delegate.getSubjectDN(); + } + + /** + * @see X509Certificate#getSubjectUniqueID() + */ + @Override + public boolean[] getSubjectUniqueID() { + return delegate.getSubjectUniqueID(); + } + + /** + * @see X509Certificate#getTBSCertificate() + */ + @Override + public byte[] getTBSCertificate() throws CertificateEncodingException { + return delegate.getTBSCertificate(); + } + + /** + * @see X509Certificate#getVersion() + */ + @Override + public int getVersion() { + return delegate.getVersion(); + } + + /** + * @see X509Certificate#hasUnsupportedCriticalExtension() + */ + @Override + public boolean hasUnsupportedCriticalExtension() { + return delegate.hasUnsupportedCriticalExtension(); + } + + /** + * @see X509Certificate#toString() + */ + @Override + public String toString() { + return delegate.toString(); + } + + /** + * @see X509Certificate#verify(java.security.PublicKey) + */ + @Override + public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + delegate.verify(key); + } + + /** + * @see X509Certificate#verify(java.security.PublicKey, java.security.Provider) + */ + @Override + public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + delegate.verify(key, sigProvider); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultClient.java new file mode 100644 index 000000000000..0bb01049216b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultClient.java @@ -0,0 +1,244 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import com.azure.security.keyvault.jca.rest.CertificateBundle; +import com.azure.security.keyvault.jca.rest.CertificateItem; +import com.azure.security.keyvault.jca.rest.CertificateListResult; +import com.azure.security.keyvault.jca.rest.CertificatePolicy; +import com.azure.security.keyvault.jca.rest.KeyProperties; +import com.azure.security.keyvault.jca.rest.SecretBundle; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.logging.Logger; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +/** + * The REST client specific to Azure KeyVault. + */ +class KeyVaultClient extends DelegateRestClient { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultClient.class.getName()); + + /** + * Stores the API version postfix. + */ + private static final String API_VERSION_POSTFIX = "?api-version=7.1"; + + /** + * Stores the KeyVault URI. + */ + private final String keyVaultUri; + + /** + * Stores the tenant ID. + */ + private String tenantId; + + /** + * Stores the client ID. + */ + private String clientId; + + /** + * Stores the client secret. + */ + private String clientSecret; + + /** + * Constructor. + * + * @param keyVaultUri the KeyVault URI. + */ + KeyVaultClient(String keyVaultUri) { + super(RestClientFactory.createClient()); + LOGGER.log(INFO, "Using KeyVault: {0}", keyVaultUri); + if (!keyVaultUri.endsWith("/")) { + keyVaultUri = keyVaultUri + "/"; + } + this.keyVaultUri = keyVaultUri; + } + + /** + * Constructor. + * + * @param keyVaultUri the KeyVault URI. + * @param tenantId the tenant ID. + * @param clientId the client ID. + * @param clientSecret the client secret. + */ + KeyVaultClient(final String keyVaultUri, final String tenantId, final String clientId, final String clientSecret) { + this(keyVaultUri); + this.tenantId = tenantId; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + /** + * Get the access token. + * + * @return the access token. + */ + private String getAccessToken() { + LOGGER.entering("KeyVaultClient", "getAccessToken"); + String accessToken = null; + try { + AuthClient authClient = new AuthClient(); + String resource = URLEncoder.encode("https://vault.azure.net", "UTF-8"); + if (tenantId != null && clientId != null && clientSecret != null) { + accessToken = authClient.getAccessToken(resource, tenantId, clientId, clientSecret); + } else { + accessToken = authClient.getAccessToken(resource); + } + } catch (UnsupportedEncodingException uee) { + LOGGER.log(WARNING, "Unsupported encoding", uee); + } + LOGGER.exiting("KeyVaultClient", "getAccessToken", accessToken); + return accessToken; + } + + /** + * Get the list of aliases. + * + * @return the list of aliases. + */ + public List getAliases() { + ArrayList result = new ArrayList<>(); + HashMap headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + getAccessToken()); + String url = String.format("%scertificates%s", keyVaultUri, API_VERSION_POSTFIX); + String response = get(url, headers); + CertificateListResult certificateListResult = null; + if (response != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + certificateListResult = (CertificateListResult) converter.fromJson(response, CertificateListResult.class); + } + if (certificateListResult != null && certificateListResult.getValue().size() > 0) { + for (CertificateItem certificateItem : certificateListResult.getValue()) { + String id = certificateItem.getId(); + String alias = id.substring(id.indexOf("certificates") + "certificates".length() + 1); + result.add(alias); + } + } + return result; + } + + /** + * Get the certificate bundle. + * + * @param alias the alias. + * @return the certificate bundle. + */ + private CertificateBundle getCertificateBundle(String alias) { + CertificateBundle result = null; + HashMap headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + getAccessToken()); + String url = String.format("%scertificates/%s%s", keyVaultUri, alias, API_VERSION_POSTFIX); + String response = get(url, headers); + if (response != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + result = (CertificateBundle) converter.fromJson(response, CertificateBundle.class); + } + return result; + } + + /** + * Get the certificate. + * + * @param alias the alias. + * @return the certificate, or null if not found. + */ + public Certificate getCertificate(String alias) { + LOGGER.entering("KeyVaultClient", "getCertificate", alias); + LOGGER.log(INFO, "Getting certificate for alias: {0}", alias); + X509Certificate certificate = null; + CertificateBundle certificateBundle = getCertificateBundle(alias); + if (certificateBundle != null) { + String certificateString = certificateBundle.getCer(); + if (certificateString != null) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + certificate = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(Base64.getDecoder().decode(certificateBundle.getCer())) + ); + } catch (CertificateException ce) { + LOGGER.log(WARNING, "Certificate error", ce); + } + } + } + LOGGER.exiting("KeyVaultClient", "getCertificate", certificate); + return certificate; + } + + /** + * Get the key. + * + * @param alias the alias. + * @param password the password. + * @return the key. + */ + public Key getKey(String alias, char[] password) { + LOGGER.entering("KeyVaultClient", "getKey", new Object[] { alias, password }); + LOGGER.log(INFO, "Getting key for alias: {0}", alias); + Key key = null; + CertificateBundle certificateBundle = getCertificateBundle(alias); + boolean isExportable = Optional.ofNullable(certificateBundle) + .map(CertificateBundle::getPolicy) + .map(CertificatePolicy::getKey_props) + .map(KeyProperties::isExportable) + .orElse(false); + if (isExportable) { + // Because the certificate is exportable the private key is + // available. So we'll use the KeyVault Secrets API to get the + // private key. + String certificateSecretUri = certificateBundle.getSid(); + HashMap headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + getAccessToken()); + String body = get(certificateSecretUri + API_VERSION_POSTFIX, headers); + if (body != null) { + JsonConverter converter = JsonConverterFactory.createJsonConverter(); + SecretBundle secretBundle = (SecretBundle) converter.fromJson(body, SecretBundle.class); + try { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + keyStore.load( + new ByteArrayInputStream(Base64.getDecoder().decode(secretBundle.getValue())), + "".toCharArray() + ); + alias = keyStore.aliases().nextElement(); + key = keyStore.getKey(alias, "".toCharArray()); + } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException ex) { + LOGGER.log(WARNING, "Unable to decode key", ex); + } + } + } else { + // The private key is not available so the certificate cannot be + // used for server side certificates or mTLS. Since we do not know + // the intent of the usage at this stage we skip this key. + } + LOGGER.exiting("KeyVaultClient", "getKey", key); + return key; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaProvider.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaProvider.java new file mode 100644 index 000000000000..c91b441867ac --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaProvider.java @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.util.Arrays; +import java.util.Collections; + +/** + * The Azure KeyVault security provider. + */ +public class KeyVaultJcaProvider extends Provider { + + /** + * Stores the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Stores the information. + */ + private static final String INFO = "Azure KeyVault JCA Provider"; + + /** + * Stores the name. + */ + private static final String NAME = "AzureKeyVault"; + + /** + * Stores the version. + */ + private static final Double VERSION = 1.0; + + /** + * Constructor. + */ + public KeyVaultJcaProvider() { + super(NAME, VERSION, INFO); + initialize(); + } + + /** + * Initialize the provider. + */ + private void initialize() { + AccessController.doPrivileged((PrivilegedAction) () -> { + putService( + new Provider.Service( + this, + "KeyManagerFactory", + "SunX509", + KeyVaultKeyManagerFactory.class.getName(), + Arrays.asList("SunX509", "IbmX509"), + null + ) + ); + + /* + * Note for Tomcat we needed to add "DKS" as an algorithm so it does + * not use an in-memory key store and later on can wrap the + * KeyManager using its JSSEKeyManager so the key alias is known. + * + * See SSLUtilBase.getKeyManagers and look for the + * "DKS".equalsIgnoreCase(certificate.getCertificateKeystoreType() + */ + putService( + new Provider.Service( + this, + "KeyStore", + "DKS", + KeyVaultKeyStore.class.getName(), + Collections.singletonList("DKS"), + null + ) + ); + putService( + new Provider.Service( + this, + "KeyStore", + "AzureKeyVault", + KeyVaultKeyStore.class.getName(), + Collections.singletonList("AzureKeyVault"), + null + ) + ); + return null; + }); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManager.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManager.java new file mode 100644 index 000000000000..247f61bcd71a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManager.java @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import javax.net.ssl.X509ExtendedKeyManager; +import java.net.Socket; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +/** + * The KeyVault variant of the X509ExtendedKeyManager. + */ +public class KeyVaultKeyManager extends X509ExtendedKeyManager { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultKeyManager.class.getName()); + + /** + * Stores the keystore. + */ + private final KeyStore keystore; + + /** + * Stores the password. + */ + private final char[] password; + + /** + * Constructor. + * + * @param keystore the keystore. + * @param password the password. + */ + public KeyVaultKeyManager(KeyStore keystore, char[] password) { + LOGGER.entering("KeyVaultKeyManager", "", new Object[] { keystore, password }); + this.keystore = keystore; + this.password = password; + } + + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { + LOGGER.entering( + "KeyVaultKeyManager", + "chooseClientAlias", + new Object[] { keyType, issuers, socket } + ); + String alias = null; + try { + /* + * If we only have one alias and the keystore type is not 'AzureKeyVault' + * return that alias as a match. + */ + if (!keystore.getProvider().getName().equals("AzureKeyVault") + && keystore.size() == 1) { + alias = keystore.aliases().nextElement(); + } + } catch (KeyStoreException kse) { + LOGGER.log(WARNING, "Unable to choose client alias", kse); + } + LOGGER.exiting("KeyVaultKeyManager", "chooseClientAlias", alias); + return alias; + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { + LOGGER.entering( + "KeyVaultKeyManager", + "chooseServerAlias", + new Object[] { keyType, issuers, socket } + ); + String alias = null; + try { + /* + * If we only have one alias and the keystore type is not 'AzureKeyVault' + * return that alias as a match. + */ + if (!keystore.getProvider().getName().equals("AzureKeyVault") + && keystore.size() == 1) { + alias = keystore.aliases().nextElement(); + } + } catch (KeyStoreException kse) { + LOGGER.log(WARNING, "Unable to choose server alias", kse); + } + LOGGER.exiting("KeyVaultKeyManager", "chooseServerAlias", alias); + return alias; + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + LOGGER.log(INFO, "KeyVaultKeyManager.getClientAliases: {0}, {1}", + new Object[] { keyType, issuers }); + String[] aliases = null; + try { + aliases = Collections.list(keystore.aliases()).toArray(new String[0]); + } catch (KeyStoreException kse) { + LOGGER.log(WARNING, "Unable to get client aliases", kse); + } + LOGGER.log(INFO, "KeyVaultKeyManager.getClientAliases: {0}", aliases); + return aliases; + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + LOGGER.entering("KeyVaultKeyManager", "getCertificateChain", alias); + List chain = new ArrayList<>(); + try { + Certificate[] keystoreChain = keystore.getCertificateChain(alias); + if (keystoreChain.length > 0) { + for (Certificate certificate : keystoreChain) { + if (certificate instanceof X509Certificate) { + chain.add((X509Certificate) certificate); + } + } + } + } catch (KeyStoreException kse) { + LOGGER.log(WARNING, "Unable to get certificate chain for alias: " + alias, kse); + } + LOGGER.exiting("KeyVaultKeyManager", "getCertificateChain", chain); + return chain.toArray(new X509Certificate[0]); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + LOGGER.entering("KeyVaultKeyManager", "getPrivateKey", alias); + PrivateKey privateKey = null; + try { + privateKey = (PrivateKey) keystore.getKey(alias, password); + } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException ex) { + LOGGER.log(WARNING, "Unable to get private key for alias: " + alias, ex); + } + LOGGER.exiting("KeyVaultKeyManager", "getPrivateKey", privateKey); + return privateKey; + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + LOGGER.entering("KeyVaultKeyManager", "getServerAliases", new Object[] { keyType, issuers }); + String[] serverAliases = new String[0]; + try { + serverAliases = Collections.list(keystore.aliases()).toArray(new String[0]); + } catch (KeyStoreException kse) { + LOGGER.log(WARNING, "Unable to get server aliases", kse); + } + LOGGER.exiting("KeyVaultKeyManager", "getServerAliases", serverAliases); + return serverAliases; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManagerFactory.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManagerFactory.java new file mode 100644 index 000000000000..7f1bf1e43648 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyManagerFactory.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * The KeyVault variant of the KeyManagerFactory. + */ +public class KeyVaultKeyManagerFactory extends KeyManagerFactorySpi { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultKeyManagerFactory.class.getName()); + + /** + * Stores the key managers. + */ + private final List keyManagers = new ArrayList<>(); + + @Override + protected void engineInit(KeyStore keystore, char[] password) { + LOGGER.entering( + "KeyVaultKeyManagerFactory", + "engineInit", + new Object[] { keystore, new String(password) } + ); + KeyVaultKeyManager manager = new KeyVaultKeyManager(keystore, password); + keyManagers.add(manager); + } + + @Override + protected void engineInit(ManagerFactoryParameters spec) { + } + + @Override + protected KeyManager[] engineGetKeyManagers() { + LOGGER.exiting("KeyVaultKeyManagerFactory", "engineGetKeyManagers", keyManagers); + return keyManagers.toArray(new KeyManager[0]); + } +} 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 new file mode 100644 index 000000000000..46e12d87f41f --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java @@ -0,0 +1,325 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.KeyStoreSpi; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Logger; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +/** + * The Azure KeyVault implementation of the KeyStoreSpi. + */ +public class KeyVaultKeyStore extends KeyStoreSpi { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultKeyStore.class.getName()); + + /** + * Stores the list of aliases. + */ + private List aliases; + + /** + * Stores the certificates by alias. + */ + private final HashMap certificates = new HashMap<>(); + + /** + * Stores the certificate keys by alias. + */ + private final HashMap certificateKeys = new HashMap<>(); + + /** + * Stores the creation date. + */ + private final Date creationDate; + + /** + * Stores the key vault client. + */ + private KeyVaultClient keyVault; + + /** + * Constructor. + * + *

+ * The constructor uses System.getProperty for + * azure.keyvault.uri, azure.keyvault.tenantId, + * azure.keyvault.clientId, + * azure.keyvault.clientSecret to initialize the keyvault + * client. + *

+ */ + public KeyVaultKeyStore() { + creationDate = new Date(); + String keyVaultUri = System.getProperty("azure.keyvault.uri"); + String tenantId = System.getProperty("azure.keyvault.tenantId"); + String clientId = System.getProperty("azure.keyvault.clientId"); + String clientSecret = System.getProperty("azure.keyvault.clientSecret"); + keyVault = new KeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret); + } + + @Override + public Enumeration engineAliases() { + if (aliases == null) { + aliases = keyVault.getAliases(); + } + return Collections.enumeration(aliases); + } + + @Override + public boolean engineContainsAlias(String alias) { + return engineIsCertificateEntry(alias); + } + + @Override + public void engineDeleteEntry(String alias) { + } + + @Override + public boolean engineEntryInstanceOf(String alias, Class entryClass) { + return super.engineEntryInstanceOf(alias, entryClass); + } + + @Override + public Certificate engineGetCertificate(String alias) { + Certificate certificate; + if (certificates.containsKey(alias)) { + certificate = certificates.get(alias); + } else { + certificate = keyVault.getCertificate(alias); + if (certificate != null) { + certificates.put(alias, certificate); + if (!aliases.contains(alias)) { + aliases.add(alias); + } + } + } + return certificate; + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + String alias = null; + if (cert != null) { + if (aliases == null) { + aliases = keyVault.getAliases(); + } + for (String candidateAlias : aliases) { + Certificate certificate = engineGetCertificate(candidateAlias); + if (certificate.equals(cert)) { + alias = candidateAlias; + break; + } + } + } + return alias; + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + Certificate[] chain = null; + Certificate certificate = engineGetCertificate(alias); + if (certificate != null) { + chain = new Certificate[1]; + chain[0] = certificate; + } + return chain; + } + + @Override + public Date engineGetCreationDate(String alias) { + return creationDate; + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + return super.engineGetEntry(alias, protParam); + } + + @Override + public Key engineGetKey(String alias, char[] password) { + Key key; + if (certificateKeys.containsKey(alias)) { + key = certificateKeys.get(alias); + } else { + key = keyVault.getKey(alias, password); + if (key != null) { + certificateKeys.put(alias, key); + if (!aliases.contains(alias)) { + aliases.add(alias); + } + } + } + return key; + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + if (aliases == null) { + aliases = keyVault.getAliases(); + } + return aliases.contains(alias); + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return engineIsCertificateEntry(alias); + } + + @Override + public void engineLoad(KeyStore.LoadStoreParameter param) { + if (param instanceof KeyVaultLoadStoreParameter) { + KeyVaultLoadStoreParameter parameter = (KeyVaultLoadStoreParameter) param; + keyVault = new KeyVaultClient( + parameter.getUri(), + parameter.getTenantId(), + parameter.getClientId(), + parameter.getClientSecret()); + } + sideLoad(); + } + + @Override + public void engineLoad(InputStream stream, char[] password) { + sideLoad(); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate certificate) { + if (aliases == null) { + aliases = keyVault.getAliases(); + } + if (!aliases.contains(alias)) { + aliases.add(alias); + certificates.put(alias, certificate); + } + } + + @Override + public void engineSetEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam) throws KeyStoreException { + super.engineSetEntry(alias, entry, protParam); + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) { + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) { + } + + @Override + public int engineSize() { + return aliases != null ? aliases.size() : 0; + } + + @Override + public void engineStore(OutputStream stream, char[] password) { + } + + @Override + public void engineStore(KeyStore.LoadStoreParameter param) { + } + + /** + * Get the filenames. + * + * @param path the path. + * @return the filenames. + * @throws IOException when an I/O error occurs. + */ + private String[] getFilenames(String path) throws IOException { + List filenames = new ArrayList<>(); + InputStream in = getClass().getResourceAsStream(path); + if (in != null) { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String resource; + while ((resource = br.readLine()) != null) { + filenames.add(resource); + } + } + return filenames.toArray(new String[0]); + } + + /** + * Read all the bytes for a given input stream. + * + * @param inputStream the input stream. + * @return the byte-array. + * @throws IOException when an I/O error occurs. + */ + private byte[] readAllBytes(InputStream inputStream) throws IOException { + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + while (true) { + int r = inputStream.read(buffer); + if (r == -1) { + break; + } + byteOutput.write(buffer, 0, r); + } + return byteOutput.toByteArray(); + } + + /** + * Side-load certificate from classpath. + */ + private void sideLoad() { + try { + String[] filenames = getFilenames("/keyvault"); + if (filenames.length > 0) { + for (String filename : filenames) { + try (InputStream inputStream = getClass().getResourceAsStream("/keyvault/" + filename)) { + String alias = filename; + if (alias != null) { + if (alias.lastIndexOf('.') != -1) { + alias = alias.substring(0, alias.lastIndexOf('.')); + } + byte[] bytes = readAllBytes(inputStream); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate certificate = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(bytes)); + engineSetCertificateEntry(alias, certificate); + LOGGER.log(INFO, "Side loaded certificate: {0} from: {1}", + new Object[] { alias, filename }); + } catch (CertificateException e) { + LOGGER.log(WARNING, "Unable to side-load certificate", e); + } + } + } + } + } + } catch (IOException ioe) { + LOGGER.log(WARNING, "Unable to determine certificates to side-load", ioe); + } + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java new file mode 100644 index 000000000000..5a1e616eb168 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.security.KeyStore; + +/** + * The Azure KeyVault LoadStoreParameter of the KeyStoreSpi. + */ +public class KeyVaultLoadStoreParameter implements KeyStore.LoadStoreParameter { + + /** + * Stores the URI. + */ + private final String uri; + + /** + * Stores the tenant id. + */ + private final String tenantId; + + /** + * Stores the client ID. + */ + private final String clientId; + + /** + * Stores the client secret. + */ + private final String clientSecret; + + /** + * Constructor. + * + * @param uri the KeyVault URI. + * @param tenantId the tenant ID. + * @param clientId the client ID. + * @param clientSecret the client secret. + */ + public KeyVaultLoadStoreParameter(String uri, String tenantId, String clientId, String clientSecret) { + this.uri = uri; + this.tenantId = tenantId; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + /** + * Get the protection parameter. + * + * @return null + */ + @Override + public KeyStore.ProtectionParameter getProtectionParameter() { + return null; + } + + /** + * Get the client id. + * + * @return the client id. + */ + public String getClientId() { + return clientId; + } + + /** + * Get the client secret. + * + * @return the client secret. + */ + public String getClientSecret() { + return clientSecret; + } + + /** + * Get the tenant id. + * + * @return the tenant id. + */ + public String getTenantId() { + return tenantId; + } + + /** + * Get the uri. + * + * @return the URI. + */ + public String getUri() { + return uri; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManager.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManager.java new file mode 100644 index 000000000000..0a35624aeb87 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManager.java @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.Socket; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedTrustManager; + +/** + * The KeyVault variant of the X509TrustManager. + */ +public class KeyVaultTrustManager extends X509ExtendedTrustManager implements X509TrustManager { + + /** + * Stores the default trust manager. + */ + private X509TrustManager defaultTrustManager; + + /** + * Stores the keystore. + */ + private KeyStore keyStore; + + /** + * Constructor. + * + * @param keyStore the keystore. + */ + public KeyVaultTrustManager(KeyStore keyStore) { + this.keyStore = keyStore; + if (this.keyStore == null) { + try { + this.keyStore = KeyStore.getInstance("AzureKeyVault"); + this.keyStore.load(null, null); + } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException ex) { + ex.printStackTrace(); + } + } + try { + TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX", "SunJSSE"); + factory.init(keyStore); + defaultTrustManager = (X509TrustManager) factory.getTrustManagers()[0]; + } catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException ex) { + ex.printStackTrace(); + } + if (defaultTrustManager == null) { + try { + TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX", "IbmJSSE"); + factory.init(keyStore); + defaultTrustManager = (X509TrustManager) factory.getTrustManagers()[0]; + } catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException ex) { + ex.printStackTrace(); + } + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + + boolean pass = true; + + /* + * Step 1 - see if the default trust manager passes. + */ + try { + defaultTrustManager.checkClientTrusted(chain, authType); + } catch (CertificateException ce) { + pass = false; + } + + /* + * Step 2 - see if the certificate exists in the keystore. + */ + if (!pass) { + String alias = null; + try { + alias = keyStore.getCertificateAlias(chain[0]); + } catch (KeyStoreException kse) { + kse.printStackTrace(); + } + if (alias == null) { + throw new CertificateException("Unable to verify in keystore"); + } + } + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + + boolean pass = true; + + /* + * Step 1 - see if the default trust manager passes. + */ + try { + defaultTrustManager.checkServerTrusted(chain, authType); + } catch (CertificateException ce) { + pass = false; + } + + /* + * Step 2 - see if the certificate exists in the keystore. + */ + if (!pass) { + String alias = null; + try { + alias = keyStore.getCertificateAlias(chain[0]); + } catch (KeyStoreException kse) { + kse.printStackTrace(); + } + if (alias == null) { + throw new CertificateException("Unable to verify in keystore"); + } + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + checkServerTrusted(chain, authType); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + checkServerTrusted(chain, authType); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactory.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactory.java new file mode 100644 index 000000000000..73d1e4abe696 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactory.java @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactorySpi; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * The KeyVault variant of the TrustManagerFactory. + */ +public class KeyVaultTrustManagerFactory extends TrustManagerFactorySpi { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultTrustManagerFactory.class.getName()); + + /** + * Stores the key managers. + */ + private final List trustManagers = new ArrayList<>(); + + @Override + protected void engineInit(KeyStore keystore) { + LOGGER.entering("KeyVaultKeyManagerFactory", "engineInit", keystore); + trustManagers.add(new KeyVaultTrustManager(keystore)); + } + + @Override + protected void engineInit(ManagerFactoryParameters spec) { + LOGGER.entering("KeyVaultKeyManagerFactory", "engineInit", spec); + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return trustManagers.toArray(new TrustManager[0]); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactoryProvider.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactoryProvider.java new file mode 100644 index 000000000000..a70efe2ca1e6 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultTrustManagerFactoryProvider.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; + +/** + * The Azure KeyVault TrustManagerFactory provider. + */ +public class KeyVaultTrustManagerFactoryProvider extends Provider { + + /** + * Stores the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Stores the information. + */ + private static final String INFO = "Azure KeyVault TrustManagerFactory Provider"; + + /** + * Stores the name. + */ + private static final String NAME = "AzureKeyVaultTrustManagerFactory"; + + /** + * Stores the version. + */ + private static final Double VERSION = 1.0; + + /** + * Constructor. + */ + public KeyVaultTrustManagerFactoryProvider() { + super(NAME, VERSION, INFO); + initialize(); + } + + /** + * Initialize the provider. + */ + private void initialize() { + AccessController.doPrivileged((PrivilegedAction) () -> { + putService( + new Provider.Service( + this, + "TrustManagerFactory", + "PKIX", + KeyVaultTrustManagerFactory.class.getName(), + null, + null + ) + ); + return null; + }); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/LegacyRestClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/LegacyRestClient.java new file mode 100644 index 000000000000..90460273d213 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/LegacyRestClient.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.security.keyvault.jca; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.HttpEntities; + +import java.io.IOException; +import java.util.Map; + +/** + * The RestClient that uses the Apache HttpClient class. + */ +class LegacyRestClient implements RestClient { + + /** + * Constructor. + */ + public LegacyRestClient() { + } + + @Override + public String get(String url, Map headers) { + String result = null; + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet(url); + if (headers != null) { + headers.entrySet().forEach(entry -> { + String key = entry.getKey(); + String value = entry.getValue(); + httpGet.addHeader(key, value); + }); + } + HttpClientResponseHandler responseHandler = (ClassicHttpResponse response) -> { + int status = response.getCode(); + String result1 = null; + if (status >= 200 && status < 300) { + HttpEntity entity = response.getEntity(); + result1 = entity != null ? EntityUtils.toString(entity) : null; + } + return result1; + }; + result = client.execute(httpGet, responseHandler); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + return result; + } + + @Override + public String post(String url, String body, String contentType) { + String result = null; + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(HttpEntities.create(body, ContentType.create(contentType))); + HttpClientResponseHandler responseHandler = (ClassicHttpResponse response) -> { + int status = response.getCode(); + String result1 = null; + if (status >= 200 && status < 300) { + HttpEntity entity = response.getEntity(); + result1 = entity != null ? EntityUtils.toString(entity) : null; + } + return result1; + }; + result = client.execute(httpPost, responseHandler); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + return result; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClient.java new file mode 100644 index 000000000000..510262d5b810 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClient.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import java.util.Map; + +/** + * The REST client API. + */ +interface RestClient { + + /** + * Issue a GET request. + * + * @param url the URL. + * @param headers the request headers map. + * @return the response body as a string. + */ + String get(String url, Map headers); + + /** + * Issue a POST request. + * + * @param url the URL. + * @param body the request body. + * @return the response body as a string. + */ + String post(String url, String body, String contentType); +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClientFactory.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClientFactory.java new file mode 100644 index 000000000000..5d3124767505 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/RestClientFactory.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +/** + * The RestClientFactory. + */ +class RestClientFactory { + + /** + * Constructor. + */ + private RestClientFactory() { + } + + /** + * Static helper method to create a RestClient. + * + * @return the RestClient. + */ + static RestClient createClient() { + return new LegacyRestClient(); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/package-info.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/package-info.java new file mode 100644 index 000000000000..e40cd274cf58 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * The Azure KeyVault JCA Provider package. + */ +package com.azure.security.keyvault.jca; diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateBundle.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateBundle.java new file mode 100644 index 000000000000..c874a5483b3c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateBundle.java @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * The CertificateBundle REST model. + */ +public class CertificateBundle implements Serializable { + + /** + * Stores the CER bytes. + */ + private String cer; + + /** + * Stores the Key ID. + */ + private String kid; + + /** + * Stores the policy. + */ + private CertificatePolicy policy; + + /** + * Stores the Secret ID. + */ + private String sid; + + /** + * Get the CER string. + * + * @return the CER string. + */ + public String getCer() { + return cer; + } + + /** + * Get the Key ID. + * + * @return the Key ID. + */ + public String getKid() { + return kid; + } + + /** + * Get the policy. + * + * @return the policy. + */ + public CertificatePolicy getPolicy() { + return policy; + } + + /** + * Get the Secret ID. + * + * @return the Secret ID. + */ + public String getSid() { + return sid; + } + + /** + * Set the CER string. + * + * @param cer the CER string. + */ + public void setCer(String cer) { + this.cer = cer; + } + + /** + * Set the Key ID. + * + * @param kid the Key ID. + */ + public void setKid(String kid) { + this.kid = kid; + } + + /** + * Set the policy. + * + * @param policy the policy. + */ + public void setPolicy(CertificatePolicy policy) { + this.policy = policy; + } + + /** + * Set the Secret ID. + * + * @param sid the Secret ID. + */ + public void setSid(String sid) { + this.sid = sid; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateItem.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateItem.java new file mode 100644 index 000000000000..66dbcf277c66 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateItem.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * The CertificateItem REST model. + */ +public class CertificateItem implements Serializable { + + /** + * Stores the id. + */ + private String id; + + /** + * Get the id. + * + * @return the id. + */ + public String getId() { + return id; + } + + /** + * Set the id. + * + * @param id the id. + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateListResult.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateListResult.java new file mode 100644 index 000000000000..59dd984e3db4 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificateListResult.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; +import java.util.List; + +/** + * The CertificateItem REST model. + */ +public class CertificateListResult implements Serializable { + + /** + * Stores the value. + */ + private List value; + + /** + * Get the value. + * + * @return the id. + */ + public List getValue() { + return value; + } + + /** + * Set the value. + * + * @param value the value. + */ + public void setValue(List value) { + this.value = value; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificatePolicy.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificatePolicy.java new file mode 100644 index 000000000000..e678c296b6bb --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/CertificatePolicy.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * The CertificatePolicy REST model. + */ +public class CertificatePolicy implements Serializable { + + /** + * Stores the key properties. + */ + private KeyProperties keyProperties; + + /** + * Get the key properties. + * + * @return the key properties. + */ + public KeyProperties getKey_props() { + return keyProperties; + } + + /** + * Set the key properties. + * + * @param keyProperties the key properties. + */ + public void setKey_props(KeyProperties keyProperties) { + this.keyProperties = keyProperties; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/KeyProperties.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/KeyProperties.java new file mode 100644 index 000000000000..52ee90d4826a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/KeyProperties.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * The KeyProperties REST model. + */ +public class KeyProperties implements Serializable { + + /** + * Stores if the key is exportable. + */ + private boolean exportable; + + /** + * Is the key exportable. + * + * @return true if exportable, false otherwise. + */ + public boolean isExportable() { + return exportable; + } + + /** + * Set the key to be exportable. + * + * @param exportable the exportable flag. + */ + public void setExportable(boolean exportable) { + this.exportable = exportable; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/OAuthToken.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/OAuthToken.java new file mode 100644 index 000000000000..e394baaa5591 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/OAuthToken.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * An OAuth2 token. + */ +public class OAuthToken implements Serializable { + + /** + * Stores the access token. + */ + private String access_token; + + /** + * Get the access token. + * + * @return the access token. + */ + public String getAccess_token() { + return access_token; + } + + /** + * Set the access token. + * + * @param accessToken the access token. + */ + public void setAccess_token(String accessToken) { + this.access_token = accessToken; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/SecretBundle.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/SecretBundle.java new file mode 100644 index 000000000000..a15f8ba47745 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/rest/SecretBundle.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca.rest; + +import java.io.Serializable; + +/** + * The SecretBundle REST model. + */ +public class SecretBundle implements Serializable { + + /** + * Stores the value. + */ + private String value; + + /** + * Get the value. + * + * @return the value. + */ + public String getValue() { + return value; + } + + /** + * Set the value. + * + * @param value the value. + */ + public void setValue(String value) { + this.value = value; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/AuthClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/AuthClientTest.java new file mode 100644 index 000000000000..c77edab01251 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/AuthClientTest.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import java.net.URLEncoder; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * The JUnit test for the AuthClient. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class AuthClientTest { + + /** + * Test getAuthorizationToken method. + */ + @Test + public void testGetAuthorizationToken() throws Exception { + String tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + String clientId = "2b8f123b-b18a-4077-bce0-42e10ce8bbab"; + String clientSecret = "72-~tZ9~cG~rimDI0EkQSMQ1D9DYmGmI_I"; + AuthClient authClient = new AuthClient(); + String result = authClient.getAccessToken( + "https://management.azure.com/", + tenantId, + clientId, + URLEncoder.encode(clientSecret, "UTF-8") + ); + assertNotNull(result); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/DelegateRestClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/DelegateRestClientTest.java new file mode 100644 index 000000000000..94d534680dfe --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/DelegateRestClientTest.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * The JUnit tests for the DelegateRestClient class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class DelegateRestClientTest { + + /** + * Test of getDelegate method, of class DelegateRestClient. + */ + @Test + public void testGetDelegate() { + DelegateRestClient client = new DelegateRestClient(RestClientFactory.createClient()); + assertNotNull(client.getDelegate()); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JacksonJsonConverterTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JacksonJsonConverterTest.java new file mode 100644 index 000000000000..5ad5273d2f7d --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/JacksonJsonConverterTest.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import com.azure.security.keyvault.jca.rest.CertificateBundle; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The JUnit tests for the JsonbJsonConverter class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class JacksonJsonConverterTest { + + /** + * Test fromJson method. + */ + @Test + public void testFromJson() { + String string = "{ \"cer\": \"cer\" }"; + JacksonJsonConverter converter = new JacksonJsonConverter(); + CertificateBundle bundle = (CertificateBundle) converter.fromJson(string, CertificateBundle.class); + assertNotNull(bundle); + assertEquals("cer", bundle.getCer()); + } + + /** + * Test toJson method. + * + * @throws Exception when a serious error occurs. + */ + @Test + public void testToJson() throws Exception { + JacksonJsonConverter converter = new JacksonJsonConverter(); + CertificateBundle bundle = new CertificateBundle(); + bundle.setCer("value"); + String string = converter.toJson(bundle); + assertTrue(string.contains("cer")); + assertTrue(string.contains("\"value\"")); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificateTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificateTest.java new file mode 100644 index 000000000000..ee521e308f08 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificateTest.java @@ -0,0 +1,369 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * The JUnit tests for the KeyVaultCertificate class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class KeyVaultCertificateTest { + + /** + * Stores the CER test certificate (which is valid til 2120). + */ + private static final String TEST_CERTIFICATE + = "MIIDeDCCAmCgAwIBAgIQGghBu97rQJKNnUHPWU7xjDANBgkqhkiG9w0BAQsFADAk" + + "MSIwIAYDVQQDExlodW5kcmVkLXllYXJzLmV4YW1wbGUuY29tMCAXDTIwMDkwMjE3" + + "NDUyNFoYDzIxMjAwOTAyMTc1NTI0WjAkMSIwIAYDVQQDExlodW5kcmVkLXllYXJz" + + "LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuU14" + + "btkN5wmcO2WKXqm1NUKXzi79EtqiFFkrLgPAwj5NNwMw2Akm3GpdEpwkJ8/q3l7d" + + "frDEVOO9gwZbz7xppyqutjxjllw8CCgjFdfK02btz56CGgh3X25ZZtzPbuMZJM0j" + + "o4mVEdaFNJ0eUeMppS0DcbbuTWCF7Jf1gvr8GVqx+E0IJUFkE+D4kdTbnJSaeK0A" + + "KEt94z88MPX18h8ud14uRVmUCYVZrZeswdE2tO1BpazrXELHuXCtrjGxsDDjDzeP" + + "98aFI9kblkqoJS4TsmloLEjwZLm80cyJDEmpXXMtR7C0FFXFI1BAtIa4mxSgBLsT" + + "L4GVPEGNANR8COYkHQIDAQABo4GjMIGgMA4GA1UdDwEB/wQEAwIFoDAJBgNVHRME" + + "AjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAkBgNVHREEHTAbghlo" + + "dW5kcmVkLXllYXJzLmV4YW1wbGUuY29tMB8GA1UdIwQYMBaAFOGTt4H3ho30O4e+" + + "hebwJjm2VMvIMB0GA1UdDgQWBBThk7eB94aN9DuHvoXm8CY5tlTLyDANBgkqhkiG" + + "9w0BAQsFAAOCAQEAGp8mCioVCmM+kZv6r+K2j2uog1k4HBwN1NfRoSsibDB8+QXF" + + "bmNf3M0imiuR/KJgODyuROwaa/AalxNFMOP8XTL2YmP7XsddBs9ONHHQXKjY/Ojl" + + "PsIPR7vZjwYPfEB+XEKl2fOIxDQQ921POBV7M6DdTC49T5X+FsLR1AIIfinVetT9" + + "QmNuvzulBX0T0rea/qpcPK4HTj7ToyImOaf8sXRv2s2ODLUrKWu5hhTNH2l6RIkQ" + + "U/aIAdQRfDaSE9jhtcVu5d5kCgBs7nz5AzeCisDPo5zIt4Mxej3iVaAJ79oEbHOE" + + "p192KLXLV/pscA4Wgb+PJ8AAEa5B6xq8p9JO+Q=="; + + /** + * Stores the X.509 certificate. + */ + private X509Certificate x509Certificate; + + /** + * Setup before each test. + * + */ + @BeforeEach + public void setUp() { + try { + byte[] certificateBytes = Base64.getDecoder().decode(TEST_CERTIFICATE); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + x509Certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateBytes)); + } catch (CertificateException e) { + throw new ProviderException(e); + } + } + + /** + * Test checkValidity method. + */ + @Test + public void testCheckValidity() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + certificate.checkValidity(); + } catch (CertificateExpiredException | CertificateNotYetValidException cnyve) { + fail(); + } + } + + /** + * Test checkValidity method. + */ + @Test + public void testCheckValidity2() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + certificate.checkValidity(new Date(100, Calendar.FEBRUARY, 1)); + fail(); + } catch (CertificateExpiredException ex) { + fail(); + } catch (CertificateNotYetValidException exception) { + // expecting this as the TEST_CERTIFICATE is not valid against given date. + } + } + + /** + * Test checkValidity method. + */ + @Test + public void testCheckValidity3() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + certificate.checkValidity(new Date(200, Calendar.FEBRUARY, 1)); + } catch (CertificateExpiredException | CertificateNotYetValidException exception) { + fail(); + } + } + + /** + * Test getBasicConstraints method. + */ + @Test + public void testGetBasicConstraints() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals(-1, certificate.getBasicConstraints()); + } + + /** + * Test getCriticalExtensionOIDs method. + */ + @Test + public void testGetCriticalExtensionOIDs() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + Set criticalExtensions = certificate.getCriticalExtensionOIDs(); + assertFalse(criticalExtensions.isEmpty()); + assertTrue(criticalExtensions.contains("2.5.29.15")); + } + + /** + * Test getEncoded method. + */ + @Test + public void testGetEncoded() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getEncoded()); + } catch (CertificateEncodingException cee) { + fail(); + } + } + + /** + * Test getExtensionValue method. + */ + @Test + public void testGetExtensionValue() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getExtensionValue("2.5.29.15")); + } + + /** + * Test getIssuerDN method. + */ + @Test + public void testGetIssuerDN() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals("CN=hundred-years.example.com", certificate.getIssuerDN().getName()); + } + + /** + * Test getIssuerUniqueID method. + */ + @Test + public void testGetIssuerUniqueID() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNull(certificate.getIssuerUniqueID()); + } + + /** + * Test getKeyUsage method. + */ + @Test + public void testGetKeyUsage() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getKeyUsage()); + } + + /** + * Test getNonCriticalExtensionOIDs method. + */ + @Test + public void testGetNonCriticalExtensionOIDs() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + Set nonCriticalExtensions = certificate.getNonCriticalExtensionOIDs(); + assertFalse(nonCriticalExtensions.isEmpty()); + } + + /** + * Test getNotAfter method. + */ + @Test + public void testGetNotAfter() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + Date notAfter = certificate.getNotAfter(); + assertTrue(new Date().before(notAfter)); + } + + /** + * Test getNotBefore method. + */ + @Test + public void testGetNotBefore() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + Date notBefore = certificate.getNotBefore(); + assertTrue(new Date().after(notBefore)); + } + + /** + * Test getPublicKey method. + */ + @Test + public void testGetPublicKey() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getPublicKey()); + } + + /** + * Test getSerialNumber method. + */ + @Test + public void testGetSerialNumber() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getSerialNumber()); + } + + /** + * Test getSigAlgName method. + */ + @Test + public void testGetSigAlgName() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals("SHA256withRSA", certificate.getSigAlgName()); + } + + /** + * Test getSigAlgOID method. + */ + @Test + public void testGetSigAlgOID() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals("1.2.840.113549.1.1.11", certificate.getSigAlgOID()); + } + + /** + * Test getSigAlgParams method. + */ + @Test + public void testGetSigAlgParams() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNull(certificate.getSigAlgParams()); + } + + /** + * Test getSignature method. + */ + @Test + public void testGetSignature() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getSignature()); + } + + /** + * Test getSubjectDN method. + */ + @Test + public void testGetSubjectDN() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals("CN=hundred-years.example.com", certificate.getSubjectDN().getName()); + } + + /** + * Test getSubjectUniqueID method. + */ + @Test + public void testGetSubjectUniqueID() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNull(certificate.getSubjectUniqueID()); + } + + /** + * Test getTBSCertificate method. + */ + @Test + public void testGetTBSCertificate() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.getTBSCertificate()); + } catch (CertificateEncodingException cee) { + fail(); + } + } + + /** + * Test getVersion method. + */ + @Test + public void testGetVersion() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertEquals(3, certificate.getVersion()); + } + + /** + * Test hasUnsupportedCriticalExtension method. + */ + @Test + public void testHasUnsupportedCriticalExtension() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertFalse(certificate.hasUnsupportedCriticalExtension()); + } + + /** + * Test toString method. + */ + @Test + public void testToString() { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + assertNotNull(certificate.toString()); + } + + /** + * Test verify method. + */ + @Test + public void testVerify() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + PublicKey publicKey = certificate.getPublicKey(); + certificate.verify(publicKey); + } catch (CertificateException | NoSuchAlgorithmException + | InvalidKeyException | NoSuchProviderException + | SignatureException e) { + fail(); + } + } + + /** + * Test verify method. + */ + @Test + public void testVerify2() { + try { + KeyVaultCertificate certificate = new KeyVaultCertificate(x509Certificate); + PublicKey publicKey = certificate.getPublicKey(); + certificate.verify(publicKey, "SunRsaSign"); + } catch (CertificateException | NoSuchAlgorithmException + | InvalidKeyException | NoSuchProviderException + | SignatureException e) { + fail(); + } + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultJcaProviderTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultJcaProviderTest.java new file mode 100644 index 000000000000..7940baa2c917 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultJcaProviderTest.java @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import java.security.KeyStore; +import java.security.Security; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * The JUnit tests for the KeyVaultProvider class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class KeyVaultJcaProviderTest { + + /** + * Test the constructor. + */ + @Test + public void testConstructor() { + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + assertNotNull(provider); + } + + /** + * Test getting a certificate using the Provider. + * + * @throws Exception when an error occurs. + */ + @Test + public void testGetCertificate() throws Exception { + Security.addProvider(new KeyVaultJcaProvider()); + KeyStore keystore = KeyStore.getInstance("AzureKeyVault"); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.load(parameter); + assertNull(keystore.getCertificate("myalias")); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultKeyStoreTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultKeyStoreTest.java new file mode 100644 index 000000000000..1d016bf85f0a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultKeyStoreTest.java @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.security.ProviderException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The JUnit tests for the KeyVaultKeyStore class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class KeyVaultKeyStoreTest { + + /** + * Stores the CER test certificate (which is valid til 2120). + */ + private static final String TEST_CERTIFICATE + = "MIIDeDCCAmCgAwIBAgIQGghBu97rQJKNnUHPWU7xjDANBgkqhkiG9w0BAQsFADAk" + + "MSIwIAYDVQQDExlodW5kcmVkLXllYXJzLmV4YW1wbGUuY29tMCAXDTIwMDkwMjE3" + + "NDUyNFoYDzIxMjAwOTAyMTc1NTI0WjAkMSIwIAYDVQQDExlodW5kcmVkLXllYXJz" + + "LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuU14" + + "btkN5wmcO2WKXqm1NUKXzi79EtqiFFkrLgPAwj5NNwMw2Akm3GpdEpwkJ8/q3l7d" + + "frDEVOO9gwZbz7xppyqutjxjllw8CCgjFdfK02btz56CGgh3X25ZZtzPbuMZJM0j" + + "o4mVEdaFNJ0eUeMppS0DcbbuTWCF7Jf1gvr8GVqx+E0IJUFkE+D4kdTbnJSaeK0A" + + "KEt94z88MPX18h8ud14uRVmUCYVZrZeswdE2tO1BpazrXELHuXCtrjGxsDDjDzeP" + + "98aFI9kblkqoJS4TsmloLEjwZLm80cyJDEmpXXMtR7C0FFXFI1BAtIa4mxSgBLsT" + + "L4GVPEGNANR8COYkHQIDAQABo4GjMIGgMA4GA1UdDwEB/wQEAwIFoDAJBgNVHRME" + + "AjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAkBgNVHREEHTAbghlo" + + "dW5kcmVkLXllYXJzLmV4YW1wbGUuY29tMB8GA1UdIwQYMBaAFOGTt4H3ho30O4e+" + + "hebwJjm2VMvIMB0GA1UdDgQWBBThk7eB94aN9DuHvoXm8CY5tlTLyDANBgkqhkiG" + + "9w0BAQsFAAOCAQEAGp8mCioVCmM+kZv6r+K2j2uog1k4HBwN1NfRoSsibDB8+QXF" + + "bmNf3M0imiuR/KJgODyuROwaa/AalxNFMOP8XTL2YmP7XsddBs9ONHHQXKjY/Ojl" + + "PsIPR7vZjwYPfEB+XEKl2fOIxDQQ921POBV7M6DdTC49T5X+FsLR1AIIfinVetT9" + + "QmNuvzulBX0T0rea/qpcPK4HTj7ToyImOaf8sXRv2s2ODLUrKWu5hhTNH2l6RIkQ" + + "U/aIAdQRfDaSE9jhtcVu5d5kCgBs7nz5AzeCisDPo5zIt4Mxej3iVaAJ79oEbHOE" + + "p192KLXLV/pscA4Wgb+PJ8AAEa5B6xq8p9JO+Q=="; + + @Test + public void testEngineGetCertificate() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertNull(keystore.engineGetCertificate("myalias")); + } + + @Test + public void testEngineGetCertificateAlias() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertNull(keystore.engineGetCertificateAlias(null)); + } + + @Test + public void testEngineGetCertificateChain() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertNull(keystore.engineGetCertificateChain("myalias")); + } + + @Test + public void testEngineIsCertificateEntry() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertFalse(keystore.engineIsCertificateEntry("myalias")); + } + + @Test + public void testEngineSetCertificateEntry() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + + X509Certificate certificate; + + try { + byte[] certificateBytes = Base64.getDecoder().decode(TEST_CERTIFICATE); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateBytes)); + } catch (CertificateException e) { + throw new ProviderException(e); + } + + keystore.engineSetCertificateEntry("setcert", certificate); + assertNotNull(keystore.engineGetCertificate("setcert")); + } + + @Test + public void testEngineGetKey() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertNull(keystore.engineGetKey("myalias", null)); + } + + @Test + public void testEngineIsKeyEntry() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertFalse(keystore.engineIsKeyEntry("myalias")); + } + + @Test + public void testEngineSetKeyEntry() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + keystore.engineSetKeyEntry("myalias", null, null); + } + + @Test + public void testEngineSetKeyEntry2() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + keystore.engineSetKeyEntry("myalias", null, null, null); + } + + @Test + public void testEngineAliases() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertTrue(keystore.engineAliases().hasMoreElements()); + } + + @Test + public void testEngineContainsAlias() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + keystore.engineLoad(parameter); + assertFalse(keystore.engineContainsAlias("myalias")); + } + + @Test + public void testEngineGetCreationDate() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + assertNotNull(keystore.engineGetCreationDate("myalias")); + } + + @Test + public void testEngineDeleteEntry() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + keystore.engineDeleteEntry("myalias"); + } + + @Test + public void testEngineSize() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + assertTrue(keystore.engineSize() >= 0); + } + + @Test + public void testEngineStore() { + KeyVaultKeyStore keystore = new KeyVaultKeyStore(); + keystore.engineStore(null, null); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java new file mode 100644 index 000000000000..727dca026e32 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * The JUnit tests for the KeyVaultLoadStoreParameter class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class KeyVaultLoadStoreParameterTest { + + /** + * Test getProtectionParameter method. + */ + @Test + public void testGetProtectionParameter() { + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + null, + null, + null + ); + assertNull(parameter.getProtectionParameter()); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/LegacyRestClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/LegacyRestClientTest.java new file mode 100644 index 000000000000..730a6867f19e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/LegacyRestClientTest.java @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * The JUnit tests for the LegacyRestClient class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class LegacyRestClientTest { + + /** + * Test constructor. + */ + @Test + public void testConstructor() { + LegacyRestClient client = new LegacyRestClient(); + assertNotNull(client); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/RestClientFactoryTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/RestClientFactoryTest.java new file mode 100644 index 000000000000..c99550db7609 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/RestClientFactoryTest.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * The JUnit tests for the RestClientFactory class. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class RestClientFactoryTest { + + /** + * Test createClient method. + */ + @Test + public void testCreateClient() { + assertNotNull(RestClientFactory.createClient()); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/ServerSocketTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/ServerSocketTest.java new file mode 100644 index 000000000000..4b2f4c1e9f50 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/ServerSocketTest.java @@ -0,0 +1,248 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.jca; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder; +import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.ssl.SSLContexts; +import org.junit.jupiter.api.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.security.KeyStore; +import java.security.Security; +import java.security.cert.X509Certificate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * The unit test validating the ServerSocket is created using a certificate from Azure KeyVault. + * + * @author Manfred Riem (manfred.riem@microsoft.com) + */ +public class ServerSocketTest { + + /** + * Test SSLServerSocket without client trust. + * + * @throws Exception when a serious error occurs. + */ + @Test + public void testServerSocket() throws Exception { + + /* + * Add JCA provider. + */ + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + Security.addProvider(provider); + + /* + * Setup server side. + * + * - Create an Azure KeyVault specific instance of a KeyStore. + * - Set the KeyManagerFactory to use that KeyStore. + * - Set the SSL context to use the KeyManagerFactory. + * - Create the SSLServerSocket using th SSL context. + */ + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + ks.load(parameter); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, "".toCharArray()); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(kmf.getKeyManagers(), null, null); + + SSLServerSocketFactory factory = context.getServerSocketFactory(); + SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(8765); + + Thread server = new Thread(() -> { + while (true) { + try { + Socket socket = serverSocket.accept(); + try (OutputStream outputStream = socket.getOutputStream()) { + outputStream.write("HTTP/1.1 204\r\n".getBytes()); + outputStream.flush(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }); + server.start(); + + /* + * Setup client side + * + * - Create an SSL context. + * - Set SSL context to trust any certificate. + * - Create SSL connection factory. + * - Set hostname verifier to trust any hostname. + */ + + SSLContext sslContext = SSLContexts + .custom() + .loadTrustMaterial((final X509Certificate[] chain, final String authType) -> true) + .build(); + + SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder + .create() + .setSslContext(sslContext) + .setHostnameVerifier((hostname, session) -> true) + .build(); + + PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder + .create() + .setSSLSocketFactory(sslSocketFactory) + .build(); + + /* + * And now execute the test. + */ + String result = null; + + try (CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build()) { + HttpGet httpGet = new HttpGet("https://localhost:8765"); + HttpClientResponseHandler responseHandler = (ClassicHttpResponse response) -> { + int status = response.getCode(); + String result1 = null; + if (status == 204) { + result1 = "Success"; + } + return result1; + }; + result = client.execute(httpGet, responseHandler); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + /* + * And verify all went well. + */ + assertEquals("Success", result); + } + + /** + * Test SSLServerSocket WITH self-signed client trust. + * + * @throws Exception when a serious error occurs. + */ + @Test + public void testServerSocketWithSelfSignedClientTrust() throws Exception { + + /* + * Add JCA provider. + */ + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + Security.addProvider(provider); + + /* + * Setup server side. + * + * - Create an Azure KeyVault specific instance of a KeyStore. + * - Set the KeyManagerFactory to use that KeyStore. + * - Set the SSL context to use the KeyManagerFactory. + * - Create the SSLServerSocket using th SSL context. + */ + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( + System.getProperty("azure.keyvault.uri"), + System.getProperty("azure.tenant.id"), + System.getProperty("azure.client.id"), + System.getProperty("azure.client.secret")); + ks.load(parameter); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, "".toCharArray()); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(kmf.getKeyManagers(), null, null); + + SSLServerSocketFactory factory = context.getServerSocketFactory(); + SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(8766); + + Thread server = new Thread(() -> { + while (true) { + try { + Socket socket = serverSocket.accept(); + try (OutputStream outputStream = socket.getOutputStream()) { + outputStream.write("HTTP/1.1 204\r\n".getBytes()); + outputStream.flush(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }); + server.start(); + + /* + * Setup client side + * + * - Create an SSL context. + * - Set SSL context to trust any certificate. + * - Create SSL connection factory. + * - Set hostname verifier to trust any hostname. + */ + + SSLContext sslContext = SSLContexts + .custom() + .loadTrustMaterial(ks, new TrustSelfSignedStrategy()) + .build(); + + SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder + .create() + .setSslContext(sslContext) + .setHostnameVerifier((hostname, session) -> true) + .build(); + + PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder + .create() + .setSSLSocketFactory(sslSocketFactory) + .build(); + + /* + * And now execute the test. + */ + String result = null; + + try (CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build()) { + HttpGet httpGet = new HttpGet("https://localhost:8766"); + HttpClientResponseHandler responseHandler = (ClassicHttpResponse response) -> { + int status = response.getCode(); + String result1 = null; + if (status == 204) { + result1 = "Success"; + } + return result1; + }; + result = client.execute(httpGet, responseHandler); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + /* + * And verify all went well. + */ + assertEquals("Success", result); + } +} diff --git a/sdk/keyvault/ci.yml b/sdk/keyvault/ci.yml index 331e673a980f..f8c015c6c168 100644 --- a/sdk/keyvault/ci.yml +++ b/sdk/keyvault/ci.yml @@ -54,6 +54,9 @@ extends: - name: azure-security-keyvault-certificates groupId: com.azure safeName: azuresecuritykeyvaultcertificates + - name: azure-security-keyvault-jca + groupId: com.azure + safeName: azuresecuritykeyvaultjca - name: azure-security-keyvault-keys groupId: com.azure safeName: azuresecuritykeyvaultkeys diff --git a/sdk/keyvault/pom.xml b/sdk/keyvault/pom.xml index 6042dcedc8af..a282efaeaafb 100644 --- a/sdk/keyvault/pom.xml +++ b/sdk/keyvault/pom.xml @@ -18,6 +18,7 @@ microsoft-azure-keyvault-test azure-security-keyvault-administration azure-security-keyvault-certificates + azure-security-keyvault-jca azure-security-keyvault-keys azure-security-keyvault-secrets diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/Dockerfile b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/Dockerfile new file mode 100644 index 000000000000..091170acf881 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/Dockerfile @@ -0,0 +1,3 @@ +FROM mcr.microsoft.com/java/jdk:8-zulu-centos +ADD target/app.jar . +CMD java -jar app.jar diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/pom.xml new file mode 100644 index 000000000000..7937464ffb28 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/pom.xml @@ -0,0 +1,111 @@ + + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.3.RELEASE + + + + + com.azure.spring + azure-spring-boot-sample-keyvault-certificates + 1.0.0 + jar + + Azure Spring Boot Starter Sample - Key Vault Certificates + Sample project for Azure Key Vault Certificates Starter + https://github.com/Azure/azure-sdk-for-java + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + com.azure.spring + azure-spring-boot-starter-keyvault-certificates + 3.0.0-beta.1 + + + org.springframework.boot + spring-boot-starter-actuator + + + + app + + + org.springframework.boot + spring-boot-maven-plugin + + + ${azure.keyvault.uri} + ${azure.tenant.id} + ${azure.client.id} + ${azure.client.secret} + ${server.ssl.key-alias} + + + + + + diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleApplication.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleApplication.java new file mode 100644 index 000000000000..ced366272239 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleApplication.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.security.keyvault.certificates.sample; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleApplication.class, args); + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleController.java b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleController.java new file mode 100644 index 000000000000..e6bb798fc363 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/sample/SampleController.java @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.security.keyvault.certificates.sample; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class SampleController { + + @GetMapping("/") + public String helloWorld() { + return "Hello World"; + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/application.properties b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/application.properties new file mode 100644 index 000000000000..932545d08d83 --- /dev/null +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/application.properties @@ -0,0 +1,23 @@ +# The URI to the Azure KeyVault used +azure.keyvault.uri=${AZURE_KEYVAULT_URI} +# The alias corresponding to the certificate in Azure KeyVault. +server.ssl.key-alias=${SERVER_SSL_KEY_ALIAS} +# The keystore type that enables the use of Azure KeyVault for your server-side +# SSL certificate. +server.ssl.key-store-type=AzureKeyVault +# The truststore type that enables the use of Azure KeyVault for trusted +# certificates, a.k.a the ones you trust when making an outbound SSL connection +# server.ssl.trust-store-type=AzureKeyVault +# The Tenant ID for your Azure KeyVault (needed if you are not using managed +# identity). +azure.keyvault.tenantId=${AZURE_KEYVAULT_TENTANT_ID} +# The Client ID that has been setup with access to your Azure KeyVault (needed +# if you are not using managed identity). +azure.keyvault.clientId=${AZURE_KEYVAULT_CLIENT_ID} +# The Client Secret that will be used for accessing your Azure KeyVault (needed +# if you are not using managed identity). +azure.keyvault.clientSecret=${AZURE_KEYVAULT_CLIENT_SECRET} +# The server port. +server.port=8443 +# Just for debugging purposes. +management.endpoints.web.exposure.include=* diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/keyvault/sideload.x509 b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/keyvault/sideload.x509 new file mode 100644 index 000000000000..7c9f48aae658 Binary files /dev/null and b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates/src/main/resources/keyvault/sideload.x509 differ diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/CHANGELOG.md b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/CHANGELOG.md new file mode 100644 index 000000000000..02434784717d --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 3.0.0-beta.1 (Unreleased) diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/README.md b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/README.md new file mode 100644 index 000000000000..e32959f329f4 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/README.md @@ -0,0 +1,278 @@ +# Azure Key Vault Certificates Spring Boot starter + +## Server side SSL + +### Using a managed identity + +To use the starter for server side SSL, you will need to add the following to +your application.properties (if the application is using Spring Cloud Config +Server for its configuration add it to the bootstrap.yml of the application) + +``` +azure.keyvault.uri= +server.ssl.key-alias= +server.ssl.key-store-type=AzureKeyVault +``` + +Note: make sure the managed identity has access to the Azure KeyVault to access +keys, secrets and certificates. + +Add then add the following Maven dependency to your POM file. + +```xml + + com.azure + azure-spring-boot-starter-keyvault-certificates + +``` + +### Using a client ID and client secret + +To use the starter for server side SSL, you will need to add the following to +your application.properties (if the application is using Spring Cloud Config +Server for its configuration add it to the bootstrap.yml of the application) + +``` +azure.keyvault.uri= +azure.keyvault.tenantId= +azure.keyvault.clientId= +azure.keyvault.clientSecret= +server.ssl.key-alias= +server.ssl.key-store-type=AzureKeyVault +``` + +Note: make sure the client ID has access to the Azure KeyVault to access +keys, secrets and certificates. + +Add then add the following Maven dependency to your POM file. + +```xml + + com.azure + azure-spring-boot-starter-keyvault-certificates + +``` + +## Client side SSL + +### Using a managed identity + +To use the starter for client side SSL, you will need to add the following to +your application.properties (if the application is using Spring Cloud Config +Server for its configuration add it to the bootstrap.yml of the application) + +``` +azure.keyvault.uri= +``` +Note: make sure the managed identity has access to the Azure KeyVault to access +keys, secrets and certificates. + +Add then add the following Maven dependency to your POM file. + +```xml + + com.azure + azure-spring-boot-starter-keyvault-certificates + +``` + +If you are using RestTemplate use code similar to the example below. + +```java + @Bean + public RestTemplate restTemplate() throws Exception { + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(ks, new TrustSelfSignedStrategy()) + .build(); + + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(); + + requestFactory.setHttpClient(httpClient); + RestTemplate restTemplate = new RestTemplate(requestFactory); + return restTemplate; + } +``` + +### Using a client ID and client secret + +To use the starter for client side SSL, you will need to add the following to +your application.properties (if the application is using Spring Cloud Config +Server for its configuration add it to the bootstrap.yml of the application) + +``` +azure.keyvault.uri= +azure.keyvault.tenantId= +azure.keyvault.clientId= +azure.keyvault.clientSecret= +``` + +Note: make sure the client ID has access to the Azure KeyVault to access +keys, secrets and certificates. + +Add then add the following Maven dependency to your POM file. + +```xml + + com.azure + azure-spring-boot-starter-keyvault-certificates + +``` + +Then if you are using RestTemplate use the code below as a starting +point: + +```java + @Bean + public RestTemplate restTemplate() throws Exception { + KeyStore ks = KeyStore.getInstance("AzureKeyVault"); + SSLContext sslContext = SSLContexts.custom() + .loadTrustMaterial(ks, new TrustSelfSignedStrategy()) + .build(); + + HostnameVerifier allowAll = (String hostName, SSLSession session) -> true; + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, allowAll); + + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(); + + requestFactory.setHttpClient(httpClient); + RestTemplate restTemplate = new RestTemplate(requestFactory); + return restTemplate; + } +``` + +## Configuring Spring Cloud Gateway + +To configure Spring Cloud Gateway for outbound SSL you will need +to add the following configuration: + +```yaml +azure: + keyvault: + uri: + jca: + overrideTrustManagerFactory: true +``` + +Note: if any of your routes point to a service where the FQDN does not match the +issued certificate you will need to disable hostname verification. This will +be the case if your service is dynamically assigned a hostname by the hosting +platform you use. In this particular case add the configuration below to disable +hostname verification: + +```yaml +azure: + keyvault: + jca: + disableHostnameVerification: true +``` + +If you are developing you can completely disable the certificate and hostname +validation altogether by using the configuration below. Note this is NOT +recommended for production! + +```yaml +spring: + cloud: + gateway: + httpclient: + ssl: + useInsecureTrustManager: true +``` + +## Creating an Azure Key Vault + +To create an Azure KeyVault use the command line below: + +```shell + export KEY_VAULT=mykeyvault + export RESOURCE_GROUP=myresourcegroup + az keyvault create --name ${KEY_VAULT} -g ${RESOURCE_GROUP} +``` + +## Create a self-signed certificate + +To create a self-signed certificate use the command line below: + +```shell + export CERTIFICATE_ALIAS=self-signed + az keyvault certificate create --vault-name ${KEY_VAULT} \ + -n ${CERTIFICATE_ALIAS} -p "$(az keyvault certificate get-default-policy)" +``` + +## Assign a managed identity (to an Azure Spring Cloud application) + +To assign a managed identity use the command line below: + +```shell + export SPRING_CLOUD_APP=myspringcloudapp + az spring-cloud app identity assign --name ${SPRING_CLOUD_APP} + export MANAGED_IDENTITY=$(az spring-cloud app show \ + --name ${SPRING_CLOUD_APP} --query identity.principalId --output tsv) +``` + +## Grant a managed identity with access to Azure Key Vault + +To grant access use the command line below: + +```shell + az keyvault set-policy --name ${KEY_VAULT} \ + --object-id ${MANAGED_IDENTITY} \ + --key-permisssions get list \ + --secret-permissions get list \ + --certificate-permissions get list +``` + +## Side-loading certificates + +This starter allows you to side-load certificates by supplying them as part of +the application. + +To side-load add your certificates to the `src/main/resources/keyvault` folder. + +Notes: +1. The alias (certificate name) is constructed from the filename of the +certificate (minus the extension). So if your filename is `mycert.x509` the +certificate will be added with the alias of `mycert`. +2. Certificates coming from Azure KeyVault take precedence over +side-loaded certificates. + +## Testing the current version under development + +If you want to test the current version under development you will have to + +1. Build and install the [Microsoft Azure JCA Provider] for KeyVault +1. Build and install this starter. + +To build and install the starter use the following command line: + +``` + mvn clean install -DskipTests=true +``` + + +# Azure KeyVault Certificates client library for Java + +# Getting started + +# Key concepts + +# Examples + +# Troubleshooting + +# Next steps + +# Contributing \ No newline at end of file diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/pom.xml b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/pom.xml new file mode 100644 index 000000000000..432c1ae31fb9 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/pom.xml @@ -0,0 +1,185 @@ + + + 4.0.0 + + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure.spring + azure-spring-boot-starter-keyvault-certificates + 3.0.0-beta.1 + + Azure Key Vault Certificates Spring Boot Starter + Spring Boot Starter supporting Azure Key Vault Certificates + https://github.com/Azure/azure-sdk-for-java + + + + org.springframework.boot + spring-boot-starter + 2.3.3.RELEASE + + + org.springframework.boot + spring-boot-starter-validation + 2.3.3.RELEASE + + + com.azure + azure-security-keyvault-jca + 1.0.0-beta.1 + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + org.springframework.boot:spring-boot-starter:[2.3.3.RELEASE] + org.springframework.boot:spring-boot-starter-validation:[2.3.3.RELEASE] + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + + empty-javadoc-jar-with-readme + package + + jar + + + javadoc + ${project.basedir}/javadocTemp + + + + + + empty-sources-jar-with-readme + package + + jar + + + sources + ${project.basedir}/sourceTemp + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + copy-readme-to-javadocTemp + prepare-package + + + Deleting existing ${project.basedir}/javadocTemp + + + + Copying ${project.basedir}/README.md to + ${project.basedir}/javadocTemp/README.md + + + + + + run + + + + copy-readme-to-sourceTemp + prepare-package + + + Deleting existing ${project.basedir}/sourceTemp + + + + Copying ${project.basedir}/README.md to + ${project.basedir}/sourceTemp/README.md + + + + + + run + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + none + + + + + + org.revapi + revapi-maven-plugin + 0.11.2 + + true + + + + + + 1.8 + 1.8 + + diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/KeyVaultCertificatesEnvironmentPostProcessor.java b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/KeyVaultCertificatesEnvironmentPostProcessor.java new file mode 100644 index 000000000000..6d76e12b4619 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/KeyVaultCertificatesEnvironmentPostProcessor.java @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.security.keyvault.certificates.starter; + +import com.azure.security.keyvault.jca.KeyVaultJcaProvider; +import com.azure.security.keyvault.jca.KeyVaultTrustManagerFactoryProvider; + +import java.security.Security; +import java.util.Properties; +import java.util.logging.Logger; +import javax.net.ssl.HttpsURLConnection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; + +import static org.springframework.core.Ordered.LOWEST_PRECEDENCE; + +@Order(LOWEST_PRECEDENCE) +public class KeyVaultCertificatesEnvironmentPostProcessor implements EnvironmentPostProcessor { + + /** + * Stores the logger. + */ + private static final Logger LOGGER = Logger.getLogger(KeyVaultCertificatesEnvironmentPostProcessor.class.getName()); + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + + Properties systemProperties = System.getProperties(); + + String uri = environment.getProperty("azure.keyvault.uri"); + if (uri != null) { + systemProperties.put("azure.keyvault.uri", uri); + + String tenantId = environment.getProperty("azure.keyvault.tenantId"); + if (tenantId != null) { + systemProperties.put("azure.keyvault.tenantId", tenantId); + } + + String clientId = environment.getProperty("azure.keyvault.clientId"); + if (clientId != null) { + systemProperties.put("azure.keyvault.clientId", clientId); + } + + String clientSecret = environment.getProperty("azure.keyvault.clientSecret"); + if (clientSecret != null) { + systemProperties.put("azure.keyvault.clientSecret", clientSecret); + } + + String keyStoreType = environment.getProperty("server.ssl.key-store-type"); + + if (keyStoreType != null && keyStoreType.equals("AzureKeyVault")) { + MutablePropertySources sources = environment.getPropertySources(); + Properties properties = new Properties(); + properties.put("server.ssl.key-store", "classpath:keyvault.dummy"); + + try { + Class.forName("org.apache.tomcat.InstanceManager"); + properties.put("server.ssl.key-store-type", "DKS"); + } catch (ClassNotFoundException ex) { + } + + PropertiesPropertySource propertySource = + new PropertiesPropertySource("KeyStorePropertySource", properties); + sources.addFirst(propertySource); + } + + String trustStoreType = environment.getProperty("server.ssl.trust-store-type"); + + if (trustStoreType != null && trustStoreType.equals("AzureKeyVault")) { + MutablePropertySources sources = environment.getPropertySources(); + Properties properties = new Properties(); + properties.put("server.ssl.trust-store", "classpath:keyvault.dummy"); + + try { + Class.forName("org.apache.tomcat.InstanceManager"); + properties.put("server.ssl.trust-store-type", "DKS"); + } catch (ClassNotFoundException ex) { + } + + PropertiesPropertySource propertySource = + new PropertiesPropertySource("TrustStorePropertySource", properties); + sources.addFirst(propertySource); + } + + KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); + Security.insertProviderAt(provider, 1); + + String enabled = environment.getProperty("azure.keyvault.jca.overrideTrustManagerFactory"); + if (Boolean.parseBoolean(enabled)) { + KeyVaultTrustManagerFactoryProvider factoryProvider = + new KeyVaultTrustManagerFactoryProvider(); + Security.insertProviderAt(factoryProvider, 1); + } + + enabled = environment.getProperty("azure.keyvault.jca.disableHostnameVerification"); + if (Boolean.parseBoolean(enabled)) { + HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> { + return true; + }); + } + } + } +} diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/package-info.java b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/package-info.java new file mode 100644 index 000000000000..2de5b95f6ece --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/java/com/azure/spring/security/keyvault/certificates/starter/package-info.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2020 Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * The Azure Key Vault Certificate Spring Boot starter package. + */ +package com.azure.spring.security.keyvault.certificates.starter; diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/META-INF/spring.factories b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..001c6d937258 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.env.EnvironmentPostProcessor=com.azure.spring.security.keyvault.certificates.starter.KeyVaultCertificatesEnvironmentPostProcessor + diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/config/application.properties b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/config/application.properties new file mode 100644 index 000000000000..997ec590954b --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/config/application.properties @@ -0,0 +1,3 @@ +server.ssl.key-store-password=doesnotmatter +server.ssl.key-password=doesnotmatter +server.ssl.trust-store-password=doesnotmatter diff --git a/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/keyvault.dummy b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/keyvault.dummy new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-keyvault-certificates/src/main/resources/keyvault.dummy @@ -0,0 +1 @@ + diff --git a/sdk/spring/ci.yml b/sdk/spring/ci.yml index c947a220b03f..d405d35bdaac 100644 --- a/sdk/spring/ci.yml +++ b/sdk/spring/ci.yml @@ -48,6 +48,9 @@ extends: - name: azure-spring-boot-starter-data-gremlin groupId: com.azure.spring safeName: azurespringbootstarterdatagremlin + - name: azure-spring-boot-starter-keyvault-certificates + groupId: com.azure.spring + safeName: azurespringbootstarterkeyvaultcertificates - name: azure-spring-boot-starter-keyvault-secrets groupId: com.azure.spring safeName: azurespringbootstarterkeyvaultsecrets diff --git a/sdk/spring/pom.xml b/sdk/spring/pom.xml index e6b636166615..bbd8b8ae78b9 100644 --- a/sdk/spring/pom.xml +++ b/sdk/spring/pom.xml @@ -15,6 +15,7 @@ azure-spring-boot-starter-active-directory-b2c azure-spring-boot-starter-cosmos azure-spring-boot-starter-data-gremlin + azure-spring-boot-starter-keyvault-certificates azure-spring-boot-starter-keyvault-secrets azure-spring-boot-starter-metrics azure-spring-boot-starter-servicebus-jms @@ -33,6 +34,7 @@ azure-spring-boot-samples/azure-spring-boot-sample-active-directory-stateless azure-spring-boot-samples/azure-spring-boot-sample-cosmos azure-spring-boot-samples/azure-spring-boot-sample-data-gremlin + azure-spring-boot-samples/azure-spring-boot-sample-keyvault-certificates azure-spring-boot-samples/azure-spring-boot-sample-keyvault-secrets azure-spring-boot-samples/azure-spring-boot-sample-mediaservices azure-spring-boot-samples/azure-spring-boot-sample-servicebus