merge from pull request, plus cleanup
commit
b86abdd761
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<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/java"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/test/java"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/test/resources"/>
|
||||
</wb-module>
|
||||
</project-modules>
|
||||
|
|
|
@ -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>
|
|
@ -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<String> paramsToBeSanatized;
|
||||
|
||||
|
@ -109,8 +118,7 @@ public class AbstractOIDCAuthenticationFilter extends
|
|||
|
||||
public Enumeration<String> getParameterNames() {
|
||||
|
||||
ArrayList<String> paramNames = Collections.list(super
|
||||
.getParameterNames());
|
||||
ArrayList<String> 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<String, String> queryStringFields) {
|
||||
public static String buildURL(String baseURI, Map<String, String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -133,7 +133,7 @@ public class Jwt {
|
|||
return h64 + "." + c64;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse a wire-encoded JWT
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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<String> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String, ? extends JwtSigner> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, PublicKey> map;
|
||||
|
||||
private PublicKey publicKey;
|
||||
|
||||
private Map<String, ? extends JwtSigner> 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<String, PublicKey> 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<String, ? extends JwtSigner> getSigners() {
|
||||
// TODO Auto-generated method stub
|
||||
signers = new HashMap<String, JwtSigner>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String, ? extends JwtSigner> signers = new HashMap<String, JwtSigner>();
|
||||
Map<String, ? extends JwtSigner> signers = new HashMap<String, JwtSigner>();
|
||||
|
||||
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<String, ? extends JwtSigner> 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<String, ? extends JwtSigner> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Jwk> retrieveJwk(URL path) throws Exception {
|
||||
List<Jwk> keys = new ArrayList<Jwk>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Jwk> 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<Jwk> 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<Jwk> 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");
|
||||
//}
|
||||
|
||||
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{"iss":"joe",
|
||||
"user_id":34252452623,
|
||||
"aud":"yolo",
|
||||
"exp":35324583457247,
|
||||
"iat":43215325235,
|
||||
"nonce":"howdy"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"HS256"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"HS384"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"HS512"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"none"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"RS256"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"RS384"}
|
|
@ -0,0 +1,2 @@
|
|||
{"typ":"JWT",
|
||||
"alg":"RS512"}
|
|
@ -0,0 +1,6 @@
|
|||
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
|
||||
.
|
||||
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
|
||||
cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
|
||||
.
|
||||
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
|
|
@ -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-----
|
|
@ -5,6 +5,12 @@
|
|||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||
<dependent-module archiveName="spring-security-oauth2-1.0.0.BUILD-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/spring-security-oauth2/spring-security-oauth2">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<dependent-module archiveName="openid-connect-common-0.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/openid-connect-common/openid-connect-common">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<property name="java-output-path" value="/openid/target/classes"/>
|
||||
<property name="context-root" value="openid-connect-server"/>
|
||||
</wb-module>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue