来自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
证书交换
如果对AEM证书使用自签名CA(如上所述),请将证书或内部证书颁发机构(CA)证书发送到API提供程序。
此外,如果API提供商使用自签名CA证书,则从API提供商接收证书或内部证书颁发机构(CA)证书。
证书导入
要导入AEM证书,请执行以下步骤:
-
以 管理员 身份登录到 AEM作者。
-
导航到 AEM Author > Tools > Security > Users > Create or Select an existing user。
为演示目的,创建了名为
mtl-demo-user
的新用户。 -
要打开 用户属性,请单击用户名。
-
单击 密钥库 选项卡,然后单击 创建密钥库 按钮。 然后在 设置KeyStore访问密码 对话框中,为此用户的密钥库设置密码,然后单击“保存”。
-
在新屏幕的 从DER文件添加私钥 部分下,执行以下步骤:
-
输入别名
-
以DER格式导入上面生成的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组件。 - 使用
KeyStoreService
和ResourceResolver
获取用户的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
分支中可用,请参见。