Skip to content

Commit

Permalink
Allow EC signing keys and multiple checking keys in ManagementPortal
Browse files Browse the repository at this point in the history
Also adds configuration keys for the keystore password and the key aliases to use for signing and checking.
  • Loading branch information
dennyverbeeck committed Jun 12, 2018
1 parent b06e8df commit 24c31fd
Show file tree
Hide file tree
Showing 26 changed files with 588 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ public RadarToken validateAccessToken(String token) throws TokenValidationExcept
}
return new JwtRadarToken(jwt);
} catch (SignatureVerificationException sve) {
log.warn("Client presented a token with an incorrect signature, fetching public key"
+ " again. Token: {}", token);
log.warn("Client presented a token with an incorrect signature, fetching public "
+ "keys again. Token: {}", token);
refresh();
return validateAccessToken(token);
} catch (JWTVerificationException ex) {
throw new TokenValidationException(ex);
// this verifier does not accept the token, move on to the next one
}
}
throw new TokenValidationException("No registered validator could authenticate this token");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package org.radarcns.management.config;

import java.net.URI;
import java.security.KeyPair;
import java.security.interfaces.RSAPublicKey;
import org.radarcns.auth.config.ServerConfig;
import org.radarcns.management.security.jwt.RadarKeyStoreKeyFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.net.URI;
import java.security.PublicKey;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
* Radar-auth server configuration for using a local keystore. This will load the MP public key
Expand All @@ -14,22 +17,23 @@
public class LocalKeystoreConfig implements ServerConfig {

public static final String RES_MANAGEMENT_PORTAL = "res_ManagementPortal";
private final RSAPublicKey publicKey;
private final List<PublicKey> publicKeys;

/**
* Constructor will look for the keystore in the classpath at /config/keystore.jks and load
* the public key from it.
*/
public LocalKeystoreConfig() {
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("/config/keystore.jks"), "radarbase".toCharArray())
.getKeyPair("selfsigned");
publicKey = (RSAPublicKey) keyPair.getPublic();
public LocalKeystoreConfig(String keyStorePassword, List<String> checkingKeyAliases) {
RadarKeyStoreKeyFactory keyFactory = new RadarKeyStoreKeyFactory(
new ClassPathResource("/config/keystore.jks"), keyStorePassword.toCharArray());
publicKeys = checkingKeyAliases.stream()
.map(alias -> keyFactory.getKeyPair(alias).getPublic())
.collect(Collectors.toList());
}

@Override
public URI getPublicKeyEndpoint() {
return null;
public List<URI> getPublicKeyEndpoints() {
return Collections.emptyList();
}

@Override
Expand All @@ -38,7 +42,7 @@ public String getResourceName() {
}

@Override
public RSAPublicKey getPublicKey() {
return publicKey;
public List<PublicKey> getPublicKeys() {
return publicKeys;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
* Created by nivethika on 3-10-17.
*/
Expand Down Expand Up @@ -112,13 +114,43 @@ public static class Oauth {

private String clientsFile;

private String signingKeyAlias;

private List<String> checkingKeyAliases;

private String keyStorePassword;

public String getClientsFile() {
return clientsFile;
}

public void setClientsFile(String clientsFile) {
this.clientsFile = clientsFile;
}

public String getSigningKeyAlias() {
return signingKeyAlias;
}

public void setSigningKeyAlias(String signingKeyAlias) {
this.signingKeyAlias = signingKeyAlias;
}

public List<String> getCheckingKeyAliases() {
return checkingKeyAliases;
}

public void setCheckingKeyAliases(List<String> checkingKeyAliases) {
this.checkingKeyAliases = checkingKeyAliases;
}

public String getKeyStorePassword() {
return keyStorePassword;
}

public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
}

public static class CatalogueServer {
Expand All @@ -143,5 +175,4 @@ public void setEnableAutoImport(boolean enableAutoImport) {
this.enableAutoImport = enableAutoImport;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import io.github.jhipster.security.AjaxLogoutSuccessHandler;
import io.github.jhipster.security.Http401UnauthorizedEntryPoint;
import java.security.KeyPair;
import java.util.Arrays;
import javax.sql.DataSource;
import org.radarcns.auth.authorization.AuthoritiesConstants;
import org.radarcns.management.security.ClaimsTokenEnhancer;
import org.radarcns.management.security.jwt.EcdsaVerifier;
import org.radarcns.management.security.jwt.MultiVerifier;
import org.radarcns.management.security.jwt.RadarJwtAccessTokenConverter;
import org.radarcns.management.security.jwt.RadarKeyStoreKeyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
Expand All @@ -23,6 +24,8 @@
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
Expand All @@ -42,10 +45,17 @@
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;

import javax.sql.DataSource;
import java.security.KeyPair;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
public class OAuth2ServerConfiguration {

Expand Down Expand Up @@ -167,6 +177,9 @@ protected static class AuthorizationServerConfiguration extends
@Autowired
private JdbcClientDetailsService jdbcClientDetailsService;

@Autowired
private ManagementPortalProperties managementPortalProperties;

@Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
Expand All @@ -189,13 +202,32 @@ public TokenStore tokenStore() {

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("/config/keystore.jks"), "radarbase".toCharArray())
.getKeyPair("selfsigned");
RadarJwtAccessTokenConverter converter = new RadarJwtAccessTokenConverter();

// set the keypair for signing
RadarKeyStoreKeyFactory kf = new RadarKeyStoreKeyFactory(
new ClassPathResource("/config/keystore.jks"),
managementPortalProperties.getOauth().getKeyStorePassword().toCharArray());
String signKey = managementPortalProperties.getOauth().getSigningKeyAlias();
KeyPair keyPair = kf.getKeyPair(signKey);
converter.setKeyPair(keyPair);

// get all public keys for verifying and set the converter's verifier to a MultiVerifier
List<SignatureVerifier> verifiers = managementPortalProperties.getOauth()
.getCheckingKeyAliases().stream()
.map(alias -> kf.getKeyPair(alias).getPublic())
.filter(publicKey -> publicKey instanceof RSAPublicKey
|| publicKey instanceof ECPublicKey)
.map(publicKey -> {
if (publicKey instanceof RSAPublicKey) {
return new RsaVerifier((RSAPublicKey) publicKey);
} else {
return new EcdsaVerifier((ECPublicKey) publicKey);
}
})
.collect(Collectors.toList());
converter.setVerifier(new MultiVerifier(verifiers));

return converter;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

import io.github.jhipster.security.AjaxLogoutSuccessHandler;
import io.github.jhipster.security.Http401UnauthorizedEntryPoint;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import org.radarcns.auth.authentication.TokenValidator;
import org.radarcns.management.security.JwtAuthenticationFilter;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -27,6 +26,9 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
Expand All @@ -41,6 +43,9 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@Autowired
private ManagementPortalProperties managementPortalProperties;

@PostConstruct
public void init() {
try {
Expand Down Expand Up @@ -133,6 +138,8 @@ public FilterRegistrationBean jwtAuthenticationFilterRegistration() {
}

public Filter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
return new JwtAuthenticationFilter(new TokenValidator(
new LocalKeystoreConfig(managementPortalProperties.getOauth().getKeyStorePassword(),
managementPortalProperties.getOauth().getCheckingKeyAliases())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.radarcns.auth.authentication.TokenValidator;
import org.radarcns.auth.exception.TokenValidationException;
import org.radarcns.management.config.LocalKeystoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
Expand All @@ -24,9 +23,13 @@
public class JwtAuthenticationFilter extends GenericFilterBean {

private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private static TokenValidator validator = new TokenValidator(new LocalKeystoreConfig());
private final TokenValidator validator;
public static final String TOKEN_ATTRIBUTE = "jwt";

public JwtAuthenticationFilter(TokenValidator validator) {
this.validator = validator;
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws
IOException, ServletException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.radarcns.management.security.jwt;

import org.springframework.security.jwt.crypto.sign.Signer;

import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;

/**
* Class that creates ECDSA signatures for use in Spring Security.
*/
public class EcdsaSigner implements Signer {

public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
private final ECPrivateKey privateKey;
private final String algorithm;

public EcdsaSigner(ECPrivateKey privateKey) {
this(privateKey, DEFAULT_ALGORITHM);
}

public EcdsaSigner(ECPrivateKey privateKey, String signingAlgorithm) {
this.privateKey = privateKey;
this.algorithm = signingAlgorithm;
}

@Override
public byte[] sign(byte[] bytes) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
signature.update(bytes);
return signature.sign();
} catch (GeneralSecurityException ex) {
throw new SignatureException("Could not provide a signature", ex);
}
}

@Override
public String algorithm() {
return algorithm;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.radarcns.management.security.jwt;

import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;

import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.interfaces.ECPublicKey;

public class EcdsaVerifier implements SignatureVerifier {

private final ECPublicKey publicKey;
private final String algorithm;

public EcdsaVerifier(ECPublicKey publicKey) {
this(publicKey, EcdsaSigner.DEFAULT_ALGORITHM);
}

public EcdsaVerifier(ECPublicKey publicKey, String algorithm) {
this.publicKey = publicKey;
this.algorithm = algorithm;
}

@Override
public void verify(byte[] content, byte[] sig) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initVerify(publicKey);
signature.update(content);

if (!signature.verify(sig)) {
throw new InvalidSignatureException("EC Signature did not match content");
}
} catch (GeneralSecurityException ex) {
throw new SignatureException("An error occured verifying the signature", ex);
}
}

@Override
public String algorithm() {
return algorithm;
}
}
Loading

0 comments on commit 24c31fd

Please sign in to comment.