Java trustmanager behavior on expired certificates

2019-02-04 19:44发布

问题:

Does java's TrustManager implementation ignore if a certificate has expired?
I tried the following:
- Using keytool and parameter -startdate "1970/01/01 00:00:00" I created a P12 keystore with an expired certificate.
- I exported the certificate:

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: fake
Creation date: 5 ╠ά± 2011
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
Issuer: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
Serial number: -1c20
Valid from: Thu Jan 01 00:00:00 EET 1970 until: Fri Jan 02 00:00:00 EET 1970
Certificate fingerprints:
         MD5:  A9:BE:3A:3D:45:24:1B:4F:3C:9B:2E:02:E3:57:86:11
         SHA1: 21:9D:E1:04:09:CF:10:58:73:C4:62:3C:46:4C:76:A3:81:56:88:4D
         Signature algorithm name: SHA1withRSA
         Version: 3


*******************************************

I used this certificate as server certificate for Tomcat.
Then using an apache httpClient I connected to tomcat, but first I added the expired certificate to the client's trust-store (using a TrustManager

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

and loading the expired certificate).
I was expecting the connection to fail.
Instead the connection succeeds.
Using System.setProperty("javax.net.debug", "ssl");
I see:

***
Found trusted certificate:
[
[
  Version: V3
  Subject: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 10350555024148635338735220482157687267055139906998169922552357357346372886164908067983097037540922519808845662295379579697361784480052371935565129553860304254832565723373586277732296157572040989796830623403187557540749531267846797324326299709274902019299
  public exponent: 65537
  Validity: [From: Thu Jan 01 00:00:00 EET 1970,
               To: Fri Jan 02 00:00:00 EET 1970]
  Issuer: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
  SerialNumber: [   -1c20]

]

I see that in TLS handshake the expired certificate is send by Tomcat connector.
But the client (i.e. the TrustManager) does not reject the connection.
Is this the default behavior?
Am I suppose to configure the trustmanager somehow to check for expiration?

UPDATE:
I found that the actual TrustManager used is X509TrustManagerImpl. Here X509TrustManagerImpl says that this class has a minimal logic.May be I am using the wrong TrustManager?

UPDATE2: From the javadoc X509TrustManager it is not clear if it checks for certificate expiration

void checkServerTrusted(X509Certificate[] chain,String authType)
                                throws CertificateException  

Given the partial or complete certificate chain provided by the peer, build a certificate path to a trusted root and return if it can be validated and is trusted for server SSL authentication based on the authentication type.The authentication type is the key exchange algorithm portion of the cipher suites represented as a String, such as "RSA", "DHE_DSS". Note: for some exportable cipher suites, the key exchange algorithm is determined at run time during the handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, the authType should be RSA_EXPORT when an ephemeral RSA key is used for the key exchange, and RSA when the key from the server certificate is used. Checking is case-sensitive.

Thanks

回答1:

I did not try your example, but I now I regularly have to regenerate my server certificates (for our development server) since their certificates have quite short validity times.

In our case the client does not have the server certificates themselves in the truststore, but only the certificate of our CA (with longer validity), and when the client tries to connect to the server, both sides get a SSLException (which may be wrapped in another exception in your case).

I guess that the trust manager assumes something like "if you give me expired certificates to trust in, I'll do it". Try our approach instead (it also saves you to update the client each time the server certificate expires).



回答2:

I've just had a similar issue myself while overriding checkServerTrusted.

Turns out that if you need to check expiration you can call X509Certificate.checkValidity() and it will throw either a CertificateExpiredException or a CertificateNotYetValidException. Both of these extend CertificateException so they can be happily thrown by checkServerTrusted.

To solve your problem you could implement a new X509TrustManager which creates your original instance in its constructor, implements all methods as calls to the original instance, and adds a call to checkValidity for each certificate in certs[] inside checkServerTrusted.



回答3:

I believe IBM's JSSE checks for expiry while Sun's does not.