smart client

pull/105/merge
Mike Derryberry 2012-06-12 16:09:01 -04:00
parent 3e810cb5dc
commit 65dc3daaf8
23 changed files with 359 additions and 128 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/> <classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" path="src/test/java"/> <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes> <attributes>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0"> <project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="openid-connect-client"> <wb-module deploy-name="openid-connect-client">
<wb-resource deploy-path="/" source-path="/src/main/java"/>
<wb-resource deploy-path="/" source-path="/src/main/resources"/> <wb-resource deploy-path="/" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/src/test/java"/> <wb-resource deploy-path="/" source-path="/src/main/java"/>
<wb-resource deploy-path="/" source-path="/src/test/resources"/>
</wb-module> </wb-module>
</project-modules> </project-modules>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.9.1.201203220057-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>spring-servlet.xml</config>
</configs>
<configSets>
</configSets>
</beansProjectDescription>

View File

@ -28,6 +28,7 @@ import java.security.SecureRandom;
import java.security.Signature; import java.security.Signature;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
@ -42,9 +43,16 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.mitre.openid.connect.model.IdToken; 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.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication; 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.base.Splitter;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
/** /**
@ -376,11 +385,12 @@ public class AbstractOIDCAuthenticationFilter extends
* authentication * authentication
* @return The authenticated user token, or null if authentication is * @return The authenticated user token, or null if authentication is
* incomplete. * incomplete.
* @throws Exception
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
*/ */
protected Authentication handleAuthorizationGrantResponse( protected Authentication handleAuthorizationGrantResponse(
String authorizationGrant, HttpServletRequest request, String authorizationGrant, HttpServletRequest request,
OIDCServerConfiguration serverConfig) { OIDCServerConfiguration serverConfig) throws Exception {
final boolean debug = logger.isDebugEnabled(); final boolean debug = logger.isDebugEnabled();
@ -458,41 +468,44 @@ public class AbstractOIDCAuthenticationFilter extends
// Extract the id_token to insert into the // Extract the id_token to insert into the
// OpenIdConnectAuthenticationToken // OpenIdConnectAuthenticationToken
Cookie nonceSignatureCookie = WebUtils.getCookie(request,
NONCE_SIGNATURE_COOKIE_NAME);
IdToken idToken = null; IdToken idToken = null;
DynamicJwtSigningAndValidationService dynamic = new DynamicJwtSigningAndValidationService(null, null, null);
OIDCServerConfiguration oidc = new OIDCServerConfiguration();
if (jsonRoot.getAsJsonObject().get("id_token") != null) { if (jsonRoot.getAsJsonObject().get("id_token") != null) {
try { if(dynamic.validateSignature(jsonRoot.getAsJsonObject().get("id_token").getAsString())
idToken = IdToken.parse(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())){
List<String> parts = Lists.newArrayList(Splitter.on(".") try {
.split(jsonRoot.getAsJsonObject().get("id_token") idToken = IdToken.parse(jsonRoot.getAsJsonObject().get("id_token").getAsString());
.getAsString()));
if (parts.size() != 3) { } catch (Exception e) {
throw new IllegalArgumentException(
"Invalid JWT format."); // 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);
} }
}
String h64 = parts.get(0); else{
String c64 = parts.get(1); throw new AuthenticationServiceException("Problem verifying id_token");
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 { } else {
@ -505,100 +518,58 @@ public class AbstractOIDCAuthenticationFilter extends
"Token Endpoint did not return a token_id"); "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<String, String>();
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") // String iss = jsonRoot.getAsJsonObject().get("iss")
// .getAsString(); // .getAsString();
String userId = jsonRoot.getAsJsonObject().get("user_id") //String userId = jsonRoot.getAsJsonObject().get("user_id")
.getAsString(); // .getAsString();
// String aud = jsonRoot.getAsJsonObject().get("aud") // String aud = jsonRoot.getAsJsonObject().get("aud")
// .getAsString(); // .getAsString();
String nonce = jsonRoot.getAsJsonObject().get("nonce") //String nonce = jsonRoot.getAsJsonObject().get("nonce")
.getAsString(); // .getAsString();
// String exp = jsonRoot.getAsJsonObject().get("exp") // String exp = jsonRoot.getAsJsonObject().get("exp")
// .getAsString(); // .getAsString();
// Compare returned ID Token to signed session cookie // Compare returned ID Token to signed session cookie
// to detect ID Token replay by third parties. // 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)) { if (!verify(signer, publicKey, idToken.getClaims().getNonce(), sigText)) {
logger.error("Possible replay attack detected! " logger.error("Possible replay attack detected! "
+ "The comparison of the nonce in the returned " + "The comparison of the nonce in the returned "
+ "ID Token to the signed session " + "ID Token to the signed session "
+ NONCE_SIGNATURE_COOKIE_NAME + " failed."); + 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.");
throw new AuthenticationServiceException( throw new AuthenticationServiceException(
NONCE_SIGNATURE_COOKIE_NAME "Possible replay attack detected! "
+ " was found, but was null or empty."); + "The comparison of the nonce in the returned "
+ "ID Token to the signed session "
+ NONCE_SIGNATURE_COOKIE_NAME
+ " failed.");
} }
} else { } else {
logger.error(NONCE_SIGNATURE_COOKIE_NAME logger.error(NONCE_SIGNATURE_COOKIE_NAME
+ " cookie was not found."); + " was found, but was null or empty.");
throw new AuthenticationServiceException( 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 // Create an Authentication object for the token, and
// return. // return.
OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken( OpenIdConnectAuthenticationToken token = new OpenIdConnectAuthenticationToken(
userId, idToken); idToken.getTokenClaims().getUserId(), idToken);
Authentication authentication = this.getAuthenticationManager() Authentication authentication = this.getAuthenticationManager()
.authenticate(token); .authenticate(token);

View File

@ -97,9 +97,14 @@ public class OIDCAuthenticationFilter extends AbstractOIDCAuthenticationFilter {
} else if (StringUtils.isNotBlank(request.getParameter("code"))) { } else if (StringUtils.isNotBlank(request.getParameter("code"))) {
return handleAuthorizationGrantResponse( try {
request.getParameter("code"), new SanatizedRequest(request, return handleAuthorizationGrantResponse(
new String[] { "code" }), oidcServerConfig); request.getParameter("code"), new SanatizedRequest(request,
new String[] { "code" }), oidcServerConfig);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else { } else {

View File

@ -108,10 +108,15 @@ public class OIDCAuthenticationUsingChooserFilter extends
Cookie issuerCookie = WebUtils.getCookie(request, Cookie issuerCookie = WebUtils.getCookie(request,
ISSUER_COOKIE_NAME); ISSUER_COOKIE_NAME);
return handleAuthorizationGrantResponse( try {
request.getParameter("code"), new SanatizedRequest(request, return handleAuthorizationGrantResponse(
new String[] { "code" }), request.getParameter("code"), new SanatizedRequest(request,
oidcServerConfigs.get(issuerCookie.getValue())); new String[] { "code" }),
oidcServerConfigs.get(issuerCookie.getValue()));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else { } else {

View File

@ -16,20 +16,20 @@
package org.mitre.openid.connect.client; package org.mitre.openid.connect.client;
import java.io.File; import java.io.File;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.security.Key; import java.security.Key;
import org.mitre.jwt.signer.service.impl.DynamicJwtSigningAndValidationService;
import org.mitre.util.Utility; import org.mitre.util.Utility;
import com.google.gson.JsonObject;
/** /**
* @author nemonik * @author nemonik
* *
*/ */
public class OIDCServerConfiguration { public class OIDCServerConfiguration {
DynamicJwtSigningAndValidationService dynamic;
private String authorizationEndpointURI; private String authorizationEndpointURI;
private String tokenEndpointURI; private String tokenEndpointURI;
@ -40,6 +40,8 @@ public class OIDCServerConfiguration {
private String clientId; private String clientId;
private String issuer;
private String x509EncryptUrl; private String x509EncryptUrl;
private String x509SigningUrl; private String x509SigningUrl;
@ -64,6 +66,10 @@ public class OIDCServerConfiguration {
return clientId; return clientId;
} }
public String getIssuer() {
return issuer;
}
public String getClientSecret() { public String getClientSecret() {
return clientSecret; return clientSecret;
} }
@ -84,6 +90,10 @@ public class OIDCServerConfiguration {
this.clientId = clientId; this.clientId = clientId;
} }
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public void setClientSecret(String clientSecret) { public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret; this.clientSecret = clientSecret;
} }
@ -169,11 +179,18 @@ public class OIDCServerConfiguration {
+ authorizationEndpointURI + ", tokenEndpointURI=" + authorizationEndpointURI + ", tokenEndpointURI="
+ tokenEndpointURI + ", checkIDEndpointURI=" + tokenEndpointURI + ", checkIDEndpointURI="
+ checkIDEndpointURI + ", clientSecret=" + clientSecret + checkIDEndpointURI + ", clientSecret=" + clientSecret
+ ", clientId=" + clientId + ", x509EncryptedUrl=" + ", clientId=" + clientId + ", issuer=" + issuer
+", x509EncryptedUrl="
+ x509EncryptUrl + ", jwkEncryptedUrl=" + x509EncryptUrl + ", jwkEncryptedUrl="
+ jwkEncryptUrl + ", x509SigningUrl=" + jwkEncryptUrl + ", x509SigningUrl="
+ x509SigningUrl + ", jwkSigningUrl=" + x509SigningUrl + ", jwkSigningUrl="
+ jwkSigningUrl + "]"; + jwkSigningUrl + "]";
} }
public DynamicJwtSigningAndValidationService getDynamic() throws Exception{
dynamic = new DynamicJwtSigningAndValidationService(getX509SigningUrl(), getJwkSigningUrl(), getClientSecret());
return dynamic;
}
} }

View File

@ -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<OpenIdConnectAuthenticationToken> {
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;
}
}

View File

@ -101,4 +101,14 @@ public class OIDCServerConfigurationTest extends TestCase {
Key key3 = oidc.getEncryptionKey(); Key key3 = oidc.getEncryptionKey();
assertEquals(key3, null); 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");
}
} }

View File

@ -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"}
]
}

View File

@ -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"}
]
}

View File

@ -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-----

View File

@ -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-----

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/> <classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" path="src/test/java"/> <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes> <attributes>
@ -11,4 +11,3 @@
</classpathentry> </classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -2,6 +2,5 @@
<project-modules id="moduleCoreId" project-version="1.5.0"> <project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="openid-connect-common"> <wb-module deploy-name="openid-connect-common">
<wb-resource deploy-path="/" source-path="/src/main/java"/> <wb-resource deploy-path="/" source-path="/src/main/java"/>
<wb-resource deploy-path="/" source-path="/src/test/java"/>
</wb-module> </wb-module>
</project-modules> </project-modules>

View File

@ -15,12 +15,7 @@
******************************************************************************/ ******************************************************************************/
package org.mitre.jwt.model; package org.mitre.jwt.model;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;

View File

@ -17,7 +17,6 @@ package org.mitre.jwt.signer.service;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.mitre.jwt.model.Jwt; import org.mitre.jwt.model.Jwt;
@ -72,6 +71,39 @@ public interface JwtSigningAndValidationService {
* @return the signed jwt * @return the signed jwt
* @throws NoSuchAlgorithmException * @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; public void signJwt(Jwt jwt) throws NoSuchAlgorithmException;
/** /**

View File

@ -51,4 +51,35 @@ public abstract class AbstractJwtSigningAndValidationService implements JwtSigni
return false; 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;
}
}
} }

View File

@ -5,6 +5,7 @@ import java.net.URL;
import java.security.Key; import java.security.Key;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -33,9 +34,6 @@ public class DynamicJwtSigningAndValidationService extends AbstractJwtSigningAnd
private Map<String, ? extends JwtSigner> signers; private Map<String, ? extends JwtSigner> signers;
private String signingAlgorithm;
public DynamicJwtSigningAndValidationService(String x509SigningUrl, String jwkSigningUrl, String clientSecret) throws Exception { public DynamicJwtSigningAndValidationService(String x509SigningUrl, String jwkSigningUrl, String clientSecret) throws Exception {
setX509SigningUrl(x509SigningUrl); setX509SigningUrl(x509SigningUrl);
setJwkSigningUrl(jwkSigningUrl); setJwkSigningUrl(jwkSigningUrl);
@ -143,4 +141,35 @@ public class DynamicJwtSigningAndValidationService extends AbstractJwtSigningAnd
return signer; 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;
}
}
} }

View File

@ -17,7 +17,7 @@ package org.mitre.jwt.signer.service.impl;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -163,4 +163,35 @@ public class JwtSigningAndValidationServiceDefault extends AbstractJwtSigningAnd
public Map<String, ? extends JwtSigner> getSigners() { public Map<String, ? extends JwtSigner> getSigners() {
return signers; 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;
}
}
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.mitre.openid.connect.token; package org.mitre.openid.connect.token;
import java.security.NoSuchAlgorithmException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -30,6 +31,7 @@ import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.model.IdToken; import org.mitre.openid.connect.model.IdToken;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication; 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.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
@ -112,10 +114,13 @@ public class ConnectAuthCodeTokenGranter implements TokenGranter {
* @param parameters * @param parameters
* @param clientId * @param clientId
* @param scope * @param scope
* @throws NoSuchAlgorithmException
* @throws AuthenticationException
* @throws InvalidGrantException
*/ */
@Override @Override
public OAuth2AccessToken grant(String grantType, public OAuth2AccessToken grant(String grantType,
Map<String, String> parameters, String clientId, Set<String> scope) { Map<String, String> parameters, String clientId, Set<String> scope) throws NoSuchAlgorithmException, InvalidGrantException, AuthenticationException {
if (!GRANT_TYPE.equals(grantType)) { if (!GRANT_TYPE.equals(grantType)) {
return null; return null;

View File

@ -15,6 +15,8 @@
******************************************************************************/ ******************************************************************************/
package org.mitre.openid.connect.web; package org.mitre.openid.connect.web;
import java.security.NoSuchAlgorithmException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
@ -44,7 +46,7 @@ public class CheckIDEndpoint {
@PreAuthorize("hasRole('ROLE_USER')") @PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping("/checkid") @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(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

View File

@ -21,6 +21,7 @@ import static org.junit.Assert.assertThat;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@ -198,7 +199,7 @@ public class JwtTest {
} }
@Test @Test
public void testToStringPlaintext() { public void testToStringPlaintext() throws NoSuchAlgorithmException {
Jwt jwt = new Jwt(); Jwt jwt = new Jwt();
jwt.getHeader().setAlgorithm("none"); jwt.getHeader().setAlgorithm("none");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L)); jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));