KEYCLOAK-7560 Refactor Token Sign and Verify by Token Signature SPI

This commit is contained in:
Takashi Norimatsu authored and Stian Thorgersen committed 2018-09-11 08:14:10 +02:00
1 parent bd4098191b
commit 5b6036525c
47 files changed
+1979 -83

No files matched your search

@@ -24,12 +24,15 @@ import org.keycloak.jose.jws.AlgorithmType;
import org.keycloak.jose.jws.JWSHeader;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.JWSSignatureProvider;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.util.TokenUtil;
import javax.crypto.SecretKey;
import java.security.Key;
import java.security.PublicKey;
import java.util.*;
import java.util.logging.Level;
@@ -144,6 +147,18 @@ public class TokenVerifier<T extends JsonWebToken> {
private JWSInput jws;
private T token;
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
private Key verifyKey = null;
private JWSSignatureProvider signatureProvider = null;
public TokenVerifier<T> verifyKey(Key verifyKey) {
this.verifyKey = verifyKey;
return this;
}
public TokenVerifier<T> signatureProvider(JWSSignatureProvider signatureProvider) {
this.signatureProvider = signatureProvider;
return this;
}
protected TokenVerifier(String tokenString, Class<T> clazz) {
this.tokenString = tokenString;
this.clazz = clazz;
@@ -337,6 +352,12 @@ public class TokenVerifier<T extends JsonWebToken> {
}
public void verifySignature() throws VerificationException {
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
if (this.signatureProvider != null && this.verify() != null) {
verifySignatureByProvider();
return;
}
AlgorithmType algorithmType = getHeader().getAlgorithm().getType();
if (null == algorithmType) {
@@ -361,6 +382,13 @@ public class TokenVerifier<T extends JsonWebToken> {
}
}
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
private void verifySignatureByProvider() throws VerificationException {
if (!signatureProvider.verify(jws, verifyKey)) {
throw new TokenSignatureInvalidException(token, "Invalid token signature");
}
}
public TokenVerifier<T> verify() throws VerificationException {
if (getToken() == null) {
parse();
@@ -25,6 +25,7 @@ import org.keycloak.util.JsonSerialization;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.PrivateKey;
/**
@@ -36,7 +37,7 @@ public class JWSBuilder {
String kid;
String contentType;
byte[] contentBytes;
public JWSBuilder type(String type) {
this.type = type;
return this;
@@ -66,10 +67,34 @@ public class JWSBuilder {
return new EncodingBuilder();
}
protected String encodeAll(StringBuffer encoding, byte[] signature) {
encoding.append('.');
if (signature != null) {
encoding.append(Base64Url.encode(signature));
}
return encoding.toString();
}
protected String encodeHeader(Algorithm alg) {
protected void encode(Algorithm alg, byte[] data, StringBuffer encoding) {
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
encode(alg.name(), data, encoding);
}
protected byte[] marshalContent() {
return contentBytes;
}
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
protected void encode(String sigAlgName, byte[] data, StringBuffer encoding) {
encoding.append(encodeHeader(sigAlgName));
encoding.append('.');
encoding.append(Base64Url.encode(data));
}
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
protected String encodeHeader(String sigAlgName) {
StringBuilder builder = new StringBuilder("{");
builder.append("\"alg\":\"").append(alg.toString()).append("\"");
builder.append("\"alg\":\"").append(sigAlgName).append("\"");
if (type != null) builder.append(",\"typ\" : \"").append(type).append("\"");
if (kid != null) builder.append(",\"kid\" : \"").append(kid).append("\"");
@@ -82,24 +107,6 @@ public class JWSBuilder {
}
}
protected String encodeAll(StringBuffer encoding, byte[] signature) {
encoding.append('.');
if (signature != null) {
encoding.append(Base64Url.encode(signature));
}
return encoding.toString();
}
protected void encode(Algorithm alg, byte[] data, StringBuffer encoding) {
encoding.append(encodeHeader(alg));
encoding.append('.');
encoding.append(Base64Url.encode(data));
}
protected byte[] marshalContent() {
return contentBytes;
}
public class EncodingBuilder {
public String none() {
StringBuffer buffer = new StringBuffer();
@@ -108,6 +115,20 @@ public class JWSBuilder {
return encodeAll(buffer, null);
}
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
public String sign(JWSSignatureProvider signatureProvider, String sigAlgName, Key key) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
encode(sigAlgName, data, buffer);
byte[] signature = null;
try {
signature = signatureProvider.sign(buffer.toString().getBytes("UTF-8"), sigAlgName, key);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return encodeAll(buffer, signature);
}
public String sign(Algorithm algorithm, PrivateKey privateKey) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
@@ -133,7 +154,6 @@ public class JWSBuilder {
return sign(Algorithm.RS512, privateKey);
}
public String hmac256(byte[] sharedSecret) {
StringBuffer buffer = new StringBuffer();
byte[] data = marshalContent();
@@ -0,0 +1,9 @@
package org.keycloak.jose.jws;
import java.security.Key;
public interface JWSSignatureProvider {
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
byte[] sign(byte[] data, String sigAlgName, Key key);
boolean verify(JWSInput input, Key key);
}
@@ -25,6 +25,7 @@ import org.keycloak.jose.jws.JWSInput;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -27,18 +27,16 @@ import java.util.Arrays;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HashProvider {
// See "at_hash" and "c_hash" in OIDC specification
public static String oidcHash(Algorithm jwtAlgorithm, String input) {
byte[] digest = digest(jwtAlgorithm, input);
// KEYCLOAK-7560 Refactoring Token Signing and Verifying by Token Signature SPI
public static String oidcHash(String jwtAlgorithmName, String input) {
byte[] digest = digest(jwtAlgorithmName, input);
int hashLength = digest.length / 2;
byte[] hashInput = Arrays.copyOf(digest, hashLength);
return Base64Url.encode(hashInput);
}
private static byte[] digest(Algorithm algorithm, String input) {
private static byte[] digest(String algorithm, String input) {
String digestAlg = getJavaDigestAlgorithm(algorithm);
try {
@@ -49,18 +47,22 @@ public class HashProvider {
throw new RuntimeException(e);
}
}
private static String getJavaDigestAlgorithm(Algorithm alg) {
private static String getJavaDigestAlgorithm(String alg) {
switch (alg) {
case RS256:
case "RS256":
return "SHA-256";
case RS384:
case "RS384":
return "SHA-384";
case RS512:
case "RS512":
return "SHA-512";
default:
throw new IllegalArgumentException("Not an RSA Algorithm");
}
}
// See "at_hash" and "c_hash" in OIDC specification
public static String oidcHash(Algorithm jwtAlgorithm, String input) {
return oidcHash(jwtAlgorithm.name(), input);
}
}