Merge branch 'master' of github.com:jricher/OpenID-Connect-Java-Spring-Server

pull/59/head
Justin Richer 2012-02-14 16:07:21 -05:00
commit 01763dbc0c
12 changed files with 623 additions and 387 deletions

11
.gitignore vendored
View File

@ -2,4 +2,13 @@
/target /target
*~ *~
/bin /bin
*.idea
*.gitignore
.gitignore
OpenIDConnect.iml
server/openid.iml
.gitignore

View File

@ -1,75 +1,75 @@
package org.mitre.jwt.signer; package org.mitre.jwt.signer;
import java.util.List; import java.util.List;
import org.mitre.jwt.model.Jwt; import org.mitre.jwt.model.Jwt;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public abstract class AbstractJwtSigner implements JwtSigner { public abstract class AbstractJwtSigner implements JwtSigner {
private String algorithm; private String algorithm;
public AbstractJwtSigner(String algorithm) { public AbstractJwtSigner(String algorithm) {
this.algorithm = algorithm; this.algorithm = algorithm;
} }
/** /**
* @return the algorithm * @return the algorithm
*/ */
public String getAlgorithm() { public String getAlgorithm() {
return algorithm; return algorithm;
} }
/** /**
* @param algorithm the algorithm to set * @param algorithm the algorithm to set
*/ */
public void setAlgorithm(String algorithm) { public void setAlgorithm(String algorithm) {
this.algorithm = algorithm; this.algorithm = algorithm;
} }
/** /**
* Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer * Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer
*/ */
@Override @Override
public void sign(Jwt jwt) { public void sign(Jwt jwt) {
if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) { if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) {
// algorithm type doesn't match // algorithm type doesn't match
// TODO: should this be an error or should we just fix it in the incoming jwt? // TODO: should this be an error or should we just fix it in the incoming jwt?
// for now, we fix the Jwt // for now, we fix the Jwt
jwt.getHeader().setAlgorithm(algorithm); jwt.getHeader().setAlgorithm(algorithm);
} }
String sig = generateSignature(jwt.getSignatureBase()); String sig = generateSignature(jwt.getSignatureBase());
jwt.setSignature(sig); jwt.setSignature(sig);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.mitre.jwt.JwtSigner#verify(java.lang.String) * @see org.mitre.jwt.JwtSigner#verify(java.lang.String)
*/ */
@Override @Override
public boolean verify(String jwtString) { public boolean verify(String jwtString) {
// split on the dots // split on the dots
List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString)); List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString));
if (parts.size() != 3) { if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid JWT format."); throw new IllegalArgumentException("Invalid JWT format.");
} }
String h64 = parts.get(0); String h64 = parts.get(0);
String c64 = parts.get(1); String c64 = parts.get(1);
String s64 = parts.get(2); String s64 = parts.get(2);
String expectedSignature = generateSignature(h64 + "." + c64 + "."); String expectedSignature = generateSignature(h64 + "." + c64 + ".");
return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature)); return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature));
} }
protected abstract String generateSignature(String signatureBase); protected abstract String generateSignature(String signatureBase);
} }

View File

@ -1,18 +1,18 @@
package org.mitre.jwt.signer.impl; package org.mitre.jwt.signer.impl;
import org.mitre.jwt.signer.AbstractJwtSigner; import org.mitre.jwt.signer.AbstractJwtSigner;
public class PlaintextSigner extends AbstractJwtSigner { public class PlaintextSigner extends AbstractJwtSigner {
public static final String PLAINTEXT = "none"; public static final String PLAINTEXT = "none";
public PlaintextSigner() { public PlaintextSigner() {
super(PLAINTEXT); super(PLAINTEXT);
} }
@Override @Override
protected String generateSignature(String signatureBase) { protected String generateSignature(String signatureBase) {
return null; return null;
} }
} }

View File

@ -1,56 +1,56 @@
package org.mitre.openid.connect.view; package org.mitre.openid.connect.view;
import java.io.Writer; import java.io.Writer;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.servlet.view.AbstractView; import org.springframework.web.servlet.view.AbstractView;
import com.google.gson.ExclusionStrategy; import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes; import com.google.gson.FieldAttributes;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
public class JSONIdTokenView extends AbstractView { public class JSONIdTokenView extends AbstractView {
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) * @see org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/ */
protected void renderMergedOutputModel(Map<String, Object> model, protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) HttpServletRequest request, HttpServletResponse response)
throws Exception { throws Exception {
Gson gson = new GsonBuilder() Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() { .setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipField(FieldAttributes f) { public boolean shouldSkipField(FieldAttributes f) {
return false; return false;
} }
public boolean shouldSkipClass(Class<?> clazz) { public boolean shouldSkipClass(Class<?> clazz) {
// skip the JPA binding wrapper // skip the JPA binding wrapper
if (clazz.equals(BeanPropertyBindingResult.class)) { if (clazz.equals(BeanPropertyBindingResult.class)) {
return true; return true;
} }
return false; return false;
} }
}).create(); }).create();
response.setContentType("application/json"); response.setContentType("application/json");
Writer out = response.getWriter(); Writer out = response.getWriter();
Object obj = model.get("entity"); Object obj = model.get("entity");
if (obj == null) { if (obj == null) {
obj = model; obj = model;
} }
gson.toJson(obj, out); gson.toJson(obj, out);
} }
} }

View File

@ -1,56 +1,56 @@
package org.mitre.openid.connect.view; package org.mitre.openid.connect.view;
import java.io.Writer; import java.io.Writer;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.servlet.view.AbstractView; import org.springframework.web.servlet.view.AbstractView;
import com.google.gson.ExclusionStrategy; import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes; import com.google.gson.FieldAttributes;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
public class JSONUserInfoView extends AbstractView{ public class JSONUserInfoView extends AbstractView{
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) * @see org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/ */
protected void renderMergedOutputModel(Map<String, Object> model, protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) HttpServletRequest request, HttpServletResponse response)
throws Exception { throws Exception {
Gson gson = new GsonBuilder() Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() { .setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipField(FieldAttributes f) { public boolean shouldSkipField(FieldAttributes f) {
return false; return false;
} }
public boolean shouldSkipClass(Class<?> clazz) { public boolean shouldSkipClass(Class<?> clazz) {
// skip the JPA binding wrapper // skip the JPA binding wrapper
if (clazz.equals(BeanPropertyBindingResult.class)) { if (clazz.equals(BeanPropertyBindingResult.class)) {
return true; return true;
} }
return false; return false;
} }
}).create(); }).create();
response.setContentType("application/json"); response.setContentType("application/json");
Writer out = response.getWriter(); Writer out = response.getWriter();
Object obj = model.get("entity"); Object obj = model.get("entity");
if (obj == null) { if (obj == null) {
obj = model; obj = model;
} }
gson.toJson(obj, out); gson.toJson(obj, out);
} }
} }

View File

@ -0,0 +1,4 @@
<ul class="breadcrumb">
<li><a href="#">Home</a> <span class="divider">/</span></li>
<li class="active">Manage Clients</li>
</ul>

View File

@ -0,0 +1,2 @@
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OpenID Connect</title>
<meta name="description" content="">
<meta name="author" content="">
<!-- Le javascript -->
<script src="http://code.jquery.com/jquery-1.7.min.js"></script>
<script src="../bootstrap/js/bootstrap-modal.js"></script>
<script src="../bootstrap/js/bootstrap-alerts.js"></script>
<script src="../bootstrap/js/bootstrap-twipsy.js"></script>
<script src="../bootstrap/js/bootstrap-popover.js"></script>
<script src="../bootstrap/js/bootstrap-dropdown.js"></script>
<script src="../bootstrap/js/bootstrap-scrollspy.js"></script>
<script src="../bootstrap/js/bootstrap-tabs.js"></script>
<script src="../bootstrap/js/bootstrap-buttons.js"></script>
<script>$(function () {
})</script>
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le styles -->
<link href="../bootstrap/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 60px;
}
.logo {
background: url("../images/openid_small.png") no-repeat left center;
padding-left: 30px;
}
</style>
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
</head>
<body>

View File

@ -0,0 +1,16 @@
<div class="sidebar">
<div class="well">
<h5>Administrative</h5>
<ul>
<li><a href="#">Manage Clients</a></li>
<li><a href="#">White Lists</a></li>
<li><a href="#">Black Lists</a></li>
</ul>
<h5>Personal</h5>
<ul>
<li><a href="#">Manage Sites</a></li>
<li><a href="#">Manage Active Tokens</a></li>
<li><a href="#">Profile</a></li>
</ul>
</div>
</div>

View File

@ -0,0 +1,14 @@
<div class="topbar">
<div class="topbar-inner">
<div class="container-fluid">
<a class="brand" href="#"><span class="logo">OpenID Connect Server</span></a>
<ul class="nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#about">Statistics</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<p class="pull-right">Logged in as <a href="#">username</a></p>
</div>
</div>
</div>

View File

@ -6,6 +6,21 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<!-- Le javascript -->
<script src="http://code.jquery.com/jquery-1.7.min.js"></script>
<script src="../bootstrap/js/bootstrap-modal.js"></script>
<script src="../bootstrap/js/bootstrap-alerts.js"></script>
<script src="../bootstrap/js/bootstrap-twipsy.js"></script>
<script src="../bootstrap/js/bootstrap-popover.js"></script>
<script src="../bootstrap/js/bootstrap-dropdown.js"></script>
<script src="../bootstrap/js/bootstrap-scrollspy.js"></script>
<script src="../bootstrap/js/bootstrap-tabs.js"></script>
<script src="../bootstrap/js/bootstrap-buttons.js"></script>
<script>$(function () {
})</script>
<!-- Le HTML5 shim, for IE6-8 support of HTML elements --> <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
@ -33,6 +48,126 @@
<body> <body>
<div id="modal-from-dom" class="modal hide fade" style="width: 577px; max-height: none; top: 35%">
<div class="modal-header">
<a href="#" class="close">&times;</a>
<h3>Edit Client</h3>
</div>
<div class="modal-body">
<form>
<fieldset>
<legend>OpenID Client</legend>
<div class="clearfix">
<label for="xlInput">Client Name</label>
<div class="input">
<input type="text" size="30" name="xlInput" id="xlInput" class="xlarge">
</div>
</div>
<div class="clearfix">
<label for="prependedInput">Redirect Url</label>
<div class="input">
<div class="input-prepend">
<span class="add-on">http://</span>
<input type="text" size="16" name="prependedInput" id="prependedInput" class="medium">
</div>
<span class="help-block">Url to be redirected</span>
</div>
</div>
<div class="clearfix">
<label id="optionsCheckboxes">Grant Types:</label>
<div class="input">
<ul class="inputs-list">
<li>
<label>
<input type="checkbox" value="option1" name="optionsCheckboxes">
<span>Grant Type Blah</span>
</label>
</li>
<li>
<label>
<input type="checkbox" value="option2" name="optionsCheckboxes">
<span>Grant Type Blah</span>
</label>
</li>
<li>
<label>
<input type="checkbox" value="option2" name="optionsCheckboxes">
<span>Grant Type Blah</span>
</label>
</li>
</ul>
<span class="help-block">
<strong>Note:</strong> Grant type help text.
</span>
</div>
</div>
<div class="clearfix">
<label for="textarea2">Scope</label>
<div class="input">
<textarea rows="3" name="textarea2" id="textarea2" class="xlarge">email,first name</textarea>
<span class="help-block">
Please enter scopes separated by commas
</span>
</div>
</div>
<div class="clearfix">
<label for="normalSelect">Authority</label>
<div class="input">
<select id="normalSelect" name="normalSelect">
<option>My Authority Option 1</option>
<option>My Authority Option 2</option>
</select>
</div>
</div>
</fieldset>
<div class="clearfix">
<label for="form-description">Description</label>
<div class="input">
<input type="text" size="30" name="form-description" id="form-description" class="xlarge">
</div>
</div>
<div class="clearfix">
<label id="form-allow-tokens">Allow refresh tokens?</label>
<div class="input">
<ul class="inputs-list">
<li>
<label>
<input type="checkbox" value="option1" name="form-allow-tokens">
<span>&nbsp;</span>
</label>
</li>
</ul>
<span class="help-block">
<strong>Note:</strong> Labels surround all the options for much larger click areas and a more usable form.
</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<a href="#" class="btn primary">Save</a>
<a href="#" class="btn secondary">Cancel</a>
</div>
</div>
<div class="topbar"> <div class="topbar">
<div class="topbar-inner"> <div class="topbar-inner">
<div class="container-fluid"> <div class="container-fluid">
@ -66,7 +201,6 @@
</div> </div>
</div> </div>
<div class="content"> <div class="content">
<!-- Main hero unit for a primary marketing message or call to action -->
<div class=""> <div class="">
<ul class="breadcrumb"> <ul class="breadcrumb">
<li><a href="#">Home</a> <span class="divider">/</span></li> <li><a href="#">Home</a> <span class="divider">/</span></li>
@ -120,7 +254,9 @@
</td> </td>
<td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td> <td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td>
<td> <td>
<button class="btn">edit</button> <button data-controls-modal="modal-from-dom" data-backdrop="true" data-keyboard="true"
class="btn">edit
</button>
</td> </td>
<td> <td>
<button class="btn danger">delete</button> <button class="btn danger">delete</button>
@ -151,7 +287,9 @@
</td> </td>
<td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td> <td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td>
<td> <td>
<button class="btn">edit</button> <button data-controls-modal="modal-from-dom" data-backdrop="true" data-keyboard="true"
class="btn">edit
</button>
</td> </td>
<td> <td>
<button class="btn danger">delete</button> <button class="btn danger">delete</button>
@ -182,7 +320,9 @@
</td> </td>
<td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td> <td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td>
<td> <td>
<button class="btn">edit</button> <button data-controls-modal="modal-from-dom" data-backdrop="true" data-keyboard="true"
class="btn">edit
</button>
</td> </td>
<td> <td>
<button class="btn danger">delete</button> <button class="btn danger">delete</button>
@ -213,7 +353,9 @@
</td> </td>
<td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td> <td><input type="checkbox" checked="checked" value="" id="" name="" disabled></td>
<td> <td>
<button class="btn">edit</button> <button data-controls-modal="modal-from-dom" data-backdrop="true" data-keyboard="true"
class="btn">edit
</button>
</td> </td>
<td> <td>
<button class="btn danger">delete</button> <button class="btn danger">delete</button>

View File

@ -1,177 +1,177 @@
package org.mitre.jwt; package org.mitre.jwt;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mitre.jwt.model.Jwt; import org.mitre.jwt.model.Jwt;
import org.mitre.jwt.signer.JwtSigner; import org.mitre.jwt.signer.JwtSigner;
import org.mitre.jwt.signer.impl.HmacSigner; import org.mitre.jwt.signer.impl.HmacSigner;
import org.mitre.jwt.signer.impl.PlaintextSigner; import org.mitre.jwt.signer.impl.PlaintextSigner;
import org.mitre.jwt.signer.impl.RsaSigner; import org.mitre.jwt.signer.impl.RsaSigner;
import org.mitre.jwt.signer.service.impl.KeyStore; import org.mitre.jwt.signer.service.impl.KeyStore;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { @ContextConfiguration(locations = {
"file:src/main/webapp/WEB-INF/spring/application-context.xml", "file:src/main/webapp/WEB-INF/spring/application-context.xml",
"classpath:test-context.xml" }) "classpath:test-context.xml" })
public class JwtTest { public class JwtTest {
@Autowired @Autowired
@Qualifier("testKeystore") @Qualifier("testKeystore")
KeyStore keystore; KeyStore keystore;
@Test @Test
public void testToStringPlaintext() { public void testToStringPlaintext() {
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));
jwt.getClaims().setIssuer("joe"); jwt.getClaims().setIssuer("joe");
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
// sign it with a blank signature // sign it with a blank signature
JwtSigner signer = new PlaintextSigner(); JwtSigner signer = new PlaintextSigner();
signer.sign(jwt); signer.sign(jwt);
/* /*
* Expected string based on the following structures, serialized exactly as follows and base64 encoded: * Expected string based on the following structures, serialized exactly as follows and base64 encoded:
* *
* header: {"alg":"none"} * header: {"alg":"none"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true} * claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*/ */
String expected = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."; String expected = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
String actual = jwt.toString(); String actual = jwt.toString();
assertThat(actual, equalTo(expected)); assertThat(actual, equalTo(expected));
} }
@Test @Test
public void testGenerateHmacSignature() { public void testGenerateHmacSignature() {
Jwt jwt = new Jwt(); Jwt jwt = new Jwt();
jwt.getHeader().setType("JWT"); jwt.getHeader().setType("JWT");
jwt.getHeader().setAlgorithm("HS256"); jwt.getHeader().setAlgorithm("HS256");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L)); jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
jwt.getClaims().setIssuer("joe"); jwt.getClaims().setIssuer("joe");
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
// sign it // sign it
byte[] key = null; byte[] key = null;
try { try {
key = "secret".getBytes("UTF-8"); key = "secret".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
JwtSigner signer = new HmacSigner(key); JwtSigner signer = new HmacSigner(key);
signer.sign(jwt); signer.sign(jwt);
/* /*
* Expected string based on the following structures, serialized exactly as follows and base64 encoded: * Expected string based on the following structures, serialized exactly as follows and base64 encoded:
* *
* header: {"typ":"JWT","alg":"HS256"} * header: {"typ":"JWT","alg":"HS256"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true} * claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
* *
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E * Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
* *
*/ */
String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y"; String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y";
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature; String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature;
String actual = jwt.toString(); String actual = jwt.toString();
assertThat(actual, equalTo(expected)); assertThat(actual, equalTo(expected));
assertThat(jwt.getSignature(), equalTo(signature)); assertThat(jwt.getSignature(), equalTo(signature));
} }
/** /**
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testGenerateRsaSignature() throws Exception { public void testGenerateRsaSignature() throws Exception {
// java.security.KeyStore ks = KeyStore.generateRsaKeyPair(keystore // java.security.KeyStore ks = KeyStore.generateRsaKeyPair(keystore
// .getLocation().getFile().getPath(), "OpenID Connect Server", // .getLocation().getFile().getPath(), "OpenID Connect Server",
// "twentyYears", KeyStore.PASSWORD, KeyStore.PASSWORD, 30, 365*20); // "twentyYears", KeyStore.PASSWORD, KeyStore.PASSWORD, 30, 365*20);
// //
// keystore.setKeystore(ks); // keystore.setKeystore(ks);
Jwt jwt = new Jwt(); Jwt jwt = new Jwt();
jwt.getHeader().setType("JWT"); jwt.getHeader().setType("JWT");
jwt.getHeader().setAlgorithm("RS256"); jwt.getHeader().setAlgorithm("RS256");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L)); jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
jwt.getClaims().setIssuer("joe"); jwt.getClaims().setIssuer("joe");
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
JwtSigner signer = new RsaSigner(RsaSigner.Algorithm.DEFAULT, keystore, "twentyYears"); JwtSigner signer = new RsaSigner(RsaSigner.Algorithm.DEFAULT, keystore, "twentyYears");
((RsaSigner) signer).afterPropertiesSet(); ((RsaSigner) signer).afterPropertiesSet();
signer.sign(jwt); signer.sign(jwt);
String signature = "TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg"; String signature = "TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg";
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg"; String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg";
String actual = jwt.toString(); String actual = jwt.toString();
assertThat(actual, equalTo(expected)); assertThat(actual, equalTo(expected));
assertThat(jwt.getSignature(), equalTo(signature)); assertThat(jwt.getSignature(), equalTo(signature));
} }
@Test @Test
public void testValidateHmacSignature() { public void testValidateHmacSignature() {
// sign it // sign it
byte[] key = null; byte[] key = null;
try { try {
key = "secret".getBytes("UTF-8"); key = "secret".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
JwtSigner signer = new HmacSigner(key); JwtSigner signer = new HmacSigner(key);
/* /*
* Token string based on the following strucutres, serialized exactly as follows and base64 encoded: * Token string based on the following strucutres, serialized exactly as follows and base64 encoded:
* *
* header: {"typ":"JWT","alg":"HS256"} * header: {"typ":"JWT","alg":"HS256"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true} * claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
* *
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E * Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
* *
*/ */
String jwtString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E"; String jwtString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E";
boolean valid = signer.verify(jwtString); boolean valid = signer.verify(jwtString);
assertThat(valid, equalTo(Boolean.TRUE)); assertThat(valid, equalTo(Boolean.TRUE));
} }
@Test @Test
public void testParse() { public void testParse() {
String source = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."; String source = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
Jwt jwt = Jwt.parse(source); Jwt jwt = Jwt.parse(source);
assertThat(jwt.getHeader().getAlgorithm(), equalTo(PlaintextSigner.PLAINTEXT)); assertThat(jwt.getHeader().getAlgorithm(), equalTo(PlaintextSigner.PLAINTEXT));
assertThat(jwt.getClaims().getIssuer(), equalTo("joe")); assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L))); assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE)); assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE));
} }
} }