Skip to content

Commit

Permalink
KEYCLOAK-7560 Refactor token signature SPI PR
Browse files Browse the repository at this point in the history
Also incorporates:
KEYCLOAK-6770 ES256/384/512 providers
KEYCLOAK-4622 Use HS256 for refresh tokens
KEYCLOAK-4623 Use HS256 for client reg tokens
  • Loading branch information
stianst committed Sep 11, 2018
1 parent 5b60365 commit 24e6074
Show file tree
Hide file tree
Showing 190 changed files with 3,231 additions and 2,067 deletions.
1 change: 1 addition & 0 deletions core/src/main/java/org/keycloak/RSATokenVerifier.java
Expand Up @@ -27,6 +27,7 @@
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Deprecated
public class RSATokenVerifier {

private final TokenVerifier<AccessToken> tokenVerifier;
Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/org/keycloak/Token.java
@@ -0,0 +1,26 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak;

import com.fasterxml.jackson.annotation.JsonIgnore;

public interface Token {

@JsonIgnore
TokenCategory getCategory();

}
25 changes: 25 additions & 0 deletions core/src/main/java/org/keycloak/TokenCategory.java
@@ -0,0 +1,25 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak;

public enum TokenCategory {
INTERNAL,
ACCESS,
ID,
ADMIN,
USERINFO
}
79 changes: 36 additions & 43 deletions core/src/main/java/org/keycloak/TokenVerifier.java
Expand Up @@ -24,15 +24,14 @@
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.crypto.SignatureVerifierContext;
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;
Expand Down Expand Up @@ -147,15 +146,10 @@ public boolean test(JsonWebToken t) throws VerificationException {
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;
private SignatureVerifierContext verifier = null;

public TokenVerifier<T> verifierContext(SignatureVerifierContext verifier) {
this.verifier = verifier;
return this;
}

Expand Down Expand Up @@ -352,40 +346,39 @@ public JWSHeader getHeader() throws VerificationException {
}

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) {
throw new VerificationException("Unknown or unsupported token algorithm");
} else switch (algorithmType) {
case RSA:
if (publicKey == null) {
throw new VerificationException("Public key not set");
}
if (!RSAProvider.verify(jws, publicKey)) {
if (this.verifier != null) {
try {
if (!verifier.verify(jws.getEncodedSignatureInput().getBytes("UTF-8"), jws.getSignature())) {
throw new TokenSignatureInvalidException(token, "Invalid token signature");
} break;
case HMAC:
if (secretKey == null) {
throw new VerificationException("Secret key not set");
}
if (!HMACProvider.verify(jws, secretKey)) {
throw new TokenSignatureInvalidException(token, "Invalid token signature");
} break;
default:
throw new VerificationException("Unknown or unsupported token algorithm");
}
}
} catch (Exception e) {
throw new VerificationException(e);
}
} else {
AlgorithmType algorithmType = getHeader().getAlgorithm().getType();

// 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");
if (null == algorithmType) {
throw new VerificationException("Unknown or unsupported token algorithm");
} else switch (algorithmType) {
case RSA:
if (publicKey == null) {
throw new VerificationException("Public key not set");
}
if (!RSAProvider.verify(jws, publicKey)) {
throw new TokenSignatureInvalidException(token, "Invalid token signature");
}
break;
case HMAC:
if (secretKey == null) {
throw new VerificationException("Secret key not set");
}
if (!HMACProvider.verify(jws, secretKey)) {
throw new TokenSignatureInvalidException(token, "Invalid token signature");
}
break;
default:
throw new VerificationException("Unknown or unsupported token algorithm");
}
}
}

Expand Down Expand Up @@ -440,7 +433,7 @@ public boolean test(T t) throws VerificationException {
public static <T extends JsonWebToken> Predicate<T> alternative(final Predicate<? super T>... predicates) {
return new Predicate<T>() {
@Override
public boolean test(T t) throws VerificationException {
public boolean test(T t) {
for (Predicate<? super T> predicate : predicates) {
try {
if (predicate.test(t)) {
Expand Down
@@ -0,0 +1,52 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;

import java.security.PrivateKey;
import java.security.Signature;

public class AsymmetricSignatureSignerContext implements SignatureSignerContext {

private final KeyWrapper key;

public AsymmetricSignatureSignerContext(KeyWrapper key) throws SignatureException {
this.key = key;
}

@Override
public String getKid() {
return key.getKid();
}

@Override
public String getAlgorithm() {
return key.getAlgorithm();
}

@Override
public byte[] sign(byte[] data) throws SignatureException {
try {
Signature signature = Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(key.getAlgorithm()));
signature.initSign((PrivateKey) key.getSignKey());
signature.update(data);
return signature.sign();
} catch (Exception e) {
throw new SignatureException("Signing failed", e);
}
}

}
@@ -0,0 +1,54 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;

import org.keycloak.common.VerificationException;

import java.security.PublicKey;
import java.security.Signature;

public class AsymmetricSignatureVerifierContext implements SignatureVerifierContext {

private final KeyWrapper key;

public AsymmetricSignatureVerifierContext(KeyWrapper key) {
this.key = key;
}

@Override
public String getKid() {
return key.getKid();
}

@Override
public String getAlgorithm() {
return key.getAlgorithm();
}

@Override
public boolean verify(byte[] data, byte[] signature) throws VerificationException {
try {
Signature verifier = Signature.getInstance(JavaAlgorithm.getJavaAlgorithm(key.getAlgorithm()));
verifier.initVerify((PublicKey) key.getVerifyKey());
verifier.update(data);
return verifier.verify(signature);
} catch (Exception e) {
throw new VerificationException("Signing failed", e);
}
}

}
33 changes: 25 additions & 8 deletions core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java
Expand Up @@ -18,24 +18,41 @@

public class JavaAlgorithm {

public static final String RS256 = "SHA256withRSA";
public static final String RS384 = "SHA384withRSA";
public static final String RS512 = "SHA512withRSA";
public static final String HS256 = "HMACSHA256";
public static final String HS384 = "HMACSHA384";
public static final String HS512 = "HMACSHA512";
public static final String ES256 = "SHA256withECDSA";
public static final String ES384 = "SHA384withECDSA";
public static final String ES512 = "SHA512withECDSA";
public static final String AES = "AES";

public static String getJavaAlgorithm(String algorithm) {
switch (algorithm) {
case Algorithm.RS256:
return "SHA256withRSA";
return RS256;
case Algorithm.RS384:
return "SHA384withRSA";
return RS384;
case Algorithm.RS512:
return "SHA512withRSA";
return RS512;
case Algorithm.HS256:
return "HMACSHA256";
return HS256;
case Algorithm.HS384:
return "HMACSHA384";
return HS384;
case Algorithm.HS512:
return "HMACSHA512";
return HS512;
case Algorithm.ES256:
return ES256;
case Algorithm.ES384:
return ES384;
case Algorithm.ES512:
return ES512;
case Algorithm.AES:
return "AES";
return AES;
default:
throw new IllegalArgumentException("Unkown algorithm " + algorithm);
throw new IllegalArgumentException("Unknown algorithm " + algorithm);
}
}

Expand Down
21 changes: 5 additions & 16 deletions core/src/main/java/org/keycloak/crypto/KeyWrapper.java
Expand Up @@ -19,17 +19,13 @@
import javax.crypto.SecretKey;
import java.security.Key;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class KeyWrapper {

private String providerId;
private long providerPriority;
private String kid;
private Set<String> algorithms;
private String algorithm;
private String type;
private KeyUse use;
private KeyStatus status;
Expand Down Expand Up @@ -62,19 +58,12 @@ public void setKid(String kid) {
this.kid = kid;
}

public Set<String> getAlgorithms() {
return algorithms;
public String getAlgorithm() {
return algorithm;
}

public void setAlgorithms(String... algorithms) {
this.algorithms = new HashSet<>();
for (String a : algorithms) {
this.algorithms.add(a);
}
}

public void setAlgorithms(Set<String> algorithms) {
this.algorithms = algorithms;
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}

public String getType() {
Expand Down

0 comments on commit 24e6074

Please sign in to comment.