Skip to content

Commit

Permalink
Utilize signingKeyMap to add multiple signing keys
Browse files Browse the repository at this point in the history
[#114128115] https://www.pivotaltracker.com/story/show/114128115

Signed-off-by: Jonathan Lo <jlo@us.ibm.com>
  • Loading branch information
Priyata25 authored and jlo committed Mar 1, 2016
1 parent d708b6a commit 3d0aed2
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 53 deletions.
Expand Up @@ -15,6 +15,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1Sequence;
import org.cloudfoundry.identity.uaa.zone.SigningKeysMap;
import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
Expand Down Expand Up @@ -77,16 +78,18 @@ public String getRevocationHash(List<String> salts) {
* key. RSA keys should be in OpenSSH format,
* as produced by <tt>ssh-keygen</tt>.
*
* @param signingKey the key to be used for signing JWTs.
* @param keyMap a map of key id to signing key to be used for signing JWTs.
*/
public void addSigningKey(String keyId, String signingKey) {
KeyInfo keyInfo = new KeyInfo();
keyInfo.setKeyId(keyId);
keyInfo.setSigningKey(signingKey);
keys.put(keyId, keyInfo);

if(primaryKeyId == null) {
setPrimaryKeyId(keyId);
public void addSigningKeys(Map<String, String> keyMap) {
for(Map.Entry<String, String> entry : keyMap.entrySet()) {
KeyInfo keyInfo = new KeyInfo();
keyInfo.setKeyId(entry.getKey());
keyInfo.setSigningKey(entry.getValue());
keys.put(entry.getKey(), keyInfo);

if(primaryKeyId == null) {
setPrimaryKeyId(entry.getKey());
}
}
}

Expand Down
Expand Up @@ -198,7 +198,7 @@ public void setUp() {
UaaAuthenticationTestFactory.getAuthentication(userId, userName, "olds@vmware.com"));

signerProvider = new SignerProvider();
signerProvider.addSigningKey("testKey", signerKey);
signerProvider.addSigningKeys(Collections.singletonMap("testKey", signerKey));
tokenServices.setSignerProvider(signerProvider);
endpoint.setTokenServices(tokenServices);
Date oneSecondAgo = new Date(System.currentTimeMillis() - 1000);
Expand Down Expand Up @@ -268,7 +268,7 @@ public void testRejectInvalidIssuer() {

@Test(expected = InvalidTokenException.class)
public void testRejectInvalidVerifier() throws Exception {
signerProvider.addSigningKey("testKey", alternateSignerKey);
signerProvider.addSigningKeys(Collections.singletonMap("testKey", alternateSignerKey));
endpoint.checkToken(accessToken.getValue(), Collections.emptyList());
}

Expand Down Expand Up @@ -474,7 +474,7 @@ public void revokingAuthoritiesFromClients_invalidatesToken() throws Exception {

@Test
public void testSwitchVerifierKey() throws Exception {
signerProvider.addSigningKey("testKey", alternateSignerKey);
signerProvider.addSigningKeys(Collections.singletonMap("testKey", alternateSignerKey));
OAuth2AccessToken alternateToken = tokenServices.createAccessToken(authentication);
endpoint.checkToken(alternateToken.getValue(), Collections.emptyList());
try {
Expand Down
Expand Up @@ -69,7 +69,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.audience;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.cid;
Expand Down Expand Up @@ -136,7 +135,7 @@ public class UaaTokenServicesTests {

private static SignerProvider getNewSignerProvider() {
SignerProvider signerProvider = new SignerProvider();
signerProvider.addSigningKey("testKey", "9c247h8yt978w3nv45y978w45hntv6");
signerProvider.addSigningKeys(Collections.singletonMap("testKey", "9c247h8yt978w3nv45y978w45hntv6"));
return signerProvider;
}

Expand Down
Expand Up @@ -17,6 +17,7 @@
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

Expand All @@ -37,7 +38,7 @@ public class SignerProviderTests {
@Test
public void testSignedProviderSymmetricKeys() {
String keyId = generator.generate();
signerProvider.addSigningKey(keyId, "testkey");
signerProvider.addSigningKeys(Collections.singletonMap(keyId, "testkey"));

SignerProvider.KeyInfo key = signerProvider.getKey(keyId);
assertNotNull(key.getSigner());
Expand Down Expand Up @@ -66,7 +67,7 @@ public void testSignedProviderAsymmetricKeys() throws Exception {
"wTKZHjWybPHsW2q8Z6Moz5dvE+XMd11c5NtIG2/L97I=\n" +
"-----END RSA PRIVATE KEY-----";
String keyId = generator.generate();
signerProvider.addSigningKey(keyId, signingKey);
signerProvider.addSigningKeys(Collections.singletonMap(keyId, signingKey));
SignerProvider.KeyInfo key = signerProvider.getKey(keyId);
assertNotNull(key.getSigner());
assertNotNull(key.getVerifier());
Expand All @@ -78,25 +79,25 @@ public void testSignedProviderAsymmetricKeys() throws Exception {
@Test(expected = IllegalArgumentException.class)
public void testNullSigningKey() {
SignerProvider signerProvider = new SignerProvider();
signerProvider.addSigningKey("nullKey", null);
signerProvider.addSigningKeys(Collections.singletonMap("nullKey", null));
}

@Test(expected = IllegalArgumentException.class)
public void testEmptySigningKey() {
SignerProvider signerProvider = new SignerProvider();
signerProvider.addSigningKey("emptyKey", "");
signerProvider.addSigningKeys(Collections.singletonMap("emptyKey", ""));
}

@Test(expected = IllegalArgumentException.class)
public void testNullKeyId() {
SignerProvider signerProvider = new SignerProvider();
signerProvider.addSigningKey(null, "signingkeydata");
signerProvider.addSigningKeys(Collections.singletonMap(null, "signingkeydata"));
}

@Test(expected = IllegalArgumentException.class)
public void testEmptyKeyId() {
SignerProvider signerProvider = new SignerProvider();
signerProvider.addSigningKey("", "signingkeydata");
signerProvider.addSigningKeys(Collections.singletonMap("", "signingkeydata"));
}

@Test
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.springframework.security.jwt.crypto.sign.RsaVerifier;

import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -54,7 +55,7 @@ public void setUp() throws Exception {

@Test
public void sharedSecretIsReturnedFromTokenKeyEndpoint() throws Exception {
signerProvider.addSigningKey("someKeyId", "someKey");
signerProvider.addSigningKeys(Collections.singletonMap("someKeyId", "someKey"));
signerProvider.setPrimaryKeyId("someKeyId");
VerificationKeyResponse response = tokenKeyEndpoint.getKey(new UsernamePasswordAuthenticationToken("foo", "bar"));
assertEquals("HMACSHA256", response.getAlgorithm());
Expand All @@ -66,7 +67,7 @@ public void sharedSecretIsReturnedFromTokenKeyEndpoint() throws Exception {

@Test(expected = AccessDeniedException.class)
public void sharedSecretCannotBeAnonymouslyRetrievedFromTokenKeyEndpoint() throws Exception {
signerProvider.addSigningKey("anotherKeyId", "someKey");
signerProvider.addSigningKeys(Collections.singletonMap("anotherKeyId", "someKey"));
assertEquals("{alg=HMACSHA256, value=someKey}",
tokenKeyEndpoint.getKey(
new AnonymousAuthenticationToken("anon", "anonymousUser", AuthorityUtils
Expand All @@ -75,7 +76,7 @@ public void sharedSecretCannotBeAnonymouslyRetrievedFromTokenKeyEndpoint() throw

@Test
public void responseIsBackwardCompatibleWithMap() {
signerProvider.addSigningKey("literallyAnything", "someKey");
signerProvider.addSigningKeys(Collections.singletonMap("literallyAnything", "someKey"));
VerificationKeyResponse response = tokenKeyEndpoint.getKey(new UsernamePasswordAuthenticationToken("foo", "bar"));

String serialized = JsonUtils.writeValueAsString(response);
Expand All @@ -98,9 +99,9 @@ public void listResponseContainsAllPublicKeysWhenUnauthenticated() throws Except
"nxH5usX+uyfxAiA0l7olWyEYRD10DDFmINs6auuXMUrskBDz0e8lWXqV6QIgJSkM\n" +
"L5WgVmzexrNmKxmGQQhNzfgO0Lk7o+iNNZXbkxw=\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey1", signingKey1);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey1", signingKey1));

signerProvider.addSigningKey("thisIsASymmetricKeyThatShouldNotShowUp", "ItHasSomeTextThatIsNotPEM");
signerProvider.addSigningKeys(Collections.singletonMap("thisIsASymmetricKeyThatShouldNotShowUp", "ItHasSomeTextThatIsNotPEM"));

String signingKey2 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIBOQIBAAJBAKIuxhxq0SyeITbTw3SeyHz91eB6xEwRn9PPgl+klu4DRUmVs0h+\n" +
Expand All @@ -111,7 +112,7 @@ public void listResponseContainsAllPublicKeysWhenUnauthenticated() throws Except
"H20/OhqZ3/IHAiBSn3/31am8zW+l7UM+Fkc29aij+KDsYQfmmvriSp3/2QIgFtiE\n" +
"Jkd0KaxkobLdyDrW13QnEaG5TXO0Y85kfu3nP5o=\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey2", signingKey2);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey2", signingKey2));

String signingKey3 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIBOgIBAAJBAOnndOyLh8axLMyjX+gCglBCeU5Cumjxz9asho5UvO8zf03PWciZ\n" +
Expand All @@ -122,7 +123,7 @@ public void listResponseContainsAllPublicKeysWhenUnauthenticated() throws Except
"MPlmwSg1oPpANwIgHngBCtqQnvYQGpX9QO3O0oRaczBYTI789Nz2O7FE4asCIGEy\n" +
"SkbkWTex/hl+l0wdNErz/yBxP8esbPukOUqks/if\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey3", signingKey3);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey3", signingKey3));

VerificationKeysListResponse keysResponse = tokenKeyEndpoint.getKeys(null);
List<VerificationKeyResponse> keys = keysResponse.getKeys();
Expand Down Expand Up @@ -159,9 +160,9 @@ public void listResponseContainsAllKeysWhenAuthenticated() throws Exception {
"nxH5usX+uyfxAiA0l7olWyEYRD10DDFmINs6auuXMUrskBDz0e8lWXqV6QIgJSkM\n" +
"L5WgVmzexrNmKxmGQQhNzfgO0Lk7o+iNNZXbkxw=\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey1", signingKey1);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey1", signingKey1));

signerProvider.addSigningKey("SymmetricKey", "ItHasSomeTextThatIsNotPEM");
signerProvider.addSigningKeys(Collections.singletonMap("SymmetricKey", "ItHasSomeTextThatIsNotPEM"));

String signingKey2 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIBOQIBAAJBAKIuxhxq0SyeITbTw3SeyHz91eB6xEwRn9PPgl+klu4DRUmVs0h+\n" +
Expand All @@ -172,7 +173,7 @@ public void listResponseContainsAllKeysWhenAuthenticated() throws Exception {
"H20/OhqZ3/IHAiBSn3/31am8zW+l7UM+Fkc29aij+KDsYQfmmvriSp3/2QIgFtiE\n" +
"Jkd0KaxkobLdyDrW13QnEaG5TXO0Y85kfu3nP5o=\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey2", signingKey2);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey2", signingKey2));

String signingKey3 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIBOgIBAAJBAOnndOyLh8axLMyjX+gCglBCeU5Cumjxz9asho5UvO8zf03PWciZ\n" +
Expand All @@ -183,7 +184,7 @@ public void listResponseContainsAllKeysWhenAuthenticated() throws Exception {
"MPlmwSg1oPpANwIgHngBCtqQnvYQGpX9QO3O0oRaczBYTI789Nz2O7FE4asCIGEy\n" +
"SkbkWTex/hl+l0wdNErz/yBxP8esbPukOUqks/if\n" +
"-----END RSA PRIVATE KEY-----";
signerProvider.addSigningKey("RsaKey3", signingKey3);
signerProvider.addSigningKeys(Collections.singletonMap("RsaKey3", signingKey3));

VerificationKeysListResponse keysResponse = tokenKeyEndpoint.getKeys(mock(Principal.class));
List<VerificationKeyResponse> keys = keysResponse.getKeys();
Expand Down
10 changes: 0 additions & 10 deletions uaa/src/main/resources/uaa.yml
Expand Up @@ -266,16 +266,6 @@ oauth:
# QH+xY/4h8tgL+eASz5QWhj8DItm8wYGI5lKJr8f36jk0JLPUXODyDAeN6ekXY9LI
# fudkijw0dnh28LJqbkFF5wLNtATzyCfzjp+czrPMn9uqLNKt/iVD
# -----END RSA PRIVATE KEY-----
# verificationKey: |
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0m59l2u9iDnMbrXHfqkO
# rn2dVQ3vfBJqcDuFUK03d+1PZGbVlNCqnkpIJ8syFppW8ljnWweP7+LiWpRoz0I7
# fYb3d8TjhV86Y997Fl4DBrxgM6KTJOuE/uxnoDhZQ14LgOU2ckXjOzOdTsnGMKQB
# LCl0vpcXBtFLMaSbpv1ozi8h7DJyVZ6EnFQZUWGdgTMhDrmqevfx95U/16c5WBDO
# kqwIn7Glry9n9Suxygbf8g5AzpWcusZgDLIIZ7JTUldBb8qU2a0Dl4mvLZOn4wPo
# jfj9Cw2QICsc5+Pwf21fP+hzf+1WSRHbnYv8uanRO0gZ8ekGaghM/2H6gqJbo2nI
# JwIDAQAB
# -----END PUBLIC KEY-----
# # Sets the default validity for all zones
# global:
# accessTokenValiditySeconds: 3600
Expand Down
17 changes: 6 additions & 11 deletions uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml
Expand Up @@ -323,26 +323,21 @@

<bean id="signerProviderLegacyKeyInitializer" class="org.springframework.beans.factory.config.MethodInvokingBean" depends-on="signerProvider" lazy-init="false">
<property name="targetObject" ref="signerProvider" />
<property name="targetMethod" value="addSigningKey" />
<property name="targetMethod" value="addSigningKeys" />
<property name="arguments">
<list>
<bean class="java.lang.String">
<constructor-arg value="legacyTokenKey" />
</bean>
<bean class="java.lang.String">
<constructor-arg value="${jwt.token.signing-key:tokenkey}" />
</bean>
</list>
<bean class="java.util.HashMap">
<constructor-arg value="#{signingKeysMap.getKeys()}" />
</bean>
</property>
</bean>

<bean id="uaaTokenPolicy" class="org.cloudfoundry.identity.uaa.zone.TokenPolicy">
<constructor-arg name="accessTokenValidity" value="${jwt.token.policy.accessTokenValiditySeconds:#{globalTokenPolicy.getAccessTokenValidity()}}" />
<constructor-arg name="refreshTokenValidity" value="${jwt.token.policy.refreshTokenValiditySeconds:#{globalTokenPolicy.getRefreshTokenValidity()}}" />
<constructor-arg name="keyPairsMap" ref="keyPairsMap" />
<constructor-arg name="keyPairsMap" ref="signingKeysMap" />
</bean>

<bean id="keyPairsMap" class="org.cloudfoundry.identity.uaa.zone.SigningKeysMap" factory-bean="keyPairsFactory" factory-method="getKeyPairsMap" />
<bean id="signingKeysMap" class="org.cloudfoundry.identity.uaa.zone.SigningKeysMap" factory-bean="keyPairsFactory" factory-method="getKeyPairsMap" />

<bean id="keyPairsFactory" class="org.cloudfoundry.identity.uaa.impl.config.KeyPairsFactoryBean">
<constructor-arg type="java.util.Map" value="#{@config['jwt']==null ? T(java.util.Collections).EMPTY_MAP :
Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;

import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -83,7 +84,7 @@ public class TokenKeyEndpointMockMvcTests extends InjectedMockContextTest {
public void setUp() throws Exception {
SignerProvider provider = getWebApplicationContext().getBean(SignerProvider.class);
originalPrimaryKey = provider.getPrimaryKeyId();
provider.addSigningKey("testKey", signKey);
provider.addSigningKeys(Collections.singletonMap("testKey", signKey));
provider.setPrimaryKeyId("testKey");
}

Expand Down

0 comments on commit 3d0aed2

Please sign in to comment.