【问题标题】:Issue related to fetching certificate from Azure Keyvault using Java与使用 Java 从 Azure Keyvault 获取证书相关的问题
【发布时间】:2021-07-04 01:30:20
【问题描述】:

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

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

下面是我用来获取机密和证书的代码:

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;
        }

}

我们正在使用此代码保护我们的码头服务器:

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 获取证书文件?如果没有,我们如何使用证书来保护服务器?

谁能帮我解决这个问题?

提前致谢!!!

【问题讨论】:

标签: azure jetty azure-keyvault


【解决方案1】:

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

我知道在下面的代码示例中这是 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}");
        }
    }
}

【讨论】:

  • 谢谢 Tore,你有 Java 代码的参考吗?我对 C# 没有任何想法,所以我无法理解。
  • 对不起,我没有 Java 版本。 Azure 没有提供一种简单直观的方式来下载带有私钥的证书,这真的很糟糕。
猜你喜欢
  • 1970-01-01
  • 2018-12-17
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多