来自AEM的双向传输层安全性(mTLS)身份验证

了解如何从AEM对需要相互传输层安全性(mTLS)身份验证的Web API进行HTTPS调用。

mTLS或双向TLS身份验证要求​ 客户端和服务器都相互进行身份验证,从而增强了TLS协议的安全性。 此身份验证通过使用数字证书来完成。 它通常用于高度安全性和身份验证至关重要的场景。

默认情况下,在尝试与需要mTLS身份验证的Web API建立HTTPS连接时,连接会失败,并出现以下错误:

javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_required

当客户端不提供证书来验证自身时,会出现此问题。

让我们了解如何使用Apache HttpClient和​ AEM的KeyStore和TrustStore ​成功调用需要mTLS身份验证的API。

HttpClient和加载AEM KeyStore资料

从较高层面来看,需要执行以下步骤才能从AEM中调用受mTLS保护的API。

AEM证书生成

与贵组织的安全团队合作,请求AEM证书。 安全团队提供或询问与证书相关的详细信息,例如密钥、证书签名请求(CSR),并使用CSR颁发证书。

出于演示目的,请生成与证书相关的详细信息,例如密钥、证书签名请求(CSR)。 在以下示例中,自签名CA用于颁发证书。

  • 首先,生成内部证书颁发机构(CA)证书。

    code language-shell
    # Create an internal Certification Authority (CA) certificate
    openssl req -new -x509 -days 9999 -keyout internal-ca-key.pem -out internal-ca-cert.pem
    
  • 生成AEM证书。

    code language-shell
    # Generate Key
    openssl genrsa -out client-key.pem
    
    # Generate CSR
    openssl req -new -key client-key.pem -out client-csr.pem
    
    # Generate certificate and sign with internal Certification Authority (CA)
    openssl x509 -req -days 9999 -in client-csr.pem -CA internal-ca-cert.pem -CAkey internal-ca-key.pem -CAcreateserial -out client-cert.pem
    
    # Verify certificate
    openssl verify -CAfile internal-ca-cert.pem client-cert.pem
    
  • 将AEM私钥转换为DER格式,AEM KeyStore需要DER格式的私钥。

    code language-shell
    openssl pkcs8 -topk8 -inform PEM -outform DER -in client-key.pem -out client-key.der -nocrypt
    
TIP
自签名CA证书仅用于开发目的。 对于生产,请使用受信任的证书颁发机构(CA)来颁发证书。

证书交换

如果对AEM证书使用自签名CA(如上所述),请将证书或内部证书颁发机构(CA)证书发送到API提供程序。

此外,如果API提供商使用自签名CA证书,则从API提供商接收证书或内部证书颁发机构(CA)证书。

证书导入

要导入AEM证书,请执行以下步骤:

  1. 以​ 管理员 ​身份登录到​ AEM作者

  2. 导航到​ AEM Author > Tools > Security > Users > Create or Select an existing user

    创建或选择现有用户

    为演示目的,创建了名为mtl-demo-user的新用户。

  3. 要打开​ 用户属性,请单击用户名。

  4. 单击​ 密钥库 ​选项卡,然后单击​ 创建密钥库 ​按钮。 然后在​ 设置KeyStore访问密码 ​对话框中,为此用户的密钥库设置密码,然后单击“保存”。

    创建密钥库

  5. 在新屏幕的​ 从DER文件添加私钥 ​部分下,执行以下步骤:

    1. 输入别名

    2. 以DER格式导入上面生成的AEM私钥。

    3. 导入上面生成的证书链文件。

    4. 单击提交

      导入AEM私钥

  6. 验证是否已成功导入证书。

    AEM私钥和证书已导入

如果API提供程序正在使用自签名CA证书,请将收到的证书导入AEM的TrustStore,请按照此处中的步骤操作。

同样,如果AEM使用自签名CA证书,请请求API提供商导入它。

使用HttpClient的典型mTLS API调用代码

更新Java™代码,如下所示。 要使用@Reference注释来获取AEM KeyStoreService服务,调用代码必须是OSGi组件/服务或Sling模型(并在其中使用@OsgiService)。

...

// Get AEM's KeyStoreService reference
@Reference
private com.adobe.granite.keystore.KeyStoreService keyStoreService;

...

// Get AEM KeyStore using KeyStoreService
KeyStore aemKeyStore = getAEMKeyStore(keyStoreService, resourceResolver);

if (aemKeyStore != null) {

    // Create SSL Context
    SSLContextBuilder sslbuilder = new SSLContextBuilder();

    // Load AEM KeyStore material into above SSL Context with keystore password
    // Ideally password should be encrypted and stored in OSGi config
    sslbuilder.loadKeyMaterial(aemKeyStore, "admin".toCharArray());

    // If API provider cert is self-signed, load AEM TrustStore material into above SSL Context
    // Get AEM TrustStore
    KeyStore aemTrustStore = getAEMTrustStore(keyStoreService, resourceResolver);
    sslbuilder.loadTrustMaterial(aemTrustStore, null);

    // Create SSL Connection Socket using above SSL Context
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
            sslbuilder.build(), NoopHostnameVerifier.INSTANCE);

    // Create HttpClientBuilder
    HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    httpClientBuilder.setSSLSocketFactory(sslsf);

    // Create HttpClient
    CloseableHttpClient httpClient = httpClientBuilder.build();

    // Invoke API
    closeableHttpResponse = httpClient.execute(new HttpGet(MTLS_API_ENDPOINT));

    // Code that reads response code and body from the 'closeableHttpResponse' object
    ...
}

/**
 * Returns the AEM KeyStore of a user. In this example we are using the
 * 'mtl-demo-user' user.
 *
 * @param keyStoreService
 * @param resourceResolver
 * @return AEM KeyStore
 */
private KeyStore getAEMKeyStore(KeyStoreService keyStoreService, ResourceResolver resourceResolver) {

    // get AEM KeyStore of 'mtl-demo-user' user, you can create a user or use an existing one.
    // Then create keystore and upload key, certificate files.
    KeyStore aemKeyStore = keyStoreService.getKeyStore(resourceResolver, "mtl-demo-user");

    return aemKeyStore;
}

/**
 *
 * Returns the global AEM TrustStore
 *
 * @param keyStoreService OOTB OSGi service that makes AEM based KeyStore
 *                         operations easy.
 * @param resourceResolver
 * @return
 */
private KeyStore getAEMTrustStore(KeyStoreService keyStoreService, ResourceResolver resourceResolver) {

    // get AEM TrustStore from the KeyStoreService and ResourceResolver
    KeyStore aemTrustStore = keyStoreService.getTrustStore(resourceResolver);

    return aemTrustStore;
}

...
  • 将OOTB com.adobe.granite.keystore.KeyStoreService OSGi服务注入您的OSGi组件。
  • 使用KeyStoreServiceResourceResolver获取用户的AEM KeyStore,getAEMKeyStore(...)方法可做到这一点。
  • 如果API提供程序使用自签名CA证书,获取全局AEM TrustStore,则getAEMTrustStore(...)方法会执行该操作。
  • 创建SSLContextBuilder的对象,请参阅Java™ API详细信息
  • 使用loadKeyMaterial(final KeyStore keystore,final char[] keyPassword)方法将用户的AEM KeyStore加载到SSLContextBuilder中。
  • 密钥库密码是在创建密钥库时设置的密码,应存储在OSGi配置中,请参阅密码配置值

避免JVM密钥库更改

使用私有证书有效调用mTLS API的传统方法涉及修改JVM密钥库。 这是通过使用Java™ keytool命令导入私有证书来实现的。

但是,此方法不符合安全最佳实践,AEM通过使用​ 用户特定的KeyStore以及全局TrustStore ​和KeyStoreService提供了优异的选项。

解决方案包

可从此处下载视频中降级的Node.js示例项目。

AEM servlet代码在WKND站点项目的tutorial/web-api-invocation分支中可用,请参见

recommendation-more-help
c92bdb17-1e49-4e76-bcdd-89e4f85f45e6