From 65dc3daaf8b3b9efcd384182ccfa82983e34c7a2 Mon Sep 17 00:00:00 2001 From: Mike Derryberry Date: Tue, 12 Jun 2012 16:09:01 -0400 Subject: [PATCH] smart client --- openid-connect-client/.classpath | 4 +- .../org.eclipse.wst.common.component | 4 +- openid-connect-client/.springBeans | 14 ++ .../AbstractOIDCAuthenticationFilter.java | 159 +++++++----------- .../client/OIDCAuthenticationFilter.java | 11 +- .../OIDCAuthenticationUsingChooserFilter.java | 13 +- .../client/OIDCServerConfiguration.java | 27 ++- .../connect/client/OIDCUserDetailService.java | 39 +++++ .../client/OIDCServerConfigurationTest.java | 10 ++ .../src/test/resources/jwk/jwk | 8 + .../src/test/resources/jwk/jwkEncrypted | 8 + .../src/test/resources/x509/x509 | 15 ++ .../src/test/resources/x509/x509Encrypted | 15 ++ openid-connect-common/.classpath | 5 +- .../org.eclipse.wst.common.component | 1 - .../java/org/mitre/jwt/model/JwtClaims.java | 5 - .../JwtSigningAndValidationService.java | 34 +++- ...bstractJwtSigningAndValidationService.java | 31 ++++ ...DynamicJwtSigningAndValidationService.java | 37 +++- ...JwtSigningAndValidationServiceDefault.java | 33 +++- .../token/ConnectAuthCodeTokenGranter.java | 7 +- .../openid/connect/web/CheckIDEndpoint.java | 4 +- .../src/test/java/org/mitre/jwt/JwtTest.java | 3 +- 23 files changed, 359 insertions(+), 128 deletions(-) create mode 100644 openid-connect-client/.springBeans create mode 100644 openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCUserDetailService.java create mode 100644 openid-connect-client/src/test/resources/jwk/jwk create mode 100644 openid-connect-client/src/test/resources/jwk/jwkEncrypted create mode 100644 openid-connect-client/src/test/resources/x509/x509 create mode 100644 openid-connect-client/src/test/resources/x509/x509Encrypted diff --git a/openid-connect-client/.classpath b/openid-connect-client/.classpath index 40f68d2dc..57073c1a5 100644 --- a/openid-connect-client/.classpath +++ b/openid-connect-client/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/openid-connect-client/.settings/org.eclipse.wst.common.component b/openid-connect-client/.settings/org.eclipse.wst.common.component index e70fe2d47..6227d3014 100755 --- a/openid-connect-client/.settings/org.eclipse.wst.common.component +++ b/openid-connect-client/.settings/org.eclipse.wst.common.component @@ -1,8 +1,8 @@ - - + + diff --git a/openid-connect-client/.springBeans b/openid-connect-client/.springBeans new file mode 100644 index 000000000..707e4bb7c --- /dev/null +++ b/openid-connect-client/.springBeans @@ -0,0 +1,14 @@ + + + 1 + + + + + + + spring-servlet.xml + + + + diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java index 49c775859..dca5b1724 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java @@ -28,6 +28,7 @@ import java.security.SecureRandom; import java.security.Signature; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -42,9 +43,16 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.mitre.openid.connect.model.IdToken; +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtHeader; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.signer.AbstractJwtSigner; +import org.mitre.jwt.signer.impl.HmacSigner; +import org.mitre.jwt.signer.service.impl.DynamicJwtSigningAndValidationService; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; @@ -60,6 +68,7 @@ import org.springframework.web.util.WebUtils; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** @@ -376,11 +385,12 @@ public class AbstractOIDCAuthenticationFilter extends * authentication * @return The authenticated user token, or null if authentication is * incomplete. + * @throws Exception * @throws UnsupportedEncodingException */ protected Authentication handleAuthorizationGrantResponse( String authorizationGrant, HttpServletRequest request, - OIDCServerConfiguration serverConfig) { + OIDCServerConfiguration serverConfig) throws Exception { final boolean debug = logger.isDebugEnabled(); @@ -457,42 +467,45 @@ public class AbstractOIDCAuthenticationFilter extends // Extract the id_token to insert into the // OpenIdConnectAuthenticationToken - + + Cookie nonceSignatureCookie = WebUtils.getCookie(request, + NONCE_SIGNATURE_COOKIE_NAME); + IdToken idToken = null; - + DynamicJwtSigningAndValidationService dynamic = new DynamicJwtSigningAndValidationService(null, null, null); + OIDCServerConfiguration oidc = new OIDCServerConfiguration(); + if (jsonRoot.getAsJsonObject().get("id_token") != null) { - try { - idToken = IdToken.parse(jsonRoot.getAsJsonObject() - .get("id_token").getAsString()); + if(dynamic.validateSignature(jsonRoot.getAsJsonObject().get("id_token").getAsString()) + && + dynamic.validateIssuedJwt(idToken, oidc.getIssuer()) + && + dynamic.validateAudience(idToken, oidc.getClientId()) + && + dynamic.isJwtExpired(idToken) + && + dynamic.validateIssuedAt(idToken) + && + dynamic.validateNonce(idToken, nonceSignatureCookie.getValue())){ + + try { + idToken = IdToken.parse(jsonRoot.getAsJsonObject().get("id_token").getAsString()); + + } catch (Exception e) { - List parts = Lists.newArrayList(Splitter.on(".") - .split(jsonRoot.getAsJsonObject().get("id_token") - .getAsString())); + // I suspect this could happen - if (parts.size() != 3) { - throw new IllegalArgumentException( - "Invalid JWT format."); + logger.error("Problem parsing id_token: " + e); + // e.printStackTrace(); + + throw new AuthenticationServiceException( + "Problem parsing id_token return from Token endpoint: " + + e); } - - String h64 = parts.get(0); - String c64 = parts.get(1); - String s64 = parts.get(2); - - logger.debug("h64 = " + h64); - logger.debug("c64 = " + c64); - logger.debug("s64 = " + s64); - - } catch (Exception e) { - - // I suspect this could happen - - logger.error("Problem parsing id_token: " + e); - // e.printStackTrace(); - - throw new AuthenticationServiceException( - "Problem parsing id_token return from Token endpoint: " - + e); + } + else{ + throw new AuthenticationServiceException("Problem verifying id_token"); } } else { @@ -505,100 +518,58 @@ public class AbstractOIDCAuthenticationFilter extends "Token Endpoint did not return a token_id"); } - // Handle Check ID Endpoint interaction + - httpClient = new DefaultHttpClient(); - httpClient.getParams().setParameter("http.socket.timeout", - new Integer(httpSocketTimeout)); - - factory = new HttpComponentsClientHttpRequestFactory(httpClient); - restTemplate = new RestTemplate(factory); - - form = new LinkedMultiValueMap(); - - form.add("access_token", jsonRoot.getAsJsonObject().get("id_token") - .getAsString()); - - jsonString = null; - - try { - jsonString = restTemplate.postForObject( - serverConfig.getCheckIDEndpointURI(), form, - String.class); - } catch (HttpClientErrorException httpClientErrorException) { - - // Handle error - - logger.error("Check ID Endpoint error response: " - + httpClientErrorException.getStatusText() + " : " - + httpClientErrorException.getMessage()); - - throw new AuthenticationServiceException("Unable check token."); - } - - jsonRoot = new JsonParser().parse(jsonString); // String iss = jsonRoot.getAsJsonObject().get("iss") // .getAsString(); - String userId = jsonRoot.getAsJsonObject().get("user_id") - .getAsString(); + //String userId = jsonRoot.getAsJsonObject().get("user_id") + // .getAsString(); // String aud = jsonRoot.getAsJsonObject().get("aud") // .getAsString(); - String nonce = jsonRoot.getAsJsonObject().get("nonce") - .getAsString(); + //String nonce = jsonRoot.getAsJsonObject().get("nonce") + // .getAsString(); // String exp = jsonRoot.getAsJsonObject().get("exp") // .getAsString(); // Compare returned ID Token to signed session cookie // to detect ID Token replay by third parties. - Cookie nonceSignatureCookie = WebUtils.getCookie(request, - NONCE_SIGNATURE_COOKIE_NAME); - if (nonceSignatureCookie != null) { - String sigText = nonceSignatureCookie.getValue(); + String sigText = nonceSignatureCookie.getValue(); - if (sigText != null && !sigText.isEmpty()) { + if (sigText != null && !sigText.isEmpty()) { - if (!verify(signer, publicKey, nonce, sigText)) { - logger.error("Possible replay attack detected! " - + "The comparison of the nonce in the returned " - + "ID Token to the signed session " - + NONCE_SIGNATURE_COOKIE_NAME + " failed."); - - throw new AuthenticationServiceException( - "Possible replay attack detected! " - + "The comparison of the nonce in the returned " - + "ID Token to the signed session " - + NONCE_SIGNATURE_COOKIE_NAME - + " failed."); - } - - } else { - logger.error(NONCE_SIGNATURE_COOKIE_NAME - + " was found, but was null or empty."); + if (!verify(signer, publicKey, idToken.getClaims().getNonce(), sigText)) { + logger.error("Possible replay attack detected! " + + "The comparison of the nonce in the returned " + + "ID Token to the signed session " + + NONCE_SIGNATURE_COOKIE_NAME + " failed."); throw new AuthenticationServiceException( - NONCE_SIGNATURE_COOKIE_NAME - + " was found, but was null or empty."); + "Possible replay attack detected! " + + "The comparison of the nonce in the returned " + + "ID Token to the signed session " + + NONCE_SIGNATURE_COOKIE_NAME + + " failed."); } } else { - logger.error(NONCE_SIGNATURE_COOKIE_NAME - + " cookie was not found."); + + " was found, but was null or empty."); throw new AuthenticationServiceException( - NONCE_SIGNATURE_COOKIE_NAME + " cookie was not found."); + NONCE_SIGNATURE_COOKIE_NAME + + " was found, but was null or empty."); } // Create an Authentication object for the token, and // return. OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken( - userId, idToken); + idToken.getTokenClaims().getUserId(), idToken); Authentication authentication = this.getAuthenticationManager() .authenticate(token); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index 4ec398cbd..2797c7ff1 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -97,9 +97,14 @@ public class OIDCAuthenticationFilter extends AbstractOIDCAuthenticationFilter { } else if (StringUtils.isNotBlank(request.getParameter("code"))) { - return handleAuthorizationGrantResponse( - request.getParameter("code"), new SanatizedRequest(request, - new String[] { "code" }), oidcServerConfig); + try { + return handleAuthorizationGrantResponse( + request.getParameter("code"), new SanatizedRequest(request, + new String[] { "code" }), oidcServerConfig); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } else { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java index 9a420dc65..ccfc6dd93 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java @@ -108,10 +108,15 @@ public class OIDCAuthenticationUsingChooserFilter extends Cookie issuerCookie = WebUtils.getCookie(request, ISSUER_COOKIE_NAME); - return handleAuthorizationGrantResponse( - request.getParameter("code"), new SanatizedRequest(request, - new String[] { "code" }), - oidcServerConfigs.get(issuerCookie.getValue())); + try { + return handleAuthorizationGrantResponse( + request.getParameter("code"), new SanatizedRequest(request, + new String[] { "code" }), + oidcServerConfigs.get(issuerCookie.getValue())); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } else { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCServerConfiguration.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCServerConfiguration.java index 2d0d226b2..1aeb6a23c 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCServerConfiguration.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCServerConfiguration.java @@ -16,19 +16,19 @@ package org.mitre.openid.connect.client; import java.io.File; -import java.net.MalformedURLException; import java.net.URL; import java.security.Key; +import org.mitre.jwt.signer.service.impl.DynamicJwtSigningAndValidationService; import org.mitre.util.Utility; -import com.google.gson.JsonObject; - /** * @author nemonik * */ public class OIDCServerConfiguration { + + DynamicJwtSigningAndValidationService dynamic; private String authorizationEndpointURI; @@ -40,6 +40,8 @@ public class OIDCServerConfiguration { private String clientId; + private String issuer; + private String x509EncryptUrl; private String x509SigningUrl; @@ -63,6 +65,10 @@ public class OIDCServerConfiguration { public String getClientId() { return clientId; } + + public String getIssuer() { + return issuer; + } public String getClientSecret() { return clientSecret; @@ -84,6 +90,10 @@ public class OIDCServerConfiguration { this.clientId = clientId; } + public void setIssuer(String issuer) { + this.issuer = issuer; + } + public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } @@ -169,11 +179,18 @@ public class OIDCServerConfiguration { + authorizationEndpointURI + ", tokenEndpointURI=" + tokenEndpointURI + ", checkIDEndpointURI=" + checkIDEndpointURI + ", clientSecret=" + clientSecret - + ", clientId=" + clientId + ", x509EncryptedUrl=" + + ", clientId=" + clientId + ", issuer=" + issuer + +", x509EncryptedUrl=" + x509EncryptUrl + ", jwkEncryptedUrl=" + jwkEncryptUrl + ", x509SigningUrl=" + x509SigningUrl + ", jwkSigningUrl=" + jwkSigningUrl + "]"; } + + public DynamicJwtSigningAndValidationService getDynamic() throws Exception{ + dynamic = new DynamicJwtSigningAndValidationService(getX509SigningUrl(), getJwkSigningUrl(), getClientSecret()); + return dynamic; + } -} + +} \ No newline at end of file diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCUserDetailService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCUserDetailService.java new file mode 100644 index 000000000..64a0980b1 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCUserDetailService.java @@ -0,0 +1,39 @@ +package org.mitre.openid.connect.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; + +import org.mitre.openid.connect.model.IdToken; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public class OIDCUserDetailService implements UserDetailsService, + AuthenticationUserDetailsService { + + public IdToken retrieveToken(URL url) throws IOException{ + String str = new BufferedReader(new InputStreamReader(url.openStream())).toString(); + IdToken idToken = IdToken.parse(str); + return idToken; + } + + + @Override + public UserDetails loadUserDetails(OpenIdConnectAuthenticationToken token) + throws UsernameNotFoundException { + // TODO Auto-generated method stub + + return null; + } + + @Override + public UserDetails loadUserByUsername(String username) + throws UsernameNotFoundException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/OIDCServerConfigurationTest.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/OIDCServerConfigurationTest.java index 67456b7a1..e3ce69fbe 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/OIDCServerConfigurationTest.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/OIDCServerConfigurationTest.java @@ -101,4 +101,14 @@ public class OIDCServerConfigurationTest extends TestCase { Key key3 = oidc.getEncryptionKey(); assertEquals(key3, null); } + + @Test + public void testGetDynamic() throws Exception { + oidc.setX509SigningUrl(x509Url.getPath()); + oidc.setJwkSigningUrl(jwkUrl.getPath()); + oidc.setClientSecret("foo"); + assertEquals(oidc.getDynamic().getSigningX509Url(), x509Url.getPath()); + assertEquals(oidc.getDynamic().getSigningJwkUrl(), jwkUrl.getPath()); + assertEquals(oidc.getDynamic().getClientSecret(), "foo"); + } } diff --git a/openid-connect-client/src/test/resources/jwk/jwk b/openid-connect-client/src/test/resources/jwk/jwk new file mode 100644 index 000000000..a5f42177d --- /dev/null +++ b/openid-connect-client/src/test/resources/jwk/jwk @@ -0,0 +1,8 @@ + {"jwk": + [ + {"alg":"RSA", + "mod": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "exp":"AQAB", + "kid":"2011-04-29"} + ] + } \ No newline at end of file diff --git a/openid-connect-client/src/test/resources/jwk/jwkEncrypted b/openid-connect-client/src/test/resources/jwk/jwkEncrypted new file mode 100644 index 000000000..a5f42177d --- /dev/null +++ b/openid-connect-client/src/test/resources/jwk/jwkEncrypted @@ -0,0 +1,8 @@ + {"jwk": + [ + {"alg":"RSA", + "mod": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "exp":"AQAB", + "kid":"2011-04-29"} + ] + } \ No newline at end of file diff --git a/openid-connect-client/src/test/resources/x509/x509 b/openid-connect-client/src/test/resources/x509/x509 new file mode 100644 index 000000000..2d60d2c3e --- /dev/null +++ b/openid-connect-client/src/test/resources/x509/x509 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU +ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh +dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV +BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0 +NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo +BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt +YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz +LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ +Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2 +Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq +hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8 +6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg +oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/openid-connect-client/src/test/resources/x509/x509Encrypted b/openid-connect-client/src/test/resources/x509/x509Encrypted new file mode 100644 index 000000000..2d60d2c3e --- /dev/null +++ b/openid-connect-client/src/test/resources/x509/x509Encrypted @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU +ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh +dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV +BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0 +NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo +BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt +YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz +LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ +Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2 +Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq +hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8 +6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg +oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/openid-connect-common/.classpath b/openid-connect-common/.classpath index 5d8133f83..191f61b4a 100644 --- a/openid-connect-common/.classpath +++ b/openid-connect-common/.classpath @@ -1,8 +1,8 @@ - - + + @@ -11,4 +11,3 @@ - diff --git a/openid-connect-common/.settings/org.eclipse.wst.common.component b/openid-connect-common/.settings/org.eclipse.wst.common.component index c681a1288..1e24c2b7c 100644 --- a/openid-connect-common/.settings/org.eclipse.wst.common.component +++ b/openid-connect-common/.settings/org.eclipse.wst.common.component @@ -2,6 +2,5 @@ - diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtClaims.java b/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtClaims.java index 1831edf10..bc6bc9aca 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtClaims.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtClaims.java @@ -15,12 +15,7 @@ ******************************************************************************/ package org.mitre.jwt.model; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; -import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; import com.google.gson.JsonElement; diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java index 4f5ffbde7..0cb60026f 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java @@ -17,7 +17,6 @@ package org.mitre.jwt.signer.service; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.util.List; import java.util.Map; import org.mitre.jwt.model.Jwt; @@ -72,6 +71,39 @@ public interface JwtSigningAndValidationService { * @return the signed jwt * @throws NoSuchAlgorithmException */ + public boolean validateIssuedAt(Jwt jwt); + + /** + * Checks to see when this JWT was issued + * + * @param jwt + * the JWT to check + * @return true if the issued at is valid, false if not + * @throws NoSuchAlgorithmException + */ + public boolean validateAudience(Jwt jwt, String clientId); + + /** + * Checks the audience that the given JWT against the client_id of the Client + * + * @param jwt + * @param clientId + * the string representation of the client_id + * @return true if the audience matches the clinet_id, false if otherwise + * @throws NoSuchAlgorithmException + */ + public boolean validateNonce(Jwt jwt, String nonce); + + /** + * Checks to see if the nonce parameter sent in the Authorization Request + * is equal to the nonce parameter in the id token + * + * @param jwt + * @param nonce + * the string representation of the Nonce + * @return true if both nonce parameters are equal, false if otherwise + * @throws NoSuchAlgorithmException + */ public void signJwt(Jwt jwt) throws NoSuchAlgorithmException; /** diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/AbstractJwtSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/AbstractJwtSigningAndValidationService.java index ed4d99216..8d3a8d437 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/AbstractJwtSigningAndValidationService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/AbstractJwtSigningAndValidationService.java @@ -50,5 +50,36 @@ public abstract class AbstractJwtSigningAndValidationService implements JwtSigni return false; } + + @Override + public boolean validateIssuedAt(Jwt jwt) { + Date issuedAt = jwt.getClaims().getIssuedAt(); + + if (issuedAt != null) + return new Date().before(issuedAt); + else + return false; + } + + @Override + public boolean validateAudience(Jwt jwt, String clientId) { + + if(jwt.getClaims().getAudience().equals(clientId)){ + return true; + } + else{ + return false; + } + } + + @Override + public boolean validateNonce(Jwt jwt, String nonce) { + if(jwt.getClaims().getNonce().equals(nonce)){ + return true; + } + else{ + return false; + } + } } \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationService.java index efcd571ac..cf337051e 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationService.java @@ -5,6 +5,7 @@ import java.net.URL; import java.security.Key; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -33,9 +34,6 @@ public class DynamicJwtSigningAndValidationService extends AbstractJwtSigningAnd private Map signers; - private String signingAlgorithm; - - public DynamicJwtSigningAndValidationService(String x509SigningUrl, String jwkSigningUrl, String clientSecret) throws Exception { setX509SigningUrl(x509SigningUrl); setJwkSigningUrl(jwkSigningUrl); @@ -143,4 +141,35 @@ public class DynamicJwtSigningAndValidationService extends AbstractJwtSigningAnd return signer; } -} + + @Override + public boolean validateIssuedAt(Jwt jwt) { + Date issuedAt = jwt.getClaims().getIssuedAt(); + + if (issuedAt != null) + return new Date().before(issuedAt); + else + return false; + } + + @Override + public boolean validateAudience(Jwt jwt, String clientId) { + + if(jwt.getClaims().getAudience().equals(clientId)){ + return true; + } + else{ + return false; + } + } + + @Override + public boolean validateNonce(Jwt jwt, String nonce) { + if(jwt.getClaims().getNonce().equals(nonce)){ + return true; + } + else{ + return false; + } + } +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JwtSigningAndValidationServiceDefault.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JwtSigningAndValidationServiceDefault.java index 8d4ddd162..77eb8cd83 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JwtSigningAndValidationServiceDefault.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JwtSigningAndValidationServiceDefault.java @@ -17,7 +17,7 @@ package org.mitre.jwt.signer.service.impl; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.security.interfaces.RSAPublicKey; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -163,4 +163,35 @@ public class JwtSigningAndValidationServiceDefault extends AbstractJwtSigningAnd public Map getSigners() { return signers; } + + @Override + public boolean validateIssuedAt(Jwt jwt) { + Date issuedAt = jwt.getClaims().getIssuedAt(); + + if (issuedAt != null) + return new Date().before(issuedAt); + else + return false; + } + + @Override + public boolean validateAudience(Jwt jwt, String clientId) { + + if(clientId.equals(jwt.getClaims().getAudience())){ + return true; + } + else{ + return false; + } + } + + @Override + public boolean validateNonce(Jwt jwt, String nonce) { + if(nonce.equals(jwt.getClaims().getNonce())){ + return true; + } + else{ + return false; + } + } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectAuthCodeTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectAuthCodeTokenGranter.java index f3b56d6bb..3615953ed 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectAuthCodeTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectAuthCodeTokenGranter.java @@ -18,6 +18,7 @@ */ package org.mitre.openid.connect.token; +import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.Map; import java.util.Set; @@ -30,6 +31,7 @@ import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.model.IdToken; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; @@ -112,10 +114,13 @@ public class ConnectAuthCodeTokenGranter implements TokenGranter { * @param parameters * @param clientId * @param scope + * @throws NoSuchAlgorithmException + * @throws AuthenticationException + * @throws InvalidGrantException */ @Override public OAuth2AccessToken grant(String grantType, - Map parameters, String clientId, Set scope) { + Map parameters, String clientId, Set scope) throws NoSuchAlgorithmException, InvalidGrantException, AuthenticationException { if (!GRANT_TYPE.equals(grantType)) { return null; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java index aa859b3cc..a7ef053db 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java @@ -15,6 +15,8 @@ ******************************************************************************/ package org.mitre.openid.connect.web; +import java.security.NoSuchAlgorithmException; + import javax.servlet.http.HttpServletRequest; import org.mitre.jwt.signer.service.JwtSigningAndValidationService; @@ -44,7 +46,7 @@ public class CheckIDEndpoint { @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping("/checkid") - public ModelAndView checkID(@RequestParam("access_token") String tokenString, ModelAndView mav, HttpServletRequest request) { + public ModelAndView checkID(@RequestParam("access_token") String tokenString, ModelAndView mav, HttpServletRequest request) throws NoSuchAlgorithmException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); diff --git a/openid-connect-server/src/test/java/org/mitre/jwt/JwtTest.java b/openid-connect-server/src/test/java/org/mitre/jwt/JwtTest.java index f565fbd33..caefa4104 100644 --- a/openid-connect-server/src/test/java/org/mitre/jwt/JwtTest.java +++ b/openid-connect-server/src/test/java/org/mitre/jwt/JwtTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertThat; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; @@ -198,7 +199,7 @@ public class JwtTest { } @Test - public void testToStringPlaintext() { + public void testToStringPlaintext() throws NoSuchAlgorithmException { Jwt jwt = new Jwt(); jwt.getHeader().setAlgorithm("none"); jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));