0

I'm trying to use REST-assured to do some API calls for which SSL authentication is required. I have received:

  • .p12 file
  • password
  • .cert.pem file
  • .key.pem file

When I put all this in e.g. Postman, it just works. Now I want to use this in my Java code... And that's where I get stuck. I see people using separate tooling to import keys etc, but I want to do eveything in code :)

I have found people using this:

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"));

Where KEY_STORE_* is the P12-file + password(?), and TRUST_STORE_* is cert + key(?). However, this results in an error, "Invalid keystore format". I have converted the .cert.pem file to a (binary/x509) with openssl, but that doesn't change anything... What am I missing? What dark magic do I need to invoke to get this running in code?


The comments gave me an idea; maybe the .p12 file wasn't a "proper" keystore. So: I used keytool to convert the cert+key to a JKS trust store, and I used OpenSSL to convert the .p12 + password into a .pkcs12 key store.

The code is now:

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

I added the useRelaxedHTTPSValiadion call to make sure I'm not running into weird signature issues; maybe I can do without it, but first I want this working. This compiles and runs -- progress! However, now I'm confronted with an error when REST-assured executes the actual POST: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure. As mentioned, I got this working in Postman, the certificates are fine; yet somehow REST-assured/Java isn't playing nice.


As requested in one of the comments, some SSL debug/logging:

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

I think this is the culprit, No X.509 certificate for client authentication, use empty Certificate message instead... This seems weird.

Gandalf
  • 11
  • 1
  • 4
  • You won't be able to tell the JVM that your keystore type is JKS, and then give it a PEM file. Probably you'll need to create a new JKS file, and import `cert.pem` into it. You can use `keytool` for this -- recent versions will read PEM certificates without further conversion. It's not dark magic -- it's just a matter of providing data in the format you say you're providing it in ;) – Kevin Boone Sep 22 '20 at 10:53
  • Ha, good point! I was able to create the JKS and import `cert.pem` into it. Rerunning gave me the same error, although it did occur to me that the 'store types' may need to be switched (trustStore -> PKCS12, keyStore -> JKS). This gave me another error: `DER input, Integer tag error`. Any clue what's going on? – Gandalf Sep 22 '20 at 11:51
  • I'd guess that you loaded an encrypted PEM into a JKS file. But, honestly, I'd need to know a whole lot more about your set-up to have a clear idea what's going on. – Kevin Boone Sep 22 '20 at 12:02
  • Your comment gave me an idea, and I added my changes to the original question. Thanks for your help! – Gandalf Sep 22 '20 at 14:26
  • Is it resolved. I am facing the same issue. Solution would be helpful? – ChanGan Jan 07 '21 at 15:11
  • Nope, sorry... No matter what I tried, I couldn't get it to run. – Gandalf Jan 09 '21 at 11:32

1 Answers1

0

What you did with postman is also possible within java. RestAssured has already support for keystore files and also does support different formats such as jks and pcsk12. In your case the type pcsk12 can be used when loading the p12 file as a keystore object. However for the other files it doesn't support out of the box pem files. You can either merge those files into a keystore, just like how Kevin Boone suggested, please see here for all the options for converting your files: Openssl cheat sheet You can also use an additional library - SSLContext Kickstart for loading and creating the ssl configuration and supply it to RestAssured, see below for an example.

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())));

I took the p12 as an example trust material and the pem files as a identity material.

Hakan54
  • 3,121
  • 1
  • 23
  • 37
  • Thanks! I added `sslcontext-kickstart-for-pem` to my .pom file, I made sure I'm using the SSL classes from `org.apache.http.conn.ssl.*`, and now things break in PemUtils -- loadIdentityMaterial tries to contstruct an InputStream using the the certificate path (`InputStream certificateStream = PemUtils.class.getClassLoader().getResourceAsStream(certificatePath)`), and it fails for some reason. certificateStream is/stays Null. certificatePath is something like `src/test/resources/certs/certificate.cert.pem`, and the file is Unix/LF-based, containing your average BEGIN CERTIFICATE etc ASCII data. – Gandalf Sep 23 '20 at 09:52
  • It looks like you are trying to load the file from the classpath, could you the following path: `certs/certificate.cert.pem` instead of `src/test/resources/certs/certificate.cert.pem` – Hakan54 Sep 23 '20 at 10:41
  • Yeah -- that seems to do the trick for the certificates! But now I'm back to `javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure` again :( The exception is thrown by REST-assured, upon executing the actual POST request. I tried adding `RestAssured.useRelaxedHTTPSValidation()`, to no avail... I also tried to use my generated .pkcs12+new_password (instead of shipped .p12+password); same result. – Gandalf Sep 23 '20 at 13:21
  • Now you have probably some other issue. The generic exception won't give you the specific cause of it. Could you re-run your test with the following VM arguement `-Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake` and share the logs of it within your initial question? The handshake log will display up till which part went right and which part went wrong – Hakan54 Sep 23 '20 at 13:24
  • Added requested info -- I saw `No X.509 certificate for client authentication, use empty Certificate message instead` in the logs. That seems to have triggered the exception, but... how? – Gandalf Sep 24 '20 at 07:46
  • I also added the p12 certificate to the JKS... But again, same error :( – Gandalf Sep 24 '20 at 08:00
  • Could you try the answer from this page: https://stackoverflow.com/questions/59894129/sending-client-certificates-on-custom-closablehttpclient-for-zuul It looks like this person has the same issue regarding the handshake proces – Hakan54 Sep 24 '20 at 10:15
  • 1
    @Gandalf: if the server requires client auth aka client cert, and your client doesn't send a cert and auth, the server sends a fatal alert of type handshake_failure which aborts the handshake, and when JSSE receives a fatal alert it throws an exception saying that it received a fatal alert; that's how. The real question is, why didn't JSSE _do_ auth with the cert&key you provided? Compare the issuer of your cert to the certificate authorities requested by the server in the CertificateRequest message, which you snipped; one of them must match (exactly: all attributes, in order). – dave_thompson_085 Sep 24 '20 at 10:53