azure - 与使用 Java 从 Azure Keyvault 获取证书相关的问题

标签 azure jetty azure-keyvault

我正在将旧应用程序迁移到 Azure 云中。在现有的应用程序中,我们在启动时保护 Jetty 服务器的安全,因此我们使用 jks 文件来保护 Jetty 服务器的安全。

现在我们正在迁移到 Azure 云,因此我们必须从 Azure keyvault 获取 .jks 文件。那么如何从 Azure keyvault 获取完整的 .jks 文件。我能够从 Keyvault 获取 secret ,但无法获取证书(我在 Azure keyvault 中上传)。我不确定我们是否有任何 API 可以提供证书文件。

下面的代码我用来获取 secret 和证书:

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials;
import com.microsoft.azure.keyvault.models.CertificateBundle;
import com.microsoft.azure.keyvault.models.SecretBundle;

public class DemoTest {
    
    private static String vaultBase = "https://abc.vault.azure.net/";
    private static String ClientId = "*******************";
    private static String clientKey = "*****************";
    
    
    public static void main(String[] args) {
        
        KeyVaultClient keyVaultClient = GetKeyVaultClient();
        SecretBundle getSecret=keyVaultClient.getSecret(vaultBase, "mysecretkey");
        SecretBundle getSecret1=keyVaultClient.getSecret(vaultBase, "DB-PASSWORD-POC");
        SecretBundle getSecret2=keyVaultClient.getSecret(vaultBase, "certificate-value");
//      SecretBundle getSecret2=keyVaultClient.getSecret(vaultBase, "DB-PASSWORD-DEV");
        CertificateBundle getCertificate=keyVaultClient.getCertificate(vaultBase, "abcprod");
        CertificateBundle bundle = keyVaultClient.getCertificate("https://abc.vault.azure.net/certificates/abcprod/********386c9403bab8337ce21d27495");
        System.out.println(getSecret.value());
        System.out.println(getSecret1.value());
        System.out.println(getSecret2.value());
//      System.out.println(getCertificate.contentType());
//      System.out.println(getCertificate.id());
//      System.out.println(getCertificate.kid());
//      System.out.println(getCertificate.toString());
//      System.out.println(getCertificate.attributes().toString());
//      System.out.println(getCertificate.keyIdentifier().name());
//      System.out.println(getCertificate.sid());
//      System.out.println(getCertificate.certificateIdentifier().baseIdentifier());
//      System.out.println(bundle.cer());
//      System.out.println(bundle);
        
    }


    private static KeyVaultClient GetKeyVaultClient() {
        return new KeyVaultClient(new KeyVaultCredentials() {
            @Override
            public String doAuthenticate(String authorization, String resource, String scope) {
                String token = null;
                try {
                    AuthenticationResult authResult = getAccessToken(authorization, resource);
                    token = authResult.getAccessToken();
                } catch (Exception e) {
                    e.printStackTrace();

                }
                return token;
            }
        });
    }
    
     public static AuthenticationResult getAccessToken(String authorization, String resource) throws InterruptedException, ExecutionException, MalformedURLException {

            AuthenticationResult result = null;

            //Starts a service to fetch access token.
            ExecutorService service = null;
            try {
                service = Executors.newFixedThreadPool(1);
                AuthenticationContext context = new AuthenticationContext(authorization, false, service);

                Future<AuthenticationResult> future = null;

                //Acquires token based on client ID and client secret.
                if (ClientId != null && clientKey != null) {
                    ClientCredential credentials = new ClientCredential(ClientId, clientKey);
                    future = context.acquireToken(resource, credentials, null);
                }

                result = future.get();
            } finally {
                service.shutdown();
            }

            if (result == null) {
                throw new RuntimeException("Authentication results were null.");
            }
            return result;
        }

}

我们使用以下代码保护我们的 jetty 服务器:

public class ABCSubscriber {
        private static final int Port = 9090;
        private static final String KeyStoreType = "jks";
        private static final String KeyStoreFile = "/home/abc/xyz/subscriber.jks";
        private static final String KeyStorePassword = "******";
        private static final String KeyPassword = "*******";
        private static final String ContextPath = "/";
        private static final String URLPattern = "/*";

        public static void main(String[] args) throws Exception {
              
        Server server = new Server();
        HttpConfiguration http_config = new HttpConfiguration();
        http_config.setSecureScheme("https");
        http_config.setSecurePort(Port);
        http_config.setRequestHeaderSize(8192);

        // HTTP connector
        ServerConnector http = new ServerConnector(server,
                new HttpConnectionFactory(http_config));
        http.setPort(9091);
        http.setIdleTimeout(30000);

        // SSL Context Factory
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStoreType(KeyStoreType);
        sslContextFactory.setKeyStorePath(KeyStoreFile);
        sslContextFactory.setKeyStorePassword(KeyStorePassword);
        sslContextFactory.setKeyManagerPassword(KeyPassword);

        // sslContextFactory.setTrustStorePath(ncm.getKSFile());
        // sslContextFactory.setTrustStorePassword("changeit");
        sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA",
                "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");

        // SSL HTTP Configuration
        HttpConfiguration https_config = new HttpConfiguration(http_config);
        https_config.addCustomizer(new SecureRequestCustomizer());

        // SSL Connector
        ServerConnector sslConnector = new ServerConnector(server,
            new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
            new HttpConnectionFactory(https_config));
        sslConnector.setPort(Port);
        server.addConnector(sslConnector);

        /**Disable and enable protocols*/
                String[] includeProtocols = {"TLSv1.1","TLSv1.2"};
        sslContextFactory.addExcludeProtocols("TLSv1.0");
        sslContextFactory.setIncludeProtocols(includeProtocols);
     
        /**End Disable and enable protocols*/

        // HTTPS Configuration
        ServerConnector https = new ServerConnector(server,
            new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
                new HttpConnectionFactory(https_config));
        https.setPort(Port);
        https.setIdleTimeout(30000);
        //server.setConnectors(new Connector[] { http, https });
        server.setConnectors(new Connector[] {  https });
                ServletContextHandler ctxt = new ServletContextHandler(0);
                ctxt.setContextPath(ContextPath);
                server.setHandler(ctxt);

                ctxt.addServlet(new ServletHolder(new ABCServlet()), "/*");

                try {
                    server.start();
                } catch ( Exception e ) {
                        e.getLocalizedMessage();
                };
        server.join();

        }
}

那么,有什么方法可以从Azure keyvault获取证书文件吗?如果不是,那么我们如何使用证书来保护服务器?

有人可以帮我解决这个问题吗?

提前致谢!!!

最佳答案

您需要下载证书的私钥作为 secret 。使用更明显的 GetCertificate 获取 secret 只会返回证书的公钥部分。

我知道下面的代码示例中是 C#,但这就是我从 Key Vault 中获取证书的方式,我希望您能了解如何在 Java 中执行相同的操作:

        /// <summary>
        /// Helper method to get a certificate
        /// 
        /// Source https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs
        /// </summary>
        /// <param name="certificateClient"></param>
        /// <param name="secretClient"></param>
        /// <param name="certificateName"></param>
        /// <returns></returns>
        private static X509Certificate2 GetCertificateAsync(CertificateClient certificateClient,
                                                                SecretClient secretClient,
                                                                string certificateName)
        {

            KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);

            // Return a certificate with only the public key if the private key is not exportable.
            if (certificate.Policy?.Exportable != true)
            {
                return new X509Certificate2(certificate.Cer);
            }

            // Parse the secret ID and version to retrieve the private key.
            string[] segments = certificate.SecretId.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
            if (segments.Length != 3)
            {
                throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length}, URI: {certificate.SecretId}");
            }

            string secretName = segments[1];
            string secretVersion = segments[2];

            KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);

            // For PEM, you'll need to extract the base64-encoded message body.
            // .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
            if ("application/x-pkcs12".Equals(secret.Properties.ContentType, StringComparison.InvariantCultureIgnoreCase))
            {
                byte[] pfx = Convert.FromBase64String(secret.Value);
                return new X509Certificate2(pfx);
            }

            throw new NotSupportedException($"Only PKCS#12 is supported. Found Content-Type: {secret.Properties.ContentType}");
        }
    }
}

关于azure - 与使用 Java 从 Azure Keyvault 获取证书相关的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66999571/

相关文章:

sql-server - 如何从azure网站连接aws VM上的数据库

azure - 如何限制 AzCopy 从 azure 表存储复制的实体数量?

java - Jetty 9.3.4 不支持集成测试

servlets - Jetty 过滤器修改响应 - java.lang.IllegalStateException : WRITER

azure - 使用 BeginUploadFromStream 时 Blob 不存在

Azure管道: How to release Project binary file into Azure machine

java - 如何使用同一端口拥有具有多个上下文的 websocket(使用 jetty 8)

azure - 在 Azure Key Vault 支持的 Databricks 中创建 secret 范围失败

添加新 key 版本时,Azure Web App 会失去对 keyvault key 的权限

encryption - Azure Key Vault - 获取加密密码