Skip to content

Commit

Permalink
Bugfix: handle secret keys correctly (#1)
Browse files Browse the repository at this point in the history
Current key providers were expecting key files to be stored in the Plain Text format. Following the Key Generation Guide, we've updated the Key Initialization to make use of binary (.DER) format for Private Key and (X.509 Certificate) template for Public Key

Signed-off-by: Kshitij Patil <[email protected]>
  • Loading branch information
Kshitij09 authored Nov 8, 2021
1 parent 8c34ad6 commit 22e0787
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 56 deletions.
2 changes: 1 addition & 1 deletion springboot-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

group = 'com.kshitijpatil.tazabazar'
version = '1.2.0'
version = '1.3.0'
sourceCompatibility = '11'

ext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
@ConfigurationProperties(prefix = "jwt")
public class JwtConfig {
private Long expirationInMinutes;
private String privateKeyFilepath;
private String publicKeyFilepath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public String generateToken(String username, List<String> roles) {
.setExpiration(dateUtil.toDate(expiryDate))
.setIssuer(jwtIssuer)
.setId(UUID.randomUUID().toString())
.signWith(SignatureAlgorithm.RS256, jwtPrivateKeyProvider.getPrivateKey())
.signWith(SignatureAlgorithm.RS256, jwtPrivateKeyProvider.get())
.claim(CLAIM_USERNAME, username)
.claim(CLAIM_ROLES, roles.toArray())
.compact();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class JwtValidateService {
public boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(jwtPublicKeyProvider.getPublicKey())
.setSigningKey(jwtPublicKeyProvider.get())
.parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
Expand All @@ -35,7 +35,7 @@ public boolean validateToken(String token) {

public Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(jwtPublicKeyProvider.getPublicKey())
.setSigningKey(jwtPublicKeyProvider.get())
.parseClaimsJws(token)
.getBody();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class JwtInitializationException extends RuntimeException {
public JwtInitializationException(Throwable e) {
super("Something went wong while reading private key!", e);
super("Error initializing public/private key!", e);
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,38 @@
package com.kshitijpatil.tazabazar.security.jwt;

import com.kshitijpatil.tazabazar.utils.Base64Util;
import com.kshitijpatil.tazabazar.utils.ReadKeyMixin;
import com.kshitijpatil.tazabazar.configuration.JwtConfig;
import com.kshitijpatil.tazabazar.utils.ResourceUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

@Component
@RequiredArgsConstructor
public class JwtPrivateKeyProvider implements ReadKeyMixin {
@Getter
public class JwtPrivateKeyProvider {
private final ResourceUtil resourceUtil;
private final Base64Util base64Util;

@Getter
private final JwtConfig jwtConfig;
private PrivateKey privateKey;

@PostConstruct
public void init() {
privateKey = readKey(
"classpath:keys/tzb_key.pkcs8.private",
"PRIVATE",
this::privateKeySpec,
this::privateKeyGenerator
);
}

private EncodedKeySpec privateKeySpec(String data) {
return new PKCS8EncodedKeySpec(base64Util.decode(data));
}

private PrivateKey privateKeyGenerator(KeyFactory kf, EncodedKeySpec spec) {
try {
return kf.generatePrivate(spec);
} catch (InvalidKeySpecException e) {
throw new JwtInitializationException(e);
var keyBytes = resourceUtil.readAllBytes(jwtConfig.getPrivateKeyFilepath());
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(spec);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
throw new JwtInitializationException(ex);
}
}

public PrivateKey get() {
return privateKey;
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,39 @@
package com.kshitijpatil.tazabazar.security.jwt;

import com.kshitijpatil.tazabazar.utils.Base64Util;
import com.kshitijpatil.tazabazar.utils.ReadKeyMixin;
import com.kshitijpatil.tazabazar.configuration.JwtConfig;
import com.kshitijpatil.tazabazar.utils.ResourceUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.security.KeyFactory;
import java.io.IOException;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

@Component
@RequiredArgsConstructor
public class JwtPublicKeyProvider implements ReadKeyMixin {
public class JwtPublicKeyProvider {
@Getter
private final ResourceUtil resourceUtil;
private final Base64Util base64Util;

@Getter
private final JwtConfig jwtConfig;
private PublicKey publicKey;

@PostConstruct
public void init() {
publicKey = readKey(
"classpath:keys/tzb_key.x509.public",
"PUBLIC",
this::publicKeySpec,
this::publicKeyGenerator
);
}

private EncodedKeySpec publicKeySpec(String data) {
return new X509EncodedKeySpec(base64Util.decode(data));
}

private PublicKey publicKeyGenerator(KeyFactory kf, EncodedKeySpec spec) {
try {
return kf.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new JwtInitializationException(e);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
var keyInputStream = resourceUtil.getInputStream(jwtConfig.getPublicKeyFilepath());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(keyInputStream);
publicKey = cert.getPublicKey();
} catch (CertificateException | IOException ex) {
throw new JwtInitializationException(ex);
}
}

public PublicKey get() {
return publicKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

Expand All @@ -24,4 +25,13 @@ public String asString(String resourcePath) throws IOException {
return FileCopyUtils.copyToString(reader);
}
}

public InputStream getInputStream(String resourcePath) throws IOException {
Resource resource = resourceLoader.getResource(resourcePath);
return resource.getInputStream();
}

public byte[] readAllBytes(String resourcePath) throws IOException {
return getInputStream(resourcePath).readAllBytes();
}
}
2 changes: 2 additions & 0 deletions springboot-app/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ springdoc.api-docs.enabled=true
springdoc.api-docs.path=/v3/api-docs
springdoc.swagger-ui.path=/swagger-ui
jwt.expiration-in-minutes=15
jwt.private-key-filepath=classpath:keys/tzb_key.pkcs8.private
jwt.public-key-filepath=classpath:keys/tzb_key.x509.public
spring.datasource.url=jdbc:postgresql://${env.DB_HOST}:5432/${env.DB_NAME}
spring.datasource.username=${env.DB_USERNAME}
spring.datasource.password=${env.DB_PASSWORD}
Expand Down

0 comments on commit 22e0787

Please sign in to comment.