Merge pull request #4083 from knutz3n/KEYCLOAK-4780
KEYCLOAK-4780 Ensure Base64 encoded HMAC secret key is decoded before use
This commit is contained in:
commit
cc27dd485b
8 changed files with 39 additions and 12 deletions
|
@ -40,8 +40,8 @@ public class KeyUtils {
|
||||||
private KeyUtils() {
|
private KeyUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SecretKey loadSecretKey(String secret) {
|
public static SecretKey loadSecretKey(byte[] secret) {
|
||||||
return new SecretKeySpec(secret.getBytes(), "HmacSHA256");
|
return new SecretKeySpec(secret, "HmacSHA256");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KeyPair generateRsaKeyPair(int keysize) {
|
public static KeyPair generateRsaKeyPair(int keysize) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class KeyUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadSecretKey() throws Exception {
|
||||||
|
byte[] secretBytes = new byte[32];
|
||||||
|
ThreadLocalRandom.current().nextBytes(secretBytes);
|
||||||
|
SecretKeySpec expected = new SecretKeySpec(secretBytes, "HmacSHA256");
|
||||||
|
SecretKey actual = KeyUtils.loadSecretKey(secretBytes);
|
||||||
|
assertEquals(expected.getAlgorithm(), actual.getAlgorithm());
|
||||||
|
assertArrayEquals(expected.getEncoded(), actual.getEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,7 +19,6 @@ package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||||
import org.keycloak.common.util.Base64Url;
|
|
||||||
import org.keycloak.common.util.CertificateUtils;
|
import org.keycloak.common.util.CertificateUtils;
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.common.util.PemUtils;
|
import org.keycloak.common.util.PemUtils;
|
||||||
|
@ -75,14 +74,14 @@ public final class KeycloakModelUtils {
|
||||||
return UUID.randomUUID().toString();
|
return UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String generateSecret() {
|
public static byte[] generateSecret() {
|
||||||
return generateSecret(32);
|
return generateSecret(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String generateSecret(int bytes) {
|
public static byte[] generateSecret(int bytes) {
|
||||||
byte[] buf = new byte[bytes];
|
byte[] buf = new byte[bytes];
|
||||||
new SecureRandom().nextBytes(buf);
|
new SecureRandom().nextBytes(buf);
|
||||||
return Base64Url.encode(buf);
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PublicKey getPublicKey(String publicKeyPem) {
|
public static PublicKey getPublicKey(String publicKeyPem) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.keycloak.services.managers;
|
package org.keycloak.services.managers;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
@ -227,7 +228,7 @@ public class ClientSessionCode {
|
||||||
|
|
||||||
private static String generateCode(ClientSessionModel clientSession) {
|
private static String generateCode(ClientSessionModel clientSession) {
|
||||||
try {
|
try {
|
||||||
String actionId = KeycloakModelUtils.generateSecret();
|
String actionId = Base64Url.encode(KeycloakModelUtils.generateSecret());
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(actionId);
|
sb.append(actionId);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.keys;
|
package org.keycloak.keys;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.jose.jws.AlgorithmType;
|
import org.keycloak.jose.jws.AlgorithmType;
|
||||||
|
@ -47,7 +48,7 @@ public class GeneratedHmacKeyProvider implements HmacKeyProvider {
|
||||||
if (model.hasNote(SecretKey.class.getName())) {
|
if (model.hasNote(SecretKey.class.getName())) {
|
||||||
secretKey = model.getNote(SecretKey.class.getName());
|
secretKey = model.getNote(SecretKey.class.getName());
|
||||||
} else {
|
} else {
|
||||||
secretKey = KeyUtils.loadSecretKey(model.get(Attributes.SECRET_KEY));
|
secretKey = KeyUtils.loadSecretKey(Base64Url.decode(model.get(Attributes.SECRET_KEY)));
|
||||||
model.setNote(SecretKey.class.getName(), secretKey);
|
model.setNote(SecretKey.class.getName(), secretKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,8 @@ public class GeneratedHmacKeyProviderFactory extends AbstractHmacKeyProviderFact
|
||||||
|
|
||||||
private void generateSecret(ComponentModel model, int size) {
|
private void generateSecret(ComponentModel model, int size) {
|
||||||
try {
|
try {
|
||||||
String secret = KeycloakModelUtils.generateSecret(size);
|
byte[] secret = KeycloakModelUtils.generateSecret(size);
|
||||||
model.put(Attributes.SECRET_KEY, secret);
|
model.put(Attributes.SECRET_KEY, Base64Url.encode(secret));
|
||||||
|
|
||||||
String kid = KeycloakModelUtils.generateId();
|
String kid = KeycloakModelUtils.generateId();
|
||||||
model.put(Attributes.KID_KEY, kid);
|
model.put(Attributes.KID_KEY, kid);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.AbstractOAuthClient;
|
import org.keycloak.AbstractOAuthClient;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
import org.keycloak.common.util.UriUtils;
|
import org.keycloak.common.util.UriUtils;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -133,7 +134,7 @@ public abstract class AbstractSecuredLocalService {
|
||||||
if (cookie != null) {
|
if (cookie != null) {
|
||||||
stateChecker = cookie.getValue();
|
stateChecker = cookie.getValue();
|
||||||
} else {
|
} else {
|
||||||
stateChecker = KeycloakModelUtils.generateSecret();
|
stateChecker = Base64Url.encode(KeycloakModelUtils.generateSecret());
|
||||||
String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
|
String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
|
||||||
boolean secureOnly = realm.getSslRequired().isRequired(clientConnection);
|
boolean secureOnly = realm.getSslRequired().isRequired(clientConnection);
|
||||||
CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
|
CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.services.resources;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.common.util.MimeTypeUtil;
|
import org.keycloak.common.util.MimeTypeUtil;
|
||||||
import org.keycloak.models.BrowserSecurityHeaders;
|
import org.keycloak.models.BrowserSecurityHeaders;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -246,7 +247,7 @@ public class WelcomeResource {
|
||||||
if (stateChecker != null) {
|
if (stateChecker != null) {
|
||||||
return stateChecker;
|
return stateChecker;
|
||||||
} else {
|
} else {
|
||||||
stateChecker = KeycloakModelUtils.generateSecret();
|
stateChecker = Base64Url.encode(KeycloakModelUtils.generateSecret());
|
||||||
String cookiePath = uriInfo.getPath();
|
String cookiePath = uriInfo.getPath();
|
||||||
boolean secureOnly = uriInfo.getRequestUri().getScheme().equalsIgnoreCase("https");
|
boolean secureOnly = uriInfo.getRequestUri().getScheme().equalsIgnoreCase("https");
|
||||||
CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
|
CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true);
|
||||||
|
|
Loading…
Reference in a new issue