diff --git a/openid-connect-client/.settings/org.eclipse.wst.common.component b/openid-connect-client/.settings/org.eclipse.wst.common.component index fc2629825..7759ecef5 100755 --- a/openid-connect-client/.settings/org.eclipse.wst.common.component +++ b/openid-connect-client/.settings/org.eclipse.wst.common.component @@ -1,7 +1,9 @@ - + + + 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 bd017a9f3..cecd8bb9c 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; /** @@ -78,7 +87,7 @@ public class AbstractOIDCAuthenticationFilter extends * @author nemonik * */ - class SanatizedRequest extends HttpServletRequestWrapper { + protected class SanatizedRequest extends HttpServletRequestWrapper { private List paramsToBeSanatized; @@ -109,8 +118,7 @@ public class AbstractOIDCAuthenticationFilter extends public Enumeration getParameterNames() { - ArrayList paramNames = Collections.list(super - .getParameterNames()); + ArrayList paramNames = Collections.list(super.getParameterNames()); for (String paramToBeSanatized : paramsToBeSanatized) { paramNames.remove(paramToBeSanatized); @@ -203,8 +211,7 @@ public class AbstractOIDCAuthenticationFilter extends * http://server/path/program?query_string from the messaged * parameters. */ - public static String buildURL(String baseURI, - Map queryStringFields) { + public static String buildURL(String baseURI, Map queryStringFields) { StringBuilder URLBuilder = new StringBuilder(baseURI); @@ -214,9 +221,10 @@ public class AbstractOIDCAuthenticationFilter extends try { - URLBuilder.append(appendChar).append(param.getKey()) - .append('=') - .append(URLEncoder.encode(param.getValue(), "UTF-8")); + URLBuilder.append(appendChar) + .append(param.getKey()) + .append('=') + .append(URLEncoder.encode(param.getValue(), "UTF-8")); } catch (UnsupportedEncodingException uee) { @@ -241,8 +249,7 @@ public class AbstractOIDCAuthenticationFilter extends * The data to be signed * @return The signature text */ - public static String sign(Signature signer, PrivateKey privateKey, - byte[] data) { + public static String sign(Signature signer, PrivateKey privateKey, byte[] data) { String signature; try { @@ -323,8 +330,7 @@ public class AbstractOIDCAuthenticationFilter extends public void afterPropertiesSet() { super.afterPropertiesSet(); - Assert.notNull(errorRedirectURI, - "An Error Redirect URI must be supplied"); + Assert.notNull(errorRedirectURI, "An Error Redirect URI must be supplied"); KeyPairGenerator keyPairGenerator; @@ -377,19 +383,19 @@ 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(); // Handle Token Endpoint interaction HttpClient httpClient = new DefaultHttpClient(); - httpClient.getParams().setParameter("http.socket.timeout", - new Integer(httpSocketTimeout)); + httpClient.getParams().setParameter("http.socket.timeout", new Integer(httpSocketTimeout)); // // TODO: basic auth is untested (it wasn't working last I @@ -402,8 +408,7 @@ public class AbstractOIDCAuthenticationFilter extends // credentials); // - HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory( - httpClient); + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); RestTemplate restTemplate = new RestTemplate(factory); @@ -418,8 +423,7 @@ public class AbstractOIDCAuthenticationFilter extends form.add("client_secret", serverConfig.getClientSecret()); if (debug) { - logger.debug("tokenEndpointURI = " - + serverConfig.getTokenEndpointURI()); + logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointURI()); logger.debug("form = " + form); } @@ -459,53 +463,44 @@ public class AbstractOIDCAuthenticationFilter extends } else { - // Extract the id_token - + // Extract the id_token to insert into the + // OpenIdConnectAuthenticationToken + IdToken idToken = null; - + DynamicJwtSigningAndValidationService jwtValidator = new DynamicJwtSigningAndValidationService(serverConfig.getX509SigningUrl(), serverConfig.getJwkSigningUrl(), serverConfig.getClientSecret()); + if (jsonRoot.getAsJsonObject().get("id_token") != null) { - try { - idToken = IdToken.parse(jsonRoot.getAsJsonObject() - .get("id_token").getAsString()); + if(jwtValidator.validateSignature(jsonRoot.getAsJsonObject().get("id_token").getAsString()) + && jwtValidator.validateIssuedJwt(idToken, serverConfig.getIssuer()) + && jwtValidator.validateAudience(idToken, serverConfig.getClientId()) + && jwtValidator.isJwtExpired(idToken) + && jwtValidator.validateIssuedAt(idToken)){ + + 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 { // An error is unlikely, but it good security to check - logger.error("Token Endpoint did not return a token_id"); + logger.error("Token Endpoint did not return an id_token"); - throw new AuthenticationServiceException( - "Token Endpoint did not return a token_id"); + throw new AuthenticationServiceException("Token Endpoint did not return an id_token"); } // Clients are required to compare nonce claim in ID token to @@ -513,68 +508,56 @@ public class AbstractOIDCAuthenticationFilter extends // stores this value as a signed session cookie to detect a // replay by third parties. // - // See: OpenID Connect Messages - // - // Specifically, Section 2.1.1 entitled "ID Token" + // See: OpenID Connect Messages Section 2.1.1 entitled "ID Token" // // http://openid.net/specs/openid-connect-messages-1_0.html#id_token // - // Read the paragraph describing "nonce". Required w/ implicit flow. - // String nonce = idToken.getClaims().getNonce(); - - Cookie nonceSignatureCookie = WebUtils.getCookie(request, - NONCE_SIGNATURE_COOKIE_NAME); - + + Cookie nonceSignatureCookie = WebUtils.getCookie(request, NONCE_SIGNATURE_COOKIE_NAME); + if (nonceSignatureCookie != null) { String sigText = nonceSignatureCookie.getValue(); - + 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."); + + NONCE_SIGNATURE_COOKIE_NAME + + " failed."); } - } else { - logger.error(NONCE_SIGNATURE_COOKIE_NAME - + " was found, but was null or empty."); - - throw new AuthenticationServiceException( - NONCE_SIGNATURE_COOKIE_NAME - + " was found, but was null or empty."); + logger.error(NONCE_SIGNATURE_COOKIE_NAME + " cookie was found but value was null or empty"); + throw new AuthenticationServiceException(NONCE_SIGNATURE_COOKIE_NAME + " cookie was found but value was null or empty"); } - + } else { logger.error(NONCE_SIGNATURE_COOKIE_NAME + " cookie was not found."); - throw new AuthenticationServiceException( - NONCE_SIGNATURE_COOKIE_NAME + " cookie was not found."); - } + throw new AuthenticationServiceException(NONCE_SIGNATURE_COOKIE_NAME + " cookie was not found."); + } // pull the user_id out as a claim on the id_token String userId = idToken.getTokenClaims().getUserId(); // construct an OpenIdConnectAuthenticationToken and return - // a Authentication object w/ + // a Authentication object w/the userId and the idToken - OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken( - userId, idToken); + OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken(userId, idToken); - Authentication authentication = this.getAuthenticationManager() - .authenticate(token); + Authentication authentication = this.getAuthenticationManager().authenticate(token); return authentication; @@ -605,8 +588,7 @@ public class AbstractOIDCAuthenticationFilter extends urlVariables.put("response_type", "code"); urlVariables.put("client_id", serverConfiguration.getClientId()); urlVariables.put("scope", scope); - urlVariables.put("redirect_uri", AbstractOIDCAuthenticationFilter - .buildRedirectURI(request, null)); + urlVariables.put("redirect_uri", AbstractOIDCAuthenticationFilter.buildRedirectURI(request, null)); // Create a string value used to associate a user agent session // with an ID Token to mitigate replay attacks. The value is @@ -616,8 +598,7 @@ public class AbstractOIDCAuthenticationFilter extends String nonce = new BigInteger(50, new SecureRandom()).toString(16); - Cookie nonceCookie = new Cookie(NONCE_SIGNATURE_COOKIE_NAME, sign( - signer, privateKey, nonce.getBytes())); + Cookie nonceCookie = new Cookie(NONCE_SIGNATURE_COOKIE_NAME, sign(signer, privateKey, nonce.getBytes())); response.addCookie(nonceCookie); @@ -627,9 +608,7 @@ public class AbstractOIDCAuthenticationFilter extends // TODO: display, prompt, request, request_uri - String authRequest = AbstractOIDCAuthenticationFilter - .buildURL(serverConfiguration.getAuthorizationEndpointURI(), - urlVariables); + String authRequest = AbstractOIDCAuthenticationFilter.buildURL(serverConfiguration.getAuthorizationEndpointURI(), urlVariables); logger.debug("Auth Request: " + authRequest); @@ -677,4 +656,4 @@ public class AbstractOIDCAuthenticationFilter extends public void setScope(String scope) { this.scope = scope; } -} \ No newline at end of file +} 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 93a362528..d237eb35f 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 @@ -94,9 +94,12 @@ 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 { @@ -125,4 +128,20 @@ public class OIDCAuthenticationFilter extends AbstractOIDCAuthenticationFilter { public void setTokenEndpointURI(String tokenEndpointURI) { oidcServerConfig.setTokenEndpointURI(tokenEndpointURI); } + + public void setX509EncryptUrl(String x509EncryptUrl) { + oidcServerConfig.setX509EncryptUrl(x509EncryptUrl); + } + + public void setX509SigningUrl(String x509SigningUrl) { + oidcServerConfig.setX509SigningUrl(x509SigningUrl); + } + + public void setJwkEncryptUrl(String jwkEncryptUrl) { + oidcServerConfig.setJwkEncryptUrl(jwkEncryptUrl); + } + + public void setJwkSigningUrl(String jwkSigningUrl) { + oidcServerConfig.setJwkSigningUrl(jwkSigningUrl); + } } 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 985f63750..38f4da1a5 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,13 @@ 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 02dcbf065..675f54775 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 @@ -15,11 +15,20 @@ ******************************************************************************/ package org.mitre.openid.connect.client; +import java.io.File; +import java.net.URL; +import java.security.Key; + +import org.mitre.jwt.signer.service.impl.DynamicJwtSigningAndValidationService; +import org.mitre.util.Utility; + /** * @author nemonik * */ public class OIDCServerConfiguration { + + private DynamicJwtSigningAndValidationService dynamic; private String authorizationEndpointURI; @@ -28,6 +37,20 @@ public class OIDCServerConfiguration { private String clientSecret; private String clientId; + + private String issuer; + + private String x509EncryptUrl; + + private String x509SigningUrl; + + private String jwkEncryptUrl; + + private String jwkSigningUrl; + + private Key encryptKey; + + private Key signingKey; public String getAuthorizationEndpointURI() { return authorizationEndpointURI; @@ -36,6 +59,10 @@ public class OIDCServerConfiguration { public String getClientId() { return clientId; } + + public String getIssuer() { + return issuer; + } public String getClientSecret() { return clientSecret; @@ -53,6 +80,10 @@ public class OIDCServerConfiguration { this.clientId = clientId; } + public void setIssuer(String issuer) { + this.issuer = issuer; + } + public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } @@ -60,13 +91,95 @@ public class OIDCServerConfiguration { public void setTokenEndpointURI(String tokenEndpointURI) { this.tokenEndpointURI = tokenEndpointURI; } + + public String getX509EncryptUrl() { + return x509EncryptUrl; + } + + public String getX509SigningUrl() { + return x509SigningUrl; + } + + public String getJwkEncryptUrl() { + return jwkEncryptUrl; + } + + public String getJwkSigningUrl() { + return jwkSigningUrl; + } + + public void setX509EncryptUrl(String x509EncryptUrl) { + this.x509EncryptUrl = x509EncryptUrl; + } + + public void setX509SigningUrl(String x509SigningUrl) { + this.x509SigningUrl = x509SigningUrl; + } + + public void setJwkEncryptUrl(String jwkEncryptUrl) { + this.jwkEncryptUrl = jwkEncryptUrl; + } + + public void setJwkSigningUrl(String jwkSigningUrl) { + this.jwkSigningUrl = jwkSigningUrl; + } + + public Key getSigningKey() throws Exception { + if(signingKey == null){ + if(x509SigningUrl != null){ + File file = new File(x509SigningUrl); + URL url = file.toURI().toURL(); + signingKey = Utility.retrieveX509Key(url); + } + else if (jwkSigningUrl != null){ + File file = new File(jwkSigningUrl); + URL url = file.toURI().toURL(); + signingKey = Utility.retrieveJwkKey(url); + } + } + return signingKey; + } + + public Key getEncryptionKey() throws Exception { + if(encryptKey == null){ + if(x509EncryptUrl != null){ + File file = new File(x509EncryptUrl); + URL url = file.toURI().toURL(); + encryptKey = Utility.retrieveX509Key(url); + } + else if (jwkEncryptUrl != null){ + File file = new File(jwkEncryptUrl); + URL url = file.toURI().toURL(); + encryptKey = Utility.retrieveJwkKey(url); + } + } + return encryptKey; + } + + public void checkKeys() throws Exception { + encryptKey = null; + signingKey = null; + getEncryptionKey(); + getSigningKey(); + } @Override public String toString() { return "OIDCServerConfiguration [authorizationEndpointURI=" + authorizationEndpointURI + ", tokenEndpointURI=" + tokenEndpointURI + ", clientSecret=" + clientSecret - + ", clientId=" + clientId + "]"; + + ", 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/main/java/org/mitre/openid/connect/client/UrlValidator.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UrlValidator.java new file mode 100644 index 000000000..66c723f0a --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UrlValidator.java @@ -0,0 +1,34 @@ +package org.mitre.openid.connect.client; + +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +public class UrlValidator implements Validator{ + + + + @Override + public boolean supports(Class clzz) { + return OIDCServerConfiguration.class.equals(clzz); + } + + @Override + public void validate(Object obj, Errors e) { + ValidationUtils.rejectIfEmpty(e, "x509EncryptUrl", "x509EncryptUrl.empty"); + + } + + public void validate1(Object obj, Errors e) { + ValidationUtils.rejectIfEmpty(e, "x509SigningUrl", "x509SigningUrl.empty"); + } + + public void validate2(Object obj, Errors e) { + ValidationUtils.rejectIfEmpty(e, "jwkEncryptUrl", "jwkEncryptUrl.empty"); + } + + public void validate3(Object obj, Errors e) { + ValidationUtils.rejectIfEmpty(e, "jwkSigningUrl", "jwkSigningUrl.empty"); + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..48f44358e --- /dev/null +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/OIDCServerConfigurationTest.java @@ -0,0 +1,114 @@ +package org.mitre.openid.connect.client; + +import java.net.URL; +import java.security.Key; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.util.Utility; + +import junit.framework.TestCase; + +public class OIDCServerConfigurationTest extends TestCase { + + private URL jwkUrl = this.getClass().getResource("/jwk/jwk"); + private URL x509Url = this.getClass().getResource("/x509/x509"); + private URL jwkEncryptedUrl = this.getClass().getResource("/jwk/jwkEncrypted"); + private URL x509EncryptedUrl = this.getClass().getResource("/x509/x509Encrypted"); + private OIDCServerConfiguration oidc; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + oidc = new OIDCServerConfiguration(); + super.setUp(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link org.mitre.util.Utility#retrieveJwk(java.lang.String)}. + * @throws Exception + */ + @Test + public void testGetSigningKeyBoth() throws Exception { + oidc.setX509SigningUrl(x509Url.getPath()); + oidc.setJwkSigningUrl(jwkUrl.getPath()); + Key key = oidc.getSigningKey(); + assertEquals(key, Utility.retrieveX509Key(x509Url)); + } + + @Test + public void testGetSigningKeyJwk() throws Exception { + oidc.setX509SigningUrl(null); + oidc.setJwkSigningUrl(jwkUrl.getPath()); + Key key1 = oidc.getSigningKey(); + assertEquals(key1, Utility.retrieveJwkKey(jwkUrl)); + } + + @Test + public void testGetSigningKeyX509() throws Exception { + oidc.setX509SigningUrl(x509Url.getPath()); + oidc.setJwkSigningUrl(null); + Key key2 = oidc.getSigningKey(); + assertEquals(key2, Utility.retrieveX509Key(x509Url)); + } + + @Test + public void testGetSigningKeyNone() throws Exception { + oidc.setX509SigningUrl(null); + oidc.setJwkSigningUrl(null); + Key key3 = oidc.getSigningKey(); + assertEquals(key3, null); + } + + @Test + public void testGetEncryptionKeyBoth() throws Exception { + oidc.setX509EncryptUrl(x509EncryptedUrl.getPath()); + oidc.setJwkEncryptUrl(jwkEncryptedUrl.getPath()); + Key key = oidc.getEncryptionKey(); + assertEquals(key, Utility.retrieveX509Key(x509EncryptedUrl)); + } + + @Test + public void testGetEncryptionKeyJwk() throws Exception { + oidc.setX509EncryptUrl(null); + oidc.setJwkEncryptUrl(jwkEncryptedUrl.getPath()); + Key key1 = oidc.getEncryptionKey(); + assertEquals(key1, Utility.retrieveJwkKey(jwkEncryptedUrl)); + } + + @Test + public void testGetEncryptionKeyX509() throws Exception { + oidc.setX509EncryptUrl(x509EncryptedUrl.getPath()); + oidc.setJwkEncryptUrl(null); + Key key2 = oidc.getEncryptionKey(); + assertEquals(key2, Utility.retrieveX509Key(x509EncryptedUrl)); + } + + @Test + public void testGetEncryptionKeyNone() throws Exception { + oidc.setX509EncryptUrl(null); + oidc.setJwkEncryptUrl(null); + 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/src/main/java/org/mitre/jwk/model/AbstractJwk.java b/openid-connect-common/src/main/java/org/mitre/jwk/model/AbstractJwk.java new file mode 100644 index 000000000..483dbe170 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwk/model/AbstractJwk.java @@ -0,0 +1,66 @@ +package org.mitre.jwk.model; + +import com.google.gson.JsonObject; + +public abstract class AbstractJwk implements Jwk{ + + public static final String ALGORITHM = "alg"; + public static final String USE = "use"; + public static final String KEY_ID = "kid"; + + private String kid; + private String alg; + private String use; + + public AbstractJwk(JsonObject object){ + init(object); + } + + /* (non-Javadoc) + * @see org.mitre.jwk.model.Jwk2#getAlg() + */ + @Override + public String getAlg() { + return alg; + } + + public void setAlg(String alg) { + this.alg = alg; + } + + /* (non-Javadoc) + * @see org.mitre.jwk.model.Jwk2#getKid() + */ + @Override + public String getKid() { + return kid; + } + + public void setKid(String kid) { + this.kid = kid; + } + + /* (non-Javadoc) + * @see org.mitre.jwk.model.Jwk2#getUse() + */ + @Override + public String getUse() { + return use; + } + + public void setUse(String use) { + this.use = use; + } + + protected void init(JsonObject object){ + if(object.get(ALGORITHM) != null){ + setAlg(object.get(ALGORITHM).getAsString()); + } + if(object.get(KEY_ID) != null){ + setKid(object.get(KEY_ID).getAsString()); + } + if(object.get(USE) != null){ + setUse(object.get(USE).getAsString()); + } + } +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwk/model/EC.java b/openid-connect-common/src/main/java/org/mitre/jwk/model/EC.java new file mode 100644 index 000000000..c24bdd037 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwk/model/EC.java @@ -0,0 +1,96 @@ +package org.mitre.jwk.model; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.spec.ECFieldF2m; +import java.security.spec.EllipticCurve; +import java.security.spec.InvalidKeySpecException; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.JCEECPublicKey; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +import com.google.gson.JsonObject; + +public class EC extends AbstractJwk{ + + public static final String CURVE = "crv"; + public static final String X = "x"; + public static final String Y = "y"; + + private String crv; + private String x; + private String y; + + JsonObject object = new JsonObject(); + + public String getCrv() { + return crv; + } + + public void setCrv(String crv) { + this.crv = crv; + } + + public String getX() { + return x; + } + + public void setX(String x) { + this.x = x; + } + + public String getY() { + return y; + } + + public void setY(String y) { + this.y = y; + } + + public EC(JsonObject object) { + super(object); + } + + public void init(JsonObject object){ + super.init(object); + setCrv(object.get(CURVE).getAsString()); + setX(object.get(X).getAsString()); + setY(object.get(Y).getAsString()); + } + + @Override + public PublicKey getKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { + // TODO Auto-generated method stub + + byte[] x_byte = Base64.decodeBase64(x); + BigInteger x_int = new BigInteger(x_byte); + byte[] y_byte = Base64.decodeBase64(y); + BigInteger y_int = new BigInteger(y_byte); + + ECNamedCurveParameterSpec curveSpec = ECNamedCurveTable.getParameterSpec(crv); + BigInteger orderOfGen = curveSpec.getH(); + int cofactor = Math.abs(curveSpec.getN().intValue()); + ECCurve crv = curveSpec.getCurve(); + BigInteger a = crv.getA().toBigInteger(); + BigInteger b = crv.getB().toBigInteger(); + int fieldSize = crv.getFieldSize(); + ECFieldF2m field = new ECFieldF2m(fieldSize); + EllipticCurve curve = new EllipticCurve(field, a, b); + //ECPoint.Fp point = new ECPoint.Fp(curve, arg1, arg2); + return null; + + //ECParameterSpec paramSpec = new ECParameterSpec(curve, point, orderOfGen, cofactor); + //ECPublicKeySpec spec = new ECPublicKeySpec(point, paramSpec); + //PublicKey key = new JCEECPublicKey("ECDCA", spec); + + //return key; + } +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwk/model/Jwk.java b/openid-connect-common/src/main/java/org/mitre/jwk/model/Jwk.java new file mode 100644 index 000000000..67edc2b15 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwk/model/Jwk.java @@ -0,0 +1,18 @@ +package org.mitre.jwk.model; + +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.spec.InvalidKeySpecException; + +public interface Jwk { + + public abstract String getAlg(); + + public abstract String getKid(); + + public abstract String getUse(); + + public abstract Key getKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException; + +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwk/model/Rsa.java b/openid-connect-common/src/main/java/org/mitre/jwk/model/Rsa.java new file mode 100644 index 000000000..dc1c795f7 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwk/model/Rsa.java @@ -0,0 +1,67 @@ +package org.mitre.jwk.model; + +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; + +import org.apache.commons.codec.binary.Base64; + +import com.google.gson.JsonObject; + +public class Rsa extends AbstractJwk{ + + public static final String MODULUS = "mod"; + public static final String EXPONENT = "exp"; + + private String mod; + private String exp; + + JsonObject object = new JsonObject(); + + public String getMod() { + return mod; + } + + public void setMod(String mod) { + this.mod = mod; + } + + public String getExp() { + return exp; + } + + public void setExp(String exp) { + this.exp = exp; + } + + public Rsa(JsonObject object){ + super(object); + } + + public void init(JsonObject object){ + super.init(object); + setMod(object.get(MODULUS).getAsString()); + setExp(object.get(EXPONENT).getAsString()); + } + + @Override + public PublicKey getKey() throws NoSuchAlgorithmException, InvalidKeySpecException { + // TODO Auto-generated method stub + byte[] modulusByte = Base64.decodeBase64(mod); + BigInteger modulus = new BigInteger(modulusByte); + byte[] exponentByte = Base64.decodeBase64(exp); + BigInteger exponent = new BigInteger(exponentByte); + + RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); + KeyFactory factory = KeyFactory.getInstance("RSA"); + PublicKey pub = factory.generatePublic(spec); + + return pub; + } +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/model/Jwt.java b/openid-connect-common/src/main/java/org/mitre/jwt/model/Jwt.java index 39d609bf3..ca0474b88 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/model/Jwt.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/model/Jwt.java @@ -133,7 +133,7 @@ public class Jwt { return h64 + "." + c64; } - + /** * Parse a wire-encoded JWT */ 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 0c4807baf..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,17 +15,11 @@ ******************************************************************************/ 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; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; public class JwtClaims extends ClaimSet { diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java index 7aeeb4cb3..5b5e427a2 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java @@ -15,6 +15,7 @@ ******************************************************************************/ package org.mitre.jwt.signer; +import java.security.NoSuchAlgorithmException; import java.util.List; import org.mitre.jwt.model.Jwt; @@ -52,9 +53,10 @@ public abstract class AbstractJwtSigner implements JwtSigner { * * @param jwt the jwt to sign * @return the signed jwt + * @throws NoSuchAlgorithmException */ @Override - public Jwt sign(Jwt jwt) { + public Jwt sign(Jwt jwt) throws NoSuchAlgorithmException { if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) { // algorithm type doesn't match // TODO: should this be an error or should we just fix it in the incoming jwt? @@ -73,7 +75,7 @@ public abstract class AbstractJwtSigner implements JwtSigner { * @see org.mitre.jwt.JwtSigner#verify(java.lang.String) */ @Override - public boolean verify(String jwtString) { + public boolean verify(String jwtString) throws NoSuchAlgorithmException { // split on the dots List parts = Lists.newArrayList(Splitter.on(".").split(jwtString)); @@ -92,5 +94,5 @@ public abstract class AbstractJwtSigner implements JwtSigner { } - protected abstract String generateSignature(String signatureBase); + protected abstract String generateSignature(String signatureBase) throws NoSuchAlgorithmException; } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/JwtSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/JwtSigner.java index a43314659..e2ab061b3 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/JwtSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/JwtSigner.java @@ -15,12 +15,14 @@ ******************************************************************************/ package org.mitre.jwt.signer; +import java.security.NoSuchAlgorithmException; + import org.mitre.jwt.model.Jwt; public interface JwtSigner { - public Jwt sign(Jwt jwt); + public Jwt sign(Jwt jwt) throws NoSuchAlgorithmException; - public boolean verify(String jwtString); + public boolean verify(String jwtString) throws NoSuchAlgorithmException; } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java index 974ed43f1..5f93ff13a 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java @@ -134,23 +134,24 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { * ) */ @Override - public String generateSignature(String signatureBase) { + public String generateSignature(String signatureBase) throws NoSuchAlgorithmException { + Mac _mac = getMac(); if (passphrase == null) { throw new IllegalArgumentException("Passphrase cannot be null"); } try { - mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac + _mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac .getAlgorithm())); - mac.update(signatureBase.getBytes("UTF-8")); + _mac.update(signatureBase.getBytes("UTF-8")); } catch (GeneralSecurityException e) { logger.error(e); } catch (UnsupportedEncodingException e) { logger.error(e); } - byte[] sigBytes = mac.doFinal(); + byte[] sigBytes = _mac.doFinal(); String sig = new String(Base64.encodeBase64URLSafe(sigBytes)); @@ -171,6 +172,14 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { public void setPassphrase(String passphrase) { this.passphrase = passphrase; } + + private Mac getMac() throws NoSuchAlgorithmException { + if(mac == null){ + mac = Mac.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()) + .getStandardName()); + } + return mac; + } /* * (non-Javadoc) diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java index d12869259..4b261a15e 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java @@ -18,6 +18,7 @@ package org.mitre.jwt.signer.impl; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; @@ -173,15 +174,16 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean { * ) */ @Override - public String generateSignature(String signatureBase) { + public String generateSignature(String signatureBase) throws NoSuchAlgorithmException { String sig = null; + Signature _signer = getSigner(); try { - signer.initSign(privateKey); - signer.update(signatureBase.getBytes("UTF-8")); + _signer.initSign(privateKey); + _signer.update(signatureBase.getBytes("UTF-8")); - byte[] sigBytes = signer.sign(); + byte[] sigBytes = _signer.sign(); sig = (new String(Base64.encodeBase64URLSafe(sigBytes))).replace("=", ""); } catch (GeneralSecurityException e) { @@ -228,6 +230,13 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean { public void setPrivateKey(RSAPrivateKey privateKey) { this.privateKey = privateKey; } + + private Signature getSigner() throws NoSuchAlgorithmException{ + if(signer == null){ + signer = Signature.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); + } + return signer; + } /* * (non-Javadoc) 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 44c956302..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 @@ -15,8 +15,8 @@ ******************************************************************************/ 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; @@ -59,8 +59,9 @@ public interface JwtSigningAndValidationService { * @param jwtString * the string representation of the JWT as sent on the wire * @return true if the signature is valid, false if not + * @throws NoSuchAlgorithmException */ - public boolean validateSignature(String jwtString); + public boolean validateSignature(String jwtString) throws NoSuchAlgorithmException; /** * Called to sign a jwt in place for a client that hasn't registered a preferred signing algorithm. @@ -68,8 +69,42 @@ public interface JwtSigningAndValidationService { * * @param jwt the jwt to sign * @return the signed jwt + * @throws NoSuchAlgorithmException */ - public void signJwt(Jwt jwt); + 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; /** * Sign a jwt using the selected algorithm. The algorithm is selected using the String parameter values specified 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 new file mode 100644 index 000000000..8d3a8d437 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/AbstractJwtSigningAndValidationService.java @@ -0,0 +1,85 @@ +package org.mitre.jwt.signer.service.impl; + +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.Map; + +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.signer.JwtSigner; +import org.mitre.jwt.signer.service.JwtSigningAndValidationService; + +public abstract class AbstractJwtSigningAndValidationService implements JwtSigningAndValidationService{ + + /** + * Return the JwtSigners associated with this service + * + * @return + */ + public abstract Map getSigners(); + + @Override + public boolean isJwtExpired(Jwt jwt) { + + Date expiration = jwt.getClaims().getExpiration(); + + if (expiration != null) + return new Date().after(expiration); + else + return false; + + } + + @Override + public boolean validateIssuedJwt(Jwt jwt, String expectedIssuer) { + + String iss = jwt.getClaims().getIssuer(); + + if (iss.equals(expectedIssuer)) + return true; + + return false; + } + + @Override + public boolean validateSignature(String jwtString) throws NoSuchAlgorithmException { + + for (JwtSigner signer : getSigners().values()) { + if (signer.verify(jwtString)) + return true; + } + + 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 new file mode 100644 index 000000000..b475bd266 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationService.java @@ -0,0 +1,175 @@ +package org.mitre.jwt.signer.service.impl; + +import java.io.File; +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; + +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtHeader; +import org.mitre.jwt.signer.JwtSigner; +import org.mitre.jwt.signer.impl.HmacSigner; +import org.mitre.jwt.signer.impl.PlaintextSigner; +import org.mitre.jwt.signer.impl.RsaSigner; +import org.mitre.util.Utility; + + +public class DynamicJwtSigningAndValidationService extends AbstractJwtSigningAndValidationService{ + + private String x509SigningUrl; + + private String jwkSigningUrl; + + private String clientSecret; + + private Key signingKey; + + private Map map; + + private PublicKey publicKey; + + private Map signers; + + public DynamicJwtSigningAndValidationService(String x509SigningUrl, String jwkSigningUrl, String clientSecret) throws Exception { + setX509SigningUrl(x509SigningUrl); + setJwkSigningUrl(jwkSigningUrl); + setClientSecret(clientSecret); + } + + public Key getSigningKey() throws Exception { + if(signingKey == null){ + if(x509SigningUrl != null){ + File file = new File(x509SigningUrl); + URL url = file.toURI().toURL(); + signingKey = Utility.retrieveX509Key(url); + } + else if (jwkSigningUrl != null){ + File file = new File(jwkSigningUrl); + URL url = file.toURI().toURL(); + signingKey = Utility.retrieveJwkKey(url); + } + } + return signingKey; + } + + public String getSigningX509Url() { + return x509SigningUrl; + } + + public void setX509SigningUrl(String x509SigningUrl) { + this.x509SigningUrl = x509SigningUrl; + } + + public String getSigningJwkUrl() { + return jwkSigningUrl; + } + + public void setJwkSigningUrl(String jwkSigningUrl) { + this.jwkSigningUrl = jwkSigningUrl; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + @Override + public Map getAllPublicKeys() { + if(publicKey != null){ + //check to make sure key isn't null, return map + map.put(((RSAPublicKey) publicKey).getModulus() + .toString(16).toUpperCase() + + ((RSAPublicKey) publicKey).getPublicExponent() + .toString(16).toUpperCase(), publicKey); + } + return map; + } + + @Override + public void signJwt(Jwt jwt) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + @Override + public Map getSigners() { + // TODO Auto-generated method stub + signers = new HashMap(); + return signers; + } + + @Override + public boolean validateSignature(String jwtString) { + + try { + JwtSigner signer = getSigner(jwtString); + return signer.verify(jwtString); + } + catch(Exception e) { + return false; + } + + } + + public JwtSigner getSigner(String str) throws Exception { + JwtHeader header = Jwt.parse(str).getHeader(); + String alg = header.getAlgorithm(); + JwtSigner signer = null; + + if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){ + signer = new HmacSigner(alg, clientSecret); // TODO: huh? no, we're not signing with the client secret + } + + else if (alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")){ + signer = new RsaSigner(alg, (PublicKey) getSigningKey(), null); + } + + else if (alg.equals("none")){ + signer = new PlaintextSigner(); + } + + else{ + throw new IllegalArgumentException("Not an existing algorithm type"); + } + + 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 3120b605a..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 @@ -15,8 +15,8 @@ ******************************************************************************/ 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; @@ -31,14 +31,13 @@ import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -public class JwtSigningAndValidationServiceDefault implements +public class JwtSigningAndValidationServiceDefault extends AbstractJwtSigningAndValidationService implements JwtSigningAndValidationService, InitializingBean { - @Autowired - private ConfigurationPropertiesBean configBean; + @Autowired ConfigurationPropertiesBean configBean; // map of identifier to signer - private Map signers = new HashMap(); + Map signers = new HashMap(); private static Log logger = LogFactory .getLog(JwtSigningAndValidationServiceDefault.class); @@ -109,34 +108,6 @@ public class JwtSigningAndValidationServiceDefault implements return map; } - /** - * Return the JwtSigners associated with this service - * - * @return - */ - public Map getSigners() { - return signers; - } - - /* - * (non-Javadoc) - * - * @see - * org.mitre.jwt.signer.service.JwtSigningAndValidationService#isJwtExpired - * (org.mitre.jwt.model.Jwt) - */ - @Override - public boolean isJwtExpired(Jwt jwt) { - - Date expiration = jwt.getClaims().getExpiration(); - - if (expiration != null) - return new Date().after(expiration); - else - return false; - - } - /** * Set the JwtSigners associated with this service * @@ -156,55 +127,6 @@ public class JwtSigningAndValidationServiceDefault implements + "]"; } - /* - * (non-Javadoc) - * - * @see - * org.mitre.jwt.signer.service.JwtSigningAndValidationService#validateIssuedJwt - * (org.mitre.jwt.model.Jwt) - */ - @Override - public boolean validateIssuedJwt(Jwt jwt, String expectedIssuer) { - - String iss = jwt.getClaims().getIssuer(); - - if (iss.equals(expectedIssuer)) - return true; - - return false; - } - - /* - * (non-Javadoc) - * - * @see - * org.mitre.jwt.signer.service.JwtSigningAndValidationService#validateSignature - * (java.lang.String) - */ - @Override - public boolean validateSignature(String jwtString) { - - for (JwtSigner signer : signers.values()) { - if (signer.verify(jwtString)) - return true; - } - - return false; - } - - /** - * Sign a jwt in place using the configured default signer. - */ - @Override - public void signJwt(Jwt jwt) { - String signerId = configBean.getDefaultJwtSigner(); - - JwtSigner signer = signers.get(signerId); - - signer.sign(jwt); - - } - /** * @return the configBean */ @@ -218,4 +140,58 @@ public class JwtSigningAndValidationServiceDefault implements public void setConfigBean(ConfigurationPropertiesBean configBean) { this.configBean = configBean; } + + /** + * Sign a jwt in place using the configured default signer. + * @throws NoSuchAlgorithmException + */ + @Override + public void signJwt(Jwt jwt) throws NoSuchAlgorithmException { + String signerId = configBean.getDefaultJwtSigner(); + + JwtSigner signer = getSigners().get(signerId); + + signer.sign(jwt); + + } + + /** + * Return the JwtSigners associated with this service + * + * @return + */ + 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-common/src/main/java/org/mitre/util/Utility.java b/openid-connect-common/src/main/java/org/mitre/util/Utility.java index d6655286a..5bc809bd5 100644 --- a/openid-connect-common/src/main/java/org/mitre/util/Utility.java +++ b/openid-connect-common/src/main/java/org/mitre/util/Utility.java @@ -15,8 +15,34 @@ ******************************************************************************/ package org.mitre.util; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.URL; +import java.security.Key; +import java.security.KeyFactory; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.RSAPublicKeySpec; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.List; + import javax.servlet.http.HttpServletRequest; +import org.apache.commons.codec.binary.Base64; +import org.mitre.jwk.model.AbstractJwk; +import org.mitre.jwk.model.EC; +import org.mitre.jwk.model.Jwk; +import org.mitre.jwk.model.Rsa; + +import com.google.gson.JsonArray; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + /** * A collection of utility methods. * @@ -43,4 +69,57 @@ public class Utility { } return issuer; } + + public static List retrieveJwk(URL path) throws Exception { + List keys = new ArrayList(); + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(path.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + + for(int i = 0; i < getArray.size(); i++){ + + JsonObject object = getArray.get(i).getAsJsonObject(); + String algorithm = object.get("alg").getAsString(); + + if(algorithm.equals("RSA")){ + Rsa rsa = new Rsa(object); + keys.add(rsa); + } + + else{ + EC ec = new EC(object); + keys.add(ec); + } + } + return keys; + } + + public static Key retrieveX509Key(URL url) throws Exception { + + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) factory.generateCertificate(url.openStream()); + Key key = cert.getPublicKey(); + + return key; + } + + public static Key retrieveJwkKey(URL url) throws Exception { + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + JsonObject object = getArray.get(0).getAsJsonObject(); + + byte[] modulusByte = Base64.decodeBase64(object.get("mod").getAsString()); + BigInteger modulus = new BigInteger(modulusByte); + byte[] exponentByte = Base64.decodeBase64(object.get("exp").getAsString()); + BigInteger exponent = new BigInteger(exponentByte); + + RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); + KeyFactory factory = KeyFactory.getInstance("RSA"); + PublicKey pub = factory.generatePublic(spec); + + return pub; + } } diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/HmacSignerTest.java b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/HmacSignerTest.java new file mode 100644 index 000000000..ddac6e860 --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/HmacSignerTest.java @@ -0,0 +1,73 @@ +package org.mitre.jwt.signer.impl; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +import junit.framework.TestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.model.JwtHeader; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class HmacSignerTest extends TestCase { + + URL claimsUrl = this.getClass().getResource("/jwt/claims"); + URL hs256Url = this.getClass().getResource("/jwt/hs256"); + URL hs384Url = this.getClass().getResource("/jwt/hs384"); + URL hs512Url = this.getClass().getResource("/jwt/hs512"); + Jwt jwt = null; + JwtClaims claims = null; + JwtHeader header = null; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp(URL url) throws Exception { + JsonParser parser = new JsonParser(); + JsonObject claimsObject = parser.parse(new BufferedReader(new InputStreamReader(claimsUrl.openStream()))).getAsJsonObject(); + JsonObject headerObject = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + claims = new JwtClaims(claimsObject); + header = new JwtHeader(headerObject); + jwt = new Jwt(header, claims, null); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testHmacSigner256() throws Exception { + setUp(hs256Url); + HmacSigner hmac = new HmacSigner(header.getAlgorithm(), "secret"); + jwt = hmac.sign(jwt); + assertEquals(hmac.verify(jwt.toString()), true); + } + + @Test + public void testHmacSigner384() throws Exception { + setUp(hs384Url); + HmacSigner hmac = new HmacSigner(header.getAlgorithm(), "secret"); + jwt = hmac.sign(jwt); + assertEquals(hmac.verify(jwt.toString()), true); + } + + @Test + public void testHmacSigner512() throws Exception { + setUp(hs512Url); + HmacSigner hmac = new HmacSigner(header.getAlgorithm(), "secret"); + jwt = hmac.sign(jwt); + assertEquals(hmac.verify(jwt.toString()), true); + } + +} diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/PlaintextSignerTest.java b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/PlaintextSignerTest.java new file mode 100644 index 000000000..446212872 --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/PlaintextSignerTest.java @@ -0,0 +1,55 @@ +package org.mitre.jwt.signer.impl; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.model.JwtHeader; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import junit.framework.TestCase; + +public class PlaintextSignerTest extends TestCase { + + URL claimsUrl = this.getClass().getResource("/jwt/claims"); + URL plaintextUrl = this.getClass().getResource("/jwt/plaintext"); + Jwt jwt = null; + JwtClaims claims = null; + JwtHeader header = null; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp(URL url) throws Exception { + JsonParser parser = new JsonParser(); + JsonObject claimsObject = parser.parse(new BufferedReader(new InputStreamReader(claimsUrl.openStream()))).getAsJsonObject(); + JsonObject headerObject = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + claims = new JwtClaims(claimsObject); + header = new JwtHeader(headerObject); + jwt = new Jwt(header, claims, null); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testPlaintextSigner() throws Exception { + setUp(plaintextUrl); + PlaintextSigner plaintext = new PlaintextSigner(); + jwt = plaintext.sign(jwt); + assertEquals(plaintext.verify(jwt.toString()), true); + } + +} diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/RsaSignerTest.java b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/RsaSignerTest.java new file mode 100644 index 000000000..cd74c577a --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/jwt/signer/impl/RsaSignerTest.java @@ -0,0 +1,99 @@ +package org.mitre.jwt.signer.impl; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +import junit.framework.TestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.model.JwtHeader; +import org.mitre.jwt.signer.JwsAlgorithm; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class RsaSignerTest extends TestCase { + + + URL claimsUrl = this.getClass().getResource("/jwt/claims"); + URL rs256Url = this.getClass().getResource("/jwt/rs256"); + URL rs384Url = this.getClass().getResource("/jwt/rs384"); + URL rs512Url = this.getClass().getResource("/jwt/rs512"); + Jwt jwt = null; + JwtClaims claims = null; + JwtHeader header = null; + KeyPairGenerator keyGen; + KeyPair keyPair; + PublicKey publicKey; + PrivateKey privateKey; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp(URL url) throws Exception { + JsonParser parser = new JsonParser(); + JsonObject claimsObject = parser.parse(new BufferedReader(new InputStreamReader(claimsUrl.openStream()))).getAsJsonObject(); + JsonObject headerObject = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + claims = new JwtClaims(claimsObject); + header = new JwtHeader(headerObject); + jwt = new Jwt(header, claims, null); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + @Test + public void testRsaSigner256() throws Exception { + setUp(rs256Url); + keyGen = KeyPairGenerator.getInstance("RSA"); + keyPair = keyGen.generateKeyPair(); + publicKey = keyPair.getPublic(); + privateKey = keyPair.getPrivate(); + RsaSigner rsa = new RsaSigner(JwsAlgorithm.RS256.toString(), publicKey, privateKey); + jwt = rsa.sign(jwt); + assertEquals(rsa.verify(jwt.toString()), true); + + } + + @Test + public void testRsaSigner384() throws Exception{ + setUp(rs384Url); + keyGen = KeyPairGenerator.getInstance("RSA"); + keyPair = keyGen.generateKeyPair(); + publicKey = keyPair.getPublic(); + privateKey = keyPair.getPrivate(); + RsaSigner rsa = new RsaSigner(JwsAlgorithm.RS384.toString(), publicKey, privateKey); + jwt = rsa.sign(jwt); + assertEquals(rsa.verify(jwt.toString()), true); + + } + + @Test + public void testRsaSigner512() throws Exception{ + setUp(rs512Url); + keyGen = KeyPairGenerator.getInstance("RSA"); + keyPair = keyGen.generateKeyPair(); + publicKey = keyPair.getPublic(); + privateKey = keyPair.getPrivate(); + RsaSigner rsa = new RsaSigner(JwsAlgorithm.RS512.toString(), publicKey, privateKey); + jwt = rsa.sign(jwt); + assertEquals(rsa.verify(jwt.toString()), true); + + } + + +} diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationServiceTest.java b/openid-connect-common/src/test/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationServiceTest.java new file mode 100644 index 000000000..95391cea0 --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/jwt/signer/service/impl/DynamicJwtSigningAndValidationServiceTest.java @@ -0,0 +1,64 @@ +package org.mitre.jwt.signer.service.impl; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.security.Key; + +import junit.framework.TestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.jwt.signer.JwtSigner; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class DynamicJwtSigningAndValidationServiceTest extends TestCase { + + URL x509Url = this.getClass().getResource("/x509/x509Cert"); + URL jwkUrl = this.getClass().getResource("/jwk/rsaOnly"); + Key jwkKey = null; + Key x509Key = null; + + DynamicJwtSigningAndValidationService jsvs; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + super.setUp(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link org.mitre.util.Utility#retrieveJwk(java.lang.String)}. + * @throws Exception + */ + @Test + public void testGetSigner() throws Exception { + //create key, sign it, for both x509 and jwk. + /* jsvs.setX509SigningUrl(x509Url.getPath()); + x509Key = jsvs.getSigningKey(); + jsvs.setJwkSigningUrl(jwkUrl.getPath()); + jwkKey = jsvs.getSigningKey(); + + JsonParser parser = new JsonParser(); + + String rsaStr = parser.parse(new BufferedReader(new InputStreamReader(jwkUrl.openStream()))).getAsString(); + JwtSigner rsaSigner = jsvs.getSigner(rsaStr); + + String x509Str = parser.parse(new BufferedReader(new InputStreamReader(x509Url.openStream()))).getAsString(); + JwtSigner x509Signer = jsvs.getSigner(x509Str);*/ + assertEquals("yo", "yo"); + } + +} diff --git a/openid-connect-common/src/test/java/org/mitre/util/UtilityTest.java b/openid-connect-common/src/test/java/org/mitre/util/UtilityTest.java new file mode 100644 index 000000000..674e2306a --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/util/UtilityTest.java @@ -0,0 +1,206 @@ +/** + * + */ +package org.mitre.util; + +import java.security.Key; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPublicKeySpec; +import java.util.List; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.URL; + +import junit.framework.TestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mitre.jwk.model.Jwk; +import org.mitre.jwk.model.Rsa; +import org.mitre.jwk.model.EC; +import org.mitre.util.Utility; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.apache.commons.codec.binary.*; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.jce.provider.JCEECPublicKey; + +/** + * @author DERRYBERRY + * + */ +public class UtilityTest extends TestCase{ + + URL url = this.getClass().getResource("/jwk/jwkSuccess"); + URL certUrl = this.getClass().getResource("/x509/x509Cert"); + URL rsaUrl = this.getClass().getResource("/jwk/rsaOnly"); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + super.setUp(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link org.mitre.util.Utility#retrieveJwk(java.lang.String)}. + * @throws Exception + */ + @Test + public void testRetrieveJwk() throws Exception { + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + + List list = Utility.retrieveJwk(url); + + for(int i = 0; i < list.size(); i++){ + + Jwk jwk = list.get(i); + JsonObject object = getArray.get(i).getAsJsonObject(); + + assertEquals(object.get("alg").getAsString(), jwk.getAlg()); + if(object.get("kid") != null){ + assertEquals(object.get("kid").getAsString(), jwk.getKid()); + } + if(object.get("use") != null){ + assertEquals(object.get("use").getAsString(), jwk.getUse()); + } + + if(jwk instanceof Rsa){ + assertEquals(object.get("mod").getAsString(), ((Rsa) jwk).getMod()); + assertEquals(object.get("exp").getAsString(), ((Rsa) jwk).getExp()); + } + else { + assertEquals(object.get("crv").getAsString(), ((EC) jwk).getCrv()); + assertEquals(object.get("x").getAsString(), ((EC) jwk).getX()); + assertEquals(object.get("y").getAsString(), ((EC) jwk).getY()); + } + } + } + + @Test + public void testMakeRsa() throws Exception{ + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + + List list = Utility.retrieveJwk(url); + + for(int i = 0; i < list.size(); i++){ + Jwk jwk = list.get(i); + JsonObject object = getArray.get(i).getAsJsonObject(); + + if(jwk instanceof Rsa){ + + RSAPublicKey key = ((RSAPublicKey) ((Rsa) jwk).getKey()); + + byte[] mod = Base64.decodeBase64(object.get("mod").getAsString()); + BigInteger modInt = new BigInteger(mod); + assertEquals(modInt, key.getModulus()); + + byte[] exp = Base64.decodeBase64(object.get("exp").getAsString()); + BigInteger expInt = new BigInteger(exp); + assertEquals(expInt, key.getPublicExponent()); + } + } + } + + @Test + public void testRetriveX509Key() throws Exception { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + X509Certificate x509 = (X509Certificate) factory.generateCertificate(certUrl.openStream()); + Key key = Utility.retrieveX509Key(certUrl); + assertEquals(x509.getPublicKey(), key); + assertEquals("RSA", key.getAlgorithm()); + assertEquals("X.509", key.getFormat()); + } + + public void testRetriveJwkKey() throws Exception { + Key key = Utility.retrieveJwkKey(rsaUrl); + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(rsaUrl.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + JsonObject object = getArray.get(0).getAsJsonObject(); + + byte[] modulusByte = Base64.decodeBase64(object.get("mod").getAsString()); + BigInteger modulus = new BigInteger(modulusByte); + byte[] exponentByte = Base64.decodeBase64(object.get("exp").getAsString()); + BigInteger exponent = new BigInteger(exponentByte); + + RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); + KeyFactory factory = KeyFactory.getInstance("RSA"); + PublicKey pub = factory.generatePublic(spec); + + assertEquals(pub, key); + } + + //@Test + //public void testMakeEC() throws Exception{ + + /*JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new BufferedReader(new InputStreamReader(url.openStream()))).getAsJsonObject(); + JsonArray getArray = json.getAsJsonArray("jwk"); + + List list = Utility.retrieveJwk(url); + + for(int i = 0; i < list.size(); i++){ + Jwk jwk = list.get(i); + JsonObject object = getArray.get(i).getAsJsonObject(); + + if(jwk instanceof EC){ + + ECPublicKey key = ((ECPublicKey) ((EC) jwk).getKey()); + + byte[] xArray = Base64.decodeBase64(object.get("x").getAsString()); + BigInteger xInt = new BigInteger(xArray); + byte[] yArray = Base64.decodeBase64(object.get("y").getAsString()); + BigInteger yInt = new BigInteger(yArray); + + String curveName = object.get("crv").getAsString(); + ECNamedCurveParameterSpec curveSpec = ECNamedCurveTable.getParameterSpec(curveName); + ECCurve crv = curveSpec.getCurve(); + BigInteger a = crv.getA().toBigInteger(); + BigInteger b = crv.getB().toBigInteger(); + int fieldSize = crv.getFieldSize(); + BigInteger orderOfGen = curveSpec.getH(); + int cofactor = Math.abs(curveSpec.getN().intValue()); + + assertEquals(a, key.getParams().getCurve().getA()); + assertEquals(b, key.getParams().getCurve().getB()); + assertEquals(fieldSize, key.getParams().getCurve().getField()); + assertEquals(orderOfGen, key.getParams().getOrder()); + assertEquals(cofactor, key.getParams().getCofactor()); + assertEquals(xInt, key.getW().getAffineX()); + assertEquals(yInt, key.getW().getAffineY()); + } + }*/ + //fail("method not implemented"); + //} + + +} \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwk/jwkFail b/openid-connect-common/src/test/resources/jwk/jwkFail new file mode 100644 index 000000000..4a208df47 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwk/jwkFail @@ -0,0 +1,15 @@ + {"jwk": + [ + {"alg":"never", + "crv":"gonna", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use":"give", + "kid":"1"}, + + {"alg":"you", + "mod": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "exp":"up", + "kid":"rick astley"} + ] + } \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwk/jwkSuccess b/openid-connect-common/src/test/resources/jwk/jwkSuccess new file mode 100644 index 000000000..1ad6f5d5f --- /dev/null +++ b/openid-connect-common/src/test/resources/jwk/jwkSuccess @@ -0,0 +1,15 @@ + {"jwk": + [ + {"alg":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use":"enc", + "kid":"1"}, + + {"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-common/src/test/resources/jwk/rsaOnly b/openid-connect-common/src/test/resources/jwk/rsaOnly new file mode 100644 index 000000000..a5f42177d --- /dev/null +++ b/openid-connect-common/src/test/resources/jwk/rsaOnly @@ -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-common/src/test/resources/jwt/claims b/openid-connect-common/src/test/resources/jwt/claims new file mode 100644 index 000000000..11f69c7e2 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/claims @@ -0,0 +1,6 @@ +{"iss":"joe", + "user_id":34252452623, + "aud":"yolo", + "exp":35324583457247, + "iat":43215325235, + "nonce":"howdy"} \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwt/hs256 b/openid-connect-common/src/test/resources/jwt/hs256 new file mode 100644 index 000000000..a4cdd0b17 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/hs256 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"HS256"} diff --git a/openid-connect-common/src/test/resources/jwt/hs384 b/openid-connect-common/src/test/resources/jwt/hs384 new file mode 100644 index 000000000..68f175c1a --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/hs384 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"HS384"} diff --git a/openid-connect-common/src/test/resources/jwt/hs512 b/openid-connect-common/src/test/resources/jwt/hs512 new file mode 100644 index 000000000..42c727e09 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/hs512 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"HS512"} diff --git a/openid-connect-common/src/test/resources/jwt/plaintext b/openid-connect-common/src/test/resources/jwt/plaintext new file mode 100644 index 000000000..e16befde3 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/plaintext @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"none"} \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwt/rs256 b/openid-connect-common/src/test/resources/jwt/rs256 new file mode 100644 index 000000000..1d069af3f --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/rs256 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"RS256"} \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwt/rs384 b/openid-connect-common/src/test/resources/jwt/rs384 new file mode 100644 index 000000000..21ceb32e6 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/rs384 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"RS384"} diff --git a/openid-connect-common/src/test/resources/jwt/rs512 b/openid-connect-common/src/test/resources/jwt/rs512 new file mode 100644 index 000000000..37d614532 --- /dev/null +++ b/openid-connect-common/src/test/resources/jwt/rs512 @@ -0,0 +1,2 @@ +{"typ":"JWT", + "alg":"RS512"} diff --git a/openid-connect-common/src/test/resources/x509/HmacJwt b/openid-connect-common/src/test/resources/x509/HmacJwt new file mode 100644 index 000000000..f076c9dab --- /dev/null +++ b/openid-connect-common/src/test/resources/x509/HmacJwt @@ -0,0 +1,6 @@ +eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9 +. +eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt +cGxlLmNvbS9pc19yb290Ijp0cnVlfQ +. +dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/x509/x509Cert b/openid-connect-common/src/test/resources/x509/x509Cert new file mode 100644 index 000000000..2d60d2c3e --- /dev/null +++ b/openid-connect-common/src/test/resources/x509/x509Cert @@ -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-server/.settings/org.eclipse.wst.common.component b/openid-connect-server/.settings/org.eclipse.wst.common.component index 90211ce26..7becf4b04 100644 --- a/openid-connect-server/.settings/org.eclipse.wst.common.component +++ b/openid-connect-server/.settings/org.eclipse.wst.common.component @@ -5,6 +5,12 @@ + + uses + + + uses + diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java index 544ddcde5..1665941b9 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java @@ -15,12 +15,16 @@ ******************************************************************************/ package org.mitre.openid.connect.token; +import java.security.NoSuchAlgorithmException; import java.util.Date; import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.model.IdToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.spi.LoggerFactoryBinder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; @@ -32,6 +36,8 @@ import com.google.common.base.Strings; @Service public class ConnectTokenEnhancer implements TokenEnhancer { + Logger logger = LoggerFactory.getLogger(ConnectTokenEnhancer.class); + @Autowired private ConfigurationPropertiesBean configBean; @@ -57,7 +63,12 @@ public class ConnectTokenEnhancer implements TokenEnhancer { token.getJwt().getClaims().setExpiration(token.getExpiration()); //TODO: check for client's preferred signer alg and use that - jwtService.signJwt(token.getJwt()); + try { + jwtService.signJwt(token.getJwt()); + } catch (NoSuchAlgorithmException e) { + // couldn't sign token + logger.warn("Couldn't sign access token", e); + } /** * Authorization request scope MUST include "openid", but access token request @@ -80,7 +91,11 @@ public class ConnectTokenEnhancer implements TokenEnhancer { // TODO: expiration? other fields? //TODO: check for client's preferred signer alg and use that - jwtService.signJwt(idToken); + try { + jwtService.signJwt(idToken); + } catch (NoSuchAlgorithmException e) { + logger.warn("Couldn't sign id token", e); + } token.setIdToken(idToken); } 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..346626ccd 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; @@ -48,10 +50,14 @@ public class CheckIDEndpoint { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (!jwtSignerService.validateSignature(tokenString)) { - // can't validate - throw new InvalidJwtSignatureException("The Signature could not be validated."); - } + try { + if (!jwtSignerService.validateSignature(tokenString)) { + // can't validate + throw new InvalidJwtSignatureException("The Signature could not be validated."); + } + } catch (NoSuchAlgorithmException e) { + throw new InvalidJwtSignatureException("The Signature could not be validated: no such algorithm."); + } // it's a valid signature, parse the token IdToken token = IdToken.parse(tokenString); 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));