Showing posts with label security. Show all posts
Showing posts with label security. Show all posts

Thursday, 27 November 2014

Certificates with SHA-1 and SunCertPathBuilderException

As SHA-1 is heading to deprecation as hashing algoritm for certificate signatures, unpleasant effects start to appear.

Our partners we need to communicate with over HTTPS have brand new certificate signed by GoDaddy Certificate Authority. Accessing their https secured site via browser does not show anything alarming.

But accessing REST endpoint hosted on same site using java HttpUrlConnection blows up with javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

What is going on?

Every internet browser comes with quite a big set of preinstalled Certificate Authorities (CA) trusted certificates, because your browser's vendors trusts them. And because they are CAs and they are trusted, then every certificate that is signed by them is trusted too.

Same story with JVM. There is truststore file inside every JVM named cacerts. In Oracle jdk1.7.0_67 there is 87 Certificate Authorities in it as they are trused by Oracle. GoDady is there too, so why that SunCertPathBuilderException? Let's examine it more closely.

Every JVM is also shipped with command line tool named keytool. Using it you can list and also modify any keystore in jks format such as cacerts Executing... (default password is changeit)

 
keytool -list -v -keystore ${JAVA_HOME}/jre/lib/security/cacerts -storepass changeit | grep -A 14 godaddy
...will print following...
Alias name: godaddyclass2ca
Creation date: 20-Jan-2005
Entry type: trustedCertEntry

Owner: OU=Go Daddy Class 2 Certification Authority, O="The Go Daddy Group, Inc.", C=US
Issuer: OU=Go Daddy Class 2 Certification Authority, O="The Go Daddy Group, Inc.", C=US
Serial number: 0
Valid from: Tue Jun 29 18:06:20 BST 2004 until: Thu Jun 29 18:06:20 BST 2034
Certificate fingerprints:
  MD5:  91:DE:06:25:AB:DA:FD:32:17:0C:BB:25:17:2A:84:67
  SHA1: 27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4
  SHA256: C3:84:6B:F2:4B:9E:93:CA:64:27:4C:0E:C6:7C:1E:CC:5E:02:4F:FC:AC:D2:D7:40:19:35:0E:81:FE:54:6A:E4
  Signature algorithm name: SHA1withRSA
  Version: 3
Now cmparing to certificate from HTTPS website... GoDaddyG2 cerificate

There is obvious mismatch. Apart from different certificate name, validity date range also notice that Signature Algorithm is "SHA-256 with RSA". GoDaddy's certificate in JVM is different from the one in use on website, therefore SunCertPathBuilderException.

To fix this, we need to add right (G2) GoDaddy's certificate into JVM cacert keystore. Visiting GoDaddy's certificate repository obvious candidate "GoDaddy Class 2 Certification Authority Root Certificate - G2" can be found there.

wget https://certs.godaddy.com/repository/gdroot-g2.crt
keytool -printcert -file gdroot-g2.crt
Will give us something we saw in website certificate...
Owner: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Serial number: 0
Valid from: Mon Aug 31 20:00:00 EDT 2009 until: Thu Dec 31 18:59:59 EST 2037
Certificate fingerprints:
  MD5:  80:3A:BC:22:C1:E6:FB:8D:9B:3B:27:4A:32:1B:9A:01
  SHA1: 47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B
  SHA256: 45:14:0B:32:47:EB:9C:C8:C5:B4:F0:D7:B5:30:91:F7:32:92:08:9E:6E:5A:63:E2:74:9D:D3:AC:A9:19:8E:DA
  Signature algorithm name: SHA256withRSA
  Version: 3

Now just import gdroot-g2.crt into JVM cacerts truststore

sudo keytool -import -alias godaddyg2ca -file gdroot-g2.cer -keystore ${JAVA_HOME}/jre/lib/security/cacerts -storepass changeit

Owner: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Serial number: 0
Valid from: Tue Sep 01 01:00:00 BST 2009 until: Thu Dec 31 23:59:59 GMT 2037
Certificate fingerprints:
  MD5:  80:3A:BC:22:C1:E6:FB:8D:9B:3B:27:4A:32:1B:9A:01
  SHA1: 47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B
  SHA256: 45:14:0B:32:47:EB:9C:C8:C5:B4:F0:D7:B5:30:91:F7:32:92:08:9E:6E:5A:63:E2:74:9D:D3:AC:A9:19:8E:DA
  Signature algorithm name: SHA256withRSA
  Version: 3

Extensions: 

#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen:2147483647
]

#2: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  Key_CertSign
  Crl_Sign
]

#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 3A 9A 85 07 10 67 28 B6   EF F6 BD 05 41 6E 20 C1  :....g(.....An .
0010: 94 DA 0F DE                                        ....
]
]

Trust this certificate? [no]:  yes
Certificate was added to keystore

Problem solved and REST call should succeed from now using this JVM

For completeness sake, if you want to get rid of it, execute

keytool -delete -alias godaddyg2ca -keystore ${JAVA_HOME}/jre/lib/security/cacerts

What in case you are not allowed to modify JVM cacerts truststore?

Then make a copy of it, import gdroot-g2.cer into it and use this custom truststore instead of default JVM truststore using -Djavax.net.ssl.trustStore=/path/to/custom_cacerts -Djavax.net.ssl.trustStorePassword=changeit java parameters

What in case you need multiple keystores or something similarily complex?

Such scenarios cannot be solved simply by using JVM switches and parameters anymore and you have to roll your own X509TrustManager implementation. Then you need to plug it into your http client connection setup - (HttpsUrlConnection SSLSocketFactory) (Apache HttpClient 3 SecureProtocolSocketFactory) (Apache HttpClient 4 SSLConnectionSocketFactory ) (Jersey SslConfigurator)

Tuesday, 16 September 2014

Spring OAuth2RestTemplate and self-signed server certificate

It might happen to you that you ended up using spring-security-oauth2 on OAuth2 client side. Personally I would not recommend to use it as it brings much more complexity into task that is not that difficult. But every use-case is diferent.

If you also happen to integrate with site using self-signed certificate, you'll inevitably encounter following exception.

org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException: Error requesting access token.
 at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:144) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:198) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:538) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:518) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:256) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
...
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://somewhere.something.info/oauth2/token":sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:558) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:511) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:136) ~[spring-security-oauth2-2.0.2.RELEASE.jar:na]
 ... 86 common frames omitted
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.7.0_55]
 at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884) ~[na:1.7.0_55]
 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276) ~[na:1.7.0_55]
 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) ~[na:1.7.0_55]
 at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341) ~[na:1.7.0_55]
 at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153) ~[na:1.7.0_55]
 at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868) ~[na:1.7.0_55]
 at sun.security.ssl.Handshaker.process_record(Handshaker.java:804) ~[na:1.7.0_55]
 at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016) ~[na:1.7.0_55]
 at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312) ~[na:1.7.0_55]
 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339) ~[na:1.7.0_55]
 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323) ~[na:1.7.0_55]
 at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563) ~[na:1.7.0_55]
 at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) ~[na:1.7.0_55]
 at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153) ~[na:1.7.0_55]
 at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:52) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:542) ~[spring-web-4.0.5.RELEASE.jar:4.0.5.RELEASE]
 ... 88 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) ~[na:1.7.0_55]
 at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) ~[na:1.7.0_55]
 at sun.security.validator.Validator.validate(Validator.java:260) ~[na:1.7.0_55]
 at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) ~[na:1.7.0_55]
 at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231) ~[na:1.7.0_55]
 at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126) ~[na:1.7.0_55]
 at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1323) ~[na:1.7.0_55]
 ... 102 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) ~[na:1.7.0_55]
 at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) ~[na:1.7.0_55]
 at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ~[na:1.7.0_55]
 ... 108 common frames omitted

Exception is throw when https://somewhere.something.info/oauth2/token is used to trade OAuth2 code for access token. You probably know what to do. Time for stupid trick with (totaly unsecure) X509TrustManager! As OAuth2RestTemplate extends RestTemplate, it inherits public void setRequestFactory(ClientHttpRequestFactory requestFactory) method and it can be used to make the trick.

private static void disableCertificateChecks(OAuth2RestTemplate oauthTemplate) throws Exception {

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[] { new DumbX509TrustManager() }, null);
        ClientHttpRequestFactory requestFactory = new SSLContextRequestFactory(sslContext);

        //This is for OAuth protected resources
        oauthTemplate.setRequestFactory(requestFactory);
}

Code for SSLContextRequestFactory and DumbX509TrustManager is gisted here

Now, if you try to run through test once again, you will still get same SunCertPathBuilderException again. Why?

Answer is in hidden in bowels of OAuth2AccessTokenSupport, which is base class of AuthorizationCodeAccessTokenProvider. To cut the story short, it is creating it's own RestTemplates for token endpoint operations.

Luckily again, overridable setRequestFactory(...) is also provided on OAuth2AccessTokenSupport. Repeating same trick again finaly gives us working, exception free solution:

    private static void disableCertificateChecks(OAuth2RestTemplate oauthTemplate) throws Exception {

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[] { new NastyX509TrustManager() }, null);
        ClientHttpRequestFactory requestFactory = new SSLContextRequestFactory(sslContext);

        //This is for OAuth protected resources
        oauthTemplate.setRequestFactory(requestFactory);

        //AuthorizationCodeAccessTokenProvider creates it's own RestTemplate for token operations
        AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
        provider.setRequestFactory(requestFactory);
        oauthTemplate.setAccessTokenProvider(provider);
    }

Remember, you've just created huge security hole in your application. Make doubly sure it is never used in production

Happy unsecure https REST OAuth2 calls!

PS: if you favour more advanced http transport layer then basic Java HttpURLConnection (and you should on server side) like HttpComponents 4.3, then simplest possible ClientHttpRequestFactory creation would be:

        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(sslContext).build();
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);

Monday, 24 March 2014

Spring Security with multiple AuthenticationManagers

Spring security (Acegi security once) configuration was for a long time quite exhausting task. Gluing together all filters, entry points, success handlers and authentication providers was not small price to pay for overwhelming flexibility of this awesome framework.

Starting with version 2.0, simplified namespace configuration (<sec:http/>) was introduced, allowing most common setups to be configured with just a few lines. Not much get changed with version 3.0.

However, this new configuration style introduced also one important limitation - only single one security filter chain with single AuthenticationManager can be configured using it. When you happen to have web application with two faces - Web Pages and REST endpoints, having quite different authentication requirements, you are in trouble. Only option is fallback to traditional bean by bean configuration.

Following gist shows how it can be done

What a massive piece of xml!!!

Limitation of single security filter chain was removed in version 3.1, which allowed to have multiple <http authentication-manager-ref="..."/> elements, each possibly with different AuthenticationManager.

Latest and greatest version 3.2 brought long time awaited Java Configuration, with sweet @EnableWebSecurity and WebSecurityConfigurerAdapter combo. To do not repeat same mistake again, this funny trick can be used to define multiple filter chains and AutheticationManagers.

But as an exercise, I also tried to disassemble configuration into old school bean by bean way. Let's call that poor man's spring security java config.

Following gist shows how it can be done

While this is not as complex setup as xml based example before, it is still big chunk of code.

Happy Spring securing!