【问题标题】:Java/REST-assured: how to use SSL certificate in codeJava/REST-assured:如何在代码中使用 SSL 证书
【发布时间】:2020-09-24 15:44:53
【问题描述】:

我正在尝试使用 REST-assured 来执行一些需要 SSL 身份验证的 API 调用。我收到了:

  • .p12 文件
  • 密码
  • .cert.pem 文件
  • .key.pem 文件

当我把所有这些都放进去时邮递员,它只是工作。现在我想在我的 Java 代码中使用它......这就是我卡住的地方。我看到人们使用单独的工具来导入密钥等,但我想在代码中做任何事情:)

我发现有人在使用它:

RestAssured.config = RestAssured.config().sslConfig(SSLConfig.sslConfig()
                .trustStore(TRUST_STORE_PATH, TRUST_STORE_PASS).trustStoreType("JKS")
                .keyStore(KEY_STORE_PATH, KEY_STORE_PASS).keystoreType("PKCS12"));

其中 KEY_STORE_* 是 P12 文件 + 密码(?),TRUST_STORE_* 是证书 + 密钥(?)。但是,这会导致错误“无效的密钥库格式”。我已经使用 openssl 将 .cert.pem 文件转换为(二进制/x509),但这并没有改变任何东西......我错过了什么?我需要调用什么黑魔法才能让它在代码中运行?


cmets 给了我一个想法;也许 .p12 文件不是“正确的”密钥库。所以:我使用 keytool 将 cert+key 转换为 JKS 信任库,并使用 OpenSSL 将 .p12 + 密码转换为 .pkcs12 密钥库。

现在的代码是:

RestAssured.config = RestAssured.config().sslConfig(SSLConfig.sslConfig()
        .trustStore(JKS_PATH, JKS_PASS).trustStoreType("JKS")
        .keyStore(PKCS12_PATH, PKCS12_PASS).keystoreType("PKCS12"));
RestAssured.useRelaxedHTTPSValidation();

我添加了useRelaxedHTTPSValiadion 调用以确保我没有遇到奇怪的签名问题;也许我可以没有它,但首先我想要这个工作。这会编译并运行——进步!但是,现在当 REST-assured 执行实际 POST 时,我遇到了一个错误:javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure。如前所述,我在 Postman 工作,证书很好;但不知何故,REST-assured/Java 表现不佳。


根据其中一个 cmets 的要求,一些 SSL 调试/日志记录:

javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.103 CEST|ServerHello.java:891|Consuming ServerHello handshake message (
"ServerHello": {
  "server version"      : "TLSv1.2",
  "random"              : <snip>,
  "session id"          : "",
  "cipher suite"        : "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F)",
  "compression methods" : "00",
  "extensions"          : [
    "renegotiation_info (65,281)": {
      "renegotiated connection": [<no renegotiated connection>]
    },
    "ec_point_formats (11)": {
      "formats": [uncompressed]
    },
    "extended_master_secret (23)": {
      <empty>
    }
  ]
}
)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.103 CEST|SSLExtensions.java:173|Ignore unavailable extension: supported_versions
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.103 CEST|ServerHello.java:987|Negotiated protocol version: TLSv1.2
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.103 CEST|SSLExtensions.java:192|Consumed extension: renegotiation_info
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:173|Ignore unavailable extension: server_name
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:173|Ignore unavailable extension: max_fragment_length
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:173|Ignore unavailable extension: status_request
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:192|Consumed extension: ec_point_formats
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:173|Ignore unavailable extension: status_request_v2
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:192|Consumed extension: extended_master_secret
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.104 CEST|SSLExtensions.java:173|Ignore unavailable extension: session_ticket
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:163|Ignore unsupported extension: supported_versions
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:163|Ignore unsupported extension: key_share
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:192|Consumed extension: renegotiation_info
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:163|Ignore unsupported extension: pre_shared_key
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|ServerHello.java:1131|Locally assigned Session Id: <snip>
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:207|Ignore unavailable extension: server_name
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:207|Ignore unavailable extension: max_fragment_length
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.105 CEST|SSLExtensions.java:207|Ignore unavailable extension: status_request
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:215|Ignore impact of unsupported extension: ec_point_formats
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: application_layer_protocol_negotiation
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: status_request_v2
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:215|Ignore impact of unsupported extension: extended_master_secret
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: session_ticket
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: supported_versions
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: key_share
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:215|Ignore impact of unsupported extension: renegotiation_info
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.106 CEST|SSLExtensions.java:207|Ignore unavailable extension: pre_shared_key
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.112 CEST|CertificateMessage.java:357|Consuming server Certificate handshake message (<snip>)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.177 CEST|CertificateRequest.java:670|Consuming CertificateRequest handshake message (
"CertificateRequest": {
  "certificate types": [rsa_sign, dss_sign, ecdsa_sign]
  "supported signature algorithms": [rsa_pkcs1_sha256, dsa_sha256, ecdsa_secp256r1_sha256, rsa_pkcs1_sha384, dsa_sha384, ecdsa_secp384r1_sha384, rsa_pkcs1_sha512, dsa_sha512, ecdsa_secp521r1_sha512, rsa_pkcs1_sha1, dsa_sha1, ecdsa_sha1]
  "certificate authorities": [<snip>]
}
)
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.179 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.179 CEST|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha256
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.180 CEST|X509Authentication.java:246|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.180 CEST|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha256
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.181 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.181 CEST|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.181 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.181 CEST|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha384
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.182 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.182 CEST|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.182 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.182 CEST|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha512
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.182 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.182 CEST|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.183 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.183 CEST|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha1
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.183 CEST|X509Authentication.java:246|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.183 CEST|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha1
javax.net.ssl|ALL|01|main|2020-09-24 09:27:51.184 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.184 CEST|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha1
javax.net.ssl|WARNING|01|main|2020-09-24 09:27:51.184 CEST|CertificateRequest.java:774|No available authentication scheme
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.184 CEST|ServerHelloDone.java:151|Consuming ServerHelloDone handshake message (
<empty>
)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.184 CEST|CertificateMessage.java:290|No X.509 certificate for client authentication, use empty Certificate message instead
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.185 CEST|CertificateMessage.java:321|Produced client Certificate handshake message (
"Certificates": <empty list>
)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.189 CEST|ECDHClientKeyExchange.java:400|Produced ECDHE ClientKeyExchange handshake message (
"ECDH ClientKeyExchange": {
  "ecdh public": {
    <snip>
  },
}
)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.196 CEST|ChangeCipherSpec.java:115|Produced ChangeCipherSpec message
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.197 CEST|Finished.java:398|Produced client Finished handshake message (
"Finished": {
  "verify data": {
    <snip>
  }'}
)
javax.net.ssl|DEBUG|01|main|2020-09-24 09:27:51.248 CEST|Alert.java:238|Received alert message (
"Alert": {
  "level"      : "fatal",
  "description": "handshake_failure"
}
)
javax.net.ssl|ERROR|01|main|2020-09-24 09:27:51.251 CEST|TransportContext.java:361|Fatal (HANDSHAKE_FAILURE): Received fatal alert: handshake_failure

我认为这是罪魁祸首,No X.509 certificate for client authentication, use empty Certificate message instead... 这看起来很奇怪。

【问题讨论】:

  • 您将无法告诉 JVM 您的密钥库类型是 JKS,然后给它一个 PEM 文件。可能您需要创建一个新的 JKS 文件,并将 cert.pem 导入其中。您可以为此使用keytool - 最新版本将读取 PEM 证书而无需进一步转换。这不是黑魔法——这只是以你所说的格式提供数据的问题;)
  • 哈,好点子!我能够创建 JKS 并将 cert.pem 导入其中。重新运行给了我同样的错误,尽管我确实想到可能需要切换“存储类型”(trustStore -> PKCS12,keyStore -> JKS)。这给了我另一个错误:DER input, Integer tag error。有什么线索吗?
  • 我猜您将加密的 PEM 加载到 JKS 文件中。但是,老实说,我需要更多地了解您的设置,才能清楚地了解发生了什么。
  • 您的评论给了我一个想法,我在原始问题中添加了我的更改。感谢您的帮助!
  • 解决了吗。我面临同样的问题。解决方案会有帮助吗?

标签: java ssl certificate rest-assured


【解决方案1】:

你对 postman 所做的事情在 java 中也是可能的。 RestAssured 已经支持密钥库文件,并且还支持不同的格式,例如 jks 和 pcsk12。在您的情况下,可以在将 p12 文件作为密钥库对象加载时使用 pcsk12 类型。但是对于其他文件,它不支持开箱即用的 pem 文件。您可以将这些文件合并到密钥库中,就像 Kevin Boone 建议的那样,请在此处查看转换文件的所有选项:Openssl cheat sheet 您还可以使用additional library - SSLContext Kickstart 来加载和创建 ssl 配置并提供它到 RestAssured,请参见下面的示例。

X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial("cert.pem", "key.pem", "password".toCharArray());

SSLFactory sslFactory = SSLFactory.builder()
        .withIdentityMaterial(keyManager)
        .withTrustMaterial("truststore.p12", "password".toCharArray(), "PKCS12")
        .build();
        
RestAssured.config().sslConfig(SSLConfig.sslConfig().sslSocketFactory(new SSLSocketFactory(sslFactory.getSslContext())));

我以 p12 作为信任材料的示例,将 pem 文件作为身份材料。

【讨论】:

  • 谢谢!我将sslcontext-kickstart-for-pem 添加到我的.pom 文件中,我确保我使用的是org.apache.http.conn.ssl.* 中的SSL 类,现在PemUtils 出现问题——loadIdentityMaterial 尝试使用证书路径(InputStream certificateStream = PemUtils.class.getClassLoader().getResourceAsStream(certificatePath)) 构造一个InputStream ,并且由于某种原因失败了。 certificateStream 为/保持为 Null。 certificatePath 类似于 src/test/resources/certs/certificate.cert.pem,该文件基于 Unix/LF,包含您的平均 BEGIN CERTIFICATE 等 ASCII 数据。
  • 看起来您正在尝试从类路径加载文件,您能否使用以下路径:certs/certificate.cert.pem 而不是 src/test/resources/certs/certificate.cert.pem
  • 是的——这似乎对证书有用!但现在我又回到javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure :( REST 保证在执行实际的 POST 请求时抛出异常。我尝试添加RestAssured.useRelaxedHTTPSValidation(),但无济于事......我也尝试使用我生成的 . pkcs12+new_password(而不是提供的 .p12+password);同样的结果。
  • 现在您可能还有其他问题。通用异常不会给你它的具体原因。您能否使用以下 VM 争论 -Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake 重新运行您的测试,并在您最初的问题中分享它的日志?握手日志会一直显示到哪一部分正确哪一部分出错
  • @Gandalf:如果服务器需要客户端身份验证,也就是客户端证书,并且您的客户端没有发送证书和身份验证,则服务器会发送类型为握手失败的致命警报,该警报会中止握手,并且当 JSSE收到致命警报它抛出一个异常,说它收到了致命警报;这就是如何。真正的问题是,为什么 JSSE 不使用您提供的证书和密钥进行身份验证?将您的证书颁发者与您截断的 CertificateRequest 消息中服务器请求的证书颁发机构进行比较;其中之一必须匹配(确切地说:所有属性,按顺序)。
猜你喜欢
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2020-03-10
  • 2018-10-03
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多