From 882f6fddfed9c1281b370f50e787a21b2aebcb08 Mon Sep 17 00:00:00 2001 From: Alexander Schwarz Date: Thu, 16 Sep 2021 12:45:36 +0200 Subject: [PATCH] refactoring JWT and KeyStore --- certs/dev-decorator.jks | Bin 1401 -> 8563 bytes certs/dev-test.jks | Bin 3492 -> 0 bytes .../decorator/config/DgcProperties.java | 35 +++---- .../decorator/dto/IdentityResponse.java | 2 + .../validation/decorator/entity/KeyType.java | 4 +- .../decorator/service/AccessTokenService.java | 92 +++--------------- .../decorator/service/IdentityService.java | 11 ++- .../service/KeyStoreKeyProvider.java | 65 +++++++------ src/main/resources/application.yml | 30 +++--- 9 files changed, 91 insertions(+), 148 deletions(-) delete mode 100644 certs/dev-test.jks diff --git a/certs/dev-decorator.jks b/certs/dev-decorator.jks index f815f532fafe2b7e502649e84f9ec869ffdb1d49..12591c9ab45a1adbc423e6385a9f98e0c26762d4 100644 GIT binary patch literal 8563 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 literal 1401 zcmezO_TO6u1_mZ5W@L~mOU%hkNi50C&r3;7&M!(V$uBC-OwY?stz=+etiI3x4yS)RpP{_`#)uRG^Mz`IBg!#35*P^5qhQumO$2LgC@rJKs=+_k z7$V#lBFu0R137VCBSQmALt{fD14|>zC~;n65Z4gOrL$`y4TRa)!C}F~2n`fwMs{W= z1{T-TVWJnZCSUSq?>Nq5yXeOIsd>q;w92mcsF??s@2?lRRvkE|5&%Vqm};|<%>=Z9N9ala(#*BYke7IOLF z)z7ZGMfTN2SWft}IIVxjgN)pds~27Cvsfk=-mG1G-{kEnr|Ofu0XrjpNZK#Dtajk` z1)G#J_|s}VpM3;s7|}U(Ag5Ie15@O*3gXh)HMrAiXiUM?G$L-8|0;csrfhS(w*lgwVJzIPAgwwm6Y$l(|_h$;k}jalP2@MnjQQ$j_07FyWpv1 RYt43r*G-?g#xS8M5dey($!-7u diff --git a/certs/dev-test.jks b/certs/dev-test.jks deleted file mode 100644 index b344179e1dea587cad38ee1f462991cf8d3cdcad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3492 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 encAliases; + private List encAliases = new ArrayList<>(); - private List signAliases; + private List signAliases = new ArrayList<>(); + + private List keyAliases = new ArrayList<>(); private TokenProperties token; - + private List services = new ArrayList<>(); - + private List endpoints = new ArrayList<>(); @Data @@ -69,33 +70,19 @@ public static final class GatewayDownload { @Data public static final class TokenProperties { - public static final String ALGORITHM_ELLIPTIC_CURVE = "ES"; - - // use "ks" (keyStore) or "config" - private String provider; - private String issuer; private String type; - private String algorithm; + private TokenInitializeProperties initialize; + } - private int keysize; + @Data + public static final class TokenInitializeProperties { private int validity; - - private String publicKey; - - private String privateKey; - - private String keyAlgorithm; - - public SignatureAlgorithm getSignatureAlgorithm() { - final String name = String.format("%s%d", this.algorithm.toUpperCase(), this.keysize); - return SignatureAlgorithm.forName(name); - } } - + @Data public static final class ServiceProperties { diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java index f6bc0ee..721adb5 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/dto/IdentityResponse.java @@ -60,6 +60,8 @@ public static final class PublicKeyJwkIdentityResponse { private String kid; private String alg; + + private String use; } @Data diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java index fc8b258..8d38637 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/entity/KeyType.java @@ -26,5 +26,7 @@ public enum KeyType { VALIDATION_DECORATOR_ENC_KEY, - VALIDATION_DECORATOR_SIGN_KEY; + VALIDATION_DECORATOR_SIGN_KEY, + + VALIDATION_DECORATOR_KEY; } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java index 35cb3aa..bac69c5 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenService.java @@ -22,22 +22,14 @@ import eu.europa.ec.dgc.validation.decorator.config.DgcProperties; import eu.europa.ec.dgc.validation.decorator.exception.DccException; -import eu.europa.ec.dgc.validation.decorator.exception.NotImplementedException; -import eu.europa.ec.dgc.validation.decorator.exception.UncheckedInvalidKeySpecException; -import eu.europa.ec.dgc.validation.decorator.exception.UncheckedNoSuchAlgorithmException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; +import io.jsonwebtoken.SignatureAlgorithm; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; import java.time.Instant; -import java.util.Base64; import java.util.Collections; import java.util.Date; import java.util.Map; @@ -50,10 +42,6 @@ public class AccessTokenService { public static final String TOKEN_PREFIX = "Bearer "; - private static final String KEY_PROVIDER = "ks"; - - private static final String CONFIG_PROVIDER = "config"; - private final DgcProperties properties; private final KeyProvider keyProvider; @@ -103,8 +91,11 @@ public String buildAccessToken(final String subject) { */ public Map parseAccessToken(String token) { final String tokenContent = token.startsWith(TOKEN_PREFIX) ? token.replace(TOKEN_PREFIX, "") : token; + final String activeSignKey = this.keyProvider.getActiveSignKey(); + final PublicKey publicKey = this.keyProvider.receiveCertificate(activeSignKey).getPublicKey(); + final Jws parsedToken = Jwts.parser() - .setSigningKey(this.getPublicKey()) + .setSigningKey(publicKey) .requireIssuer(properties.getToken().getIssuer()) .parseClaimsJws(tokenContent); final Claims body = parsedToken.getBody(); @@ -135,71 +126,18 @@ public boolean isValid(final String token) { } private JwtBuilder getAccessTokenBuilder() { + final String activeSignKey = this.keyProvider.getActiveSignKey(); + final PrivateKey privateKey = this.keyProvider.receivePrivateKey(activeSignKey); + final String algorithm = this.keyProvider.getAlg(activeSignKey); + final SignatureAlgorithm signatureAlgorithm = io.jsonwebtoken.SignatureAlgorithm.valueOf(algorithm); + final String keyId = this.keyProvider.getKid(activeSignKey); + final int validity = properties.getToken().getInitialize().getValidity(); + return Jwts.builder() - .signWith(properties.getToken().getSignatureAlgorithm(), this.getPrivateKey()) + .signWith(signatureAlgorithm, privateKey) .setHeaderParam("typ", properties.getToken().getType()) - .setHeaderParam("kid", this.getKeyId()) + .setHeaderParam("kid", keyId) .setIssuer(properties.getToken().getIssuer()) - .setExpiration(new Date(Instant.now().plusSeconds(properties.getToken().getValidity()).toEpochMilli())); - } - - private PublicKey parsePublicKey(String privateKeyBase64) { - try { - final byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64); - final X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); - final KeyFactory kf = KeyFactory.getInstance(this.getKeyAlgorithm()); - return kf.generatePublic(spec); - } catch (NoSuchAlgorithmException e) { - throw new UncheckedNoSuchAlgorithmException(e); - } catch (InvalidKeySpecException e) { - throw new UncheckedInvalidKeySpecException(e); - } - } - - private PrivateKey parsePrivateKey(String privateKeyBase64) { - try { - final byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64); - final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); - final KeyFactory kf = KeyFactory.getInstance(this.getKeyAlgorithm()); - return kf.generatePrivate(spec); - } catch (NoSuchAlgorithmException e) { - throw new UncheckedNoSuchAlgorithmException(e); - } catch (InvalidKeySpecException e) { - throw new UncheckedInvalidKeySpecException(e); - } - } - - private String getKeyAlgorithm() { - return properties.getToken().getKeyAlgorithm(); - } - - private String getKeyId() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return "config"; - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.getKid(keyProvider.getActiveSignKey()); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); - } - - private PublicKey getPublicKey() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return this.parsePublicKey(this.properties.getToken().getPublicKey()); - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.receiveCertificate(keyProvider.getActiveSignKey()).getPublicKey(); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); - } - - private PrivateKey getPrivateKey() { - if (CONFIG_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return this.parsePrivateKey(this.properties.getToken().getPrivateKey()); - } else if (KEY_PROVIDER.equalsIgnoreCase(this.properties.getToken().getProvider())) { - return keyProvider.receivePrivateKey(keyProvider.getActiveSignKey()); - } - throw new NotImplementedException(String - .format("Token provider '%s' not implemented", this.properties.getToken().getProvider())); + .setExpiration(new Date(Instant.now().plusSeconds(validity).toEpochMilli())); } } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java index 46e4ca9..fd1c6ca 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/IdentityService.java @@ -47,7 +47,7 @@ public class IdentityService { private static final String VERIFICATION_TYPE = "JsonWebKey2020"; - private static final String PUBLIC_KEY_ALGORITM = "ES256"; + private final DgcProperties dgcProperties; @@ -114,15 +114,16 @@ private List getServices(final String element, final St } private PublicKeyJwkIdentityResponse buildPublicKey(String keyName) { - final Certificate certificate = keyProvider.receiveCertificate(keyName); - final PublicKeyJwkIdentityResponse publicKeyJwk = new PublicKeyJwkIdentityResponse(); + final Certificate certificate = keyProvider.receiveCertificate(keyName); try { + final PublicKeyJwkIdentityResponse publicKeyJwk = new PublicKeyJwkIdentityResponse(); publicKeyJwk.setX5c(Base64.getEncoder().encodeToString(certificate.getEncoded())); publicKeyJwk.setKid(keyProvider.getKid(keyName)); - publicKeyJwk.setAlg(PUBLIC_KEY_ALGORITM); + publicKeyJwk.setAlg(keyProvider.getAlg(keyName)); + publicKeyJwk.setUse(keyProvider.getKeyUse(keyName).name().toLowerCase()); + return publicKeyJwk; } catch (CertificateEncodingException e) { throw new DccException("Can not encode certificate", e); } - return publicKeyJwk; } } diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java index 67c0a52..df90cb9 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/KeyStoreKeyProvider.java @@ -85,47 +85,52 @@ public void createKeys() throws NoSuchAlgorithmException, IOException, Certifica final Path filePath = Path.of(dgcConfigProperties.getKeyStoreFile()); if (!Files.exists(filePath)) { - log.error("keyfile not found on: {} please adapt the configuration property: issuance.keyStoreFile", + final String msg = String.format( + "keyfile not found on '%s' please adapt the configuration property: issuance.keyStoreFile", filePath); - throw new DccException("keyfile not found on: " + filePath - + " please adapt the configuration property: issuance.keyStoreFile"); + log.error(msg); + throw new DccException(msg); } - final CertificateUtils certificateUtils = new CertificateUtils(); final KeyStore keyStore = KeyStore.getInstance("JKS"); final char[] keyStorePassword = dgcConfigProperties.getKeyStorePassword().toCharArray(); try (InputStream is = new FileInputStream(dgcConfigProperties.getKeyStoreFile())) { final char[] privateKeyPassword = dgcConfigProperties.getPrivateKeyPassword().toCharArray(); keyStore.load(is, privateKeyPassword); - KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection(keyStorePassword); - - for (String alias : this.getKeyNames(KeyType.ALL)) { - final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, keyPassword); - - if (privateKeyEntry != null) { - final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); - PrivateKey privateKey = privateKeyEntry.getPrivateKey(); - certificates.put(alias, cert); - privateKeys.put(alias, privateKey); - - final String kid = certificateUtils.getCertKid((X509Certificate) cert); - kids.put(alias, kid); - kidToName.put(kid, alias); - - if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.1")) { - algs.put(alias, "RS256"); - } else if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.10")) { - algs.put(alias, "PS256"); - } else if (cert.getSigAlgOID().contains("1.2.840.10045.4.3.2")) { - algs.put(alias, "ES256"); - } else { - throw new NotImplementedException(String.format("SigAlg OID '{}'", cert.getSigAlgOID())); + final KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection(keyStorePassword); + + for (final String alias : this.getKeyNames(KeyType.ALL)) { + if (keyStore.isKeyEntry(alias)) { + final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, keyPassword); + if (privateKeyEntry != null) { + final PrivateKey privateKey = privateKeyEntry.getPrivateKey(); + privateKeys.put(alias, privateKey); } - } + } + final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + this.handleCertificate(alias, cert); } } } + private void handleCertificate(final String alias, final X509Certificate cert) { + this.certificates.put(alias, cert); + + final String kid = new CertificateUtils().getCertKid((X509Certificate) cert); + this.kids.put(alias, kid); + this.kidToName.put(kid, alias); + + if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.1")) { + this.algs.put(alias, "RS256"); + } else if (cert.getSigAlgOID().contains("1.2.840.113549.1.1.10")) { + this.algs.put(alias, "PS256"); + } else if (cert.getSigAlgOID().contains("1.2.840.10045.4.3.2")) { + this.algs.put(alias, "ES256"); + } else { + throw new NotImplementedException(String.format("SigAlg OID '{}'", cert.getSigAlgOID())); + } + } + @Override public Certificate receiveCertificate(String keyName) { return certificates.get(keyName); @@ -146,9 +151,13 @@ public List getKeyNames(KeyType type) { case VALIDATION_DECORATOR_SIGN_KEY: keyNames.addAll(dgcConfigProperties.getSignAliases()); break; + case VALIDATION_DECORATOR_KEY: + keyNames.addAll(dgcConfigProperties.getKeyAliases()); + break; case ALL: keyNames.addAll(dgcConfigProperties.getEncAliases()); keyNames.addAll(dgcConfigProperties.getSignAliases()); + keyNames.addAll(dgcConfigProperties.getKeyAliases()); break; default: throw new NotImplementedException(String.format("Key type '%s'", type)); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1440429..4235278 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,22 +20,26 @@ dgc: serviceUrl: http://localhost:8080 keyStoreFile: certs/dev-decorator.jks keyStorePassword: changeMe - privateKeyPassword: changeMe - activeSignKey: validationdecoratorsignkey + privateKeyPassword: changeMe + #### activeSignKey must be one of signAliases + activeSignKey: ValidationDecoratorSignKey-1 encAliases: - - validationdecoratorenckey + - ValidationDecoratorEncKey-1 signAliases: - - validationdecoratorsignkey + - ValidationDecoratorSignKey-1 + keyAliases: + - AccessTokenServiceKey-1 + - ServiceProviderKey-1 + - CancellationServiceKey-1 + - StatusServiceKey-1 + - DccValidatorServiceKey-1 + token: - provider: config #### use "ks" (keyStore) or "config" issuer: Validation Decorator Dervice type: JWT - algorithm: ES - keysize: 256 - validity: 3600 #### in seconds. Expiration is calculated - publicKey: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIPrtYsW9+Juwp/mt7h8FJ3LgFRIUl2Vlmcl1DUm5gNHl0LnHIL4Jff6mg6yVhehdQiMvkhUtTvmFIUWONSJEnw== - privateKey: MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBSuPIbykwH24sjQsTneeN6EyjiA1NK5W7uca+HxmGmWw== - keyAlgorithm: EC + initialize: + #### in seconds. Expiration is calculated + validity: 3600 #### Validation Service services: - id: ${dgc.serviceUrl}/identity/ValidationService @@ -44,8 +48,8 @@ dgc: name: Validation Service #### Validation Decorator endpoints: - - id: ${dgc.serviceUrl}/identity/AccessCredentialService - type: AccessCredentialService + - id: ${dgc.serviceUrl}/identity/AccessTokenService + type: AccessTokenService serviceEndpoint: ${dgc.serviceUrl}/token name: Validation Decorator Token - id: ${dgc.serviceUrl}/identity/ServiceProvider