From 1f2b1607c816a4e9896186d2731a04d361abd94a Mon Sep 17 00:00:00 2001 From: Elex Date: Thu, 4 Jan 2024 01:15:47 +0900 Subject: [PATCH] - update jwt - add security --- json-web-token/build.gradle.kts | 6 +-- .../java/kr/pe/elex/examples/JwtSample.java | 21 +++++++-- .../java/kr/pe/elex/examples/SampleTest.java | 44 +++++++++++++----- security/README.md | 3 ++ security/build.gradle.kts | 14 ++++++ security/logback.xml | 20 +++++++++ .../java/kr/pe/elex/examples/AESKeys.java | 45 +++++++++++++++++++ .../java/kr/pe/elex/examples/RSAKeys.java | 44 ++++++++++++++++++ .../kr/pe/elex/examples/package-info.java | 8 ++++ .../java/kr/pe/elex/examples/AESKeysTest.java | 40 +++++++++++++++++ .../java/kr/pe/elex/examples/SampleTest.java | 21 +++++++++ settings.gradle.kts | 2 +- 12 files changed, 248 insertions(+), 20 deletions(-) create mode 100644 security/README.md create mode 100644 security/build.gradle.kts create mode 100644 security/logback.xml create mode 100644 security/src/main/java/kr/pe/elex/examples/AESKeys.java create mode 100644 security/src/main/java/kr/pe/elex/examples/RSAKeys.java create mode 100644 security/src/main/java/kr/pe/elex/examples/package-info.java create mode 100644 security/src/test/java/kr/pe/elex/examples/AESKeysTest.java create mode 100644 security/src/test/java/kr/pe/elex/examples/SampleTest.java diff --git a/json-web-token/build.gradle.kts b/json-web-token/build.gradle.kts index 773fd3e..c4b5b12 100644 --- a/json-web-token/build.gradle.kts +++ b/json-web-token/build.gradle.kts @@ -11,9 +11,9 @@ plugins { dependencies { // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api - implementation("io.jsonwebtoken:jjwt-api:0.11.2") + implementation("io.jsonwebtoken:jjwt-api:0.12.3") // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl - runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2") - runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2") + runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3") + runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3") } diff --git a/json-web-token/src/main/java/kr/pe/elex/examples/JwtSample.java b/json-web-token/src/main/java/kr/pe/elex/examples/JwtSample.java index 76b255f..0b5a3a4 100644 --- a/json-web-token/src/main/java/kr/pe/elex/examples/JwtSample.java +++ b/json-web-token/src/main/java/kr/pe/elex/examples/JwtSample.java @@ -29,12 +29,20 @@ public class JwtSample { } public static String generateToken() throws InvalidKeyException { - return Jwts.builder() + /*return Jwts.builder() .setHeaderParam(Header.TYPE, Header.JWT_TYPE) .setIssuer("Elex") .setExpiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS))) .claim("userId", 3) .signWith(Keys.hmacShaKeyFor(key)) + .compact();*/ + return Jwts.builder() + .header().type(Header.JWT_TYPE) + .and() + .issuer("Elex") + .expiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS))) + .claim("userId", 3) + .signWith(Keys.hmacShaKeyFor(key)) .compact(); } @@ -53,11 +61,16 @@ public class JwtSample { throws UnsupportedJwtException, MalformedJwtException, SignatureException, ExpiredJwtException, MissingClaimException, IncorrectClaimException { - return Jwts.parserBuilder() + /*return Jwts.parserBuilder() .setSigningKey(key) .requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 .build() - .parseClaimsJws(parseHeader(token)); + .parseClaimsJws(parseHeader(token));*/ + return Jwts.parser() + .verifyWith(Keys.hmacShaKeyFor(key)) + .requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 + .build() + .parseSignedClaims(parseHeader(token)); } /** @@ -84,7 +97,7 @@ public class JwtSample { final String authHeader = "Bearer " + token; Jws claims = parseToken(authHeader); System.out.println(claims); - final int userId = claims.getBody().get("userId", Integer.class); + final int userId = claims.getPayload().get("userId", Integer.class); System.out.println("User Id: " + userId); } } diff --git a/json-web-token/src/test/java/kr/pe/elex/examples/SampleTest.java b/json-web-token/src/test/java/kr/pe/elex/examples/SampleTest.java index 60f8dc1..413dfd2 100644 --- a/json-web-token/src/test/java/kr/pe/elex/examples/SampleTest.java +++ b/json-web-token/src/test/java/kr/pe/elex/examples/SampleTest.java @@ -9,10 +9,14 @@ package kr.pe.elex.examples; import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.io.CompressionAlgorithm; import io.jsonwebtoken.security.Keys; import org.junit.jupiter.api.Test; -import java.security.Key; +import javax.crypto.SecretKey; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Base64; @@ -24,23 +28,39 @@ import static org.junit.jupiter.api.Assertions.*; class SampleTest { @Test - void test(){ + void test() throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] key = new byte[32]; new Random().nextBytes(key); - final Key signingKey = Keys.hmacShaKeyFor(key); + //final SecretKey signingKey = Keys.hmacShaKeyFor(key); + KeyPair keyPair = Jwts.SIG.RS384.keyPair().build(); + PrivateKey privateKey = keyPair.getPrivate(); + PublicKey publicKey = keyPair.getPublic(); + String base64PublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded()); + String jwt = Jwts.builder() - .setHeaderParam(Header.TYPE, Header.JWT_TYPE) - .setIssuer("Elex") - .setExpiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS))) + .header().type("JWT") + .and() + .issuer("Elex") + .expiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS))) .claim("userId", 3) - .signWith(signingKey) + .signWith(keyPair.getPrivate()) .compact(); System.out.println(jwt); - String issuer = Jwts.parserBuilder() - .setSigningKey(signingKey) - //.requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 + + X509EncodedKeySpec ukeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey)); + PublicKey pKey = KeyFactory.getInstance("RSA").generatePublic(ukeySpec); + + Integer userId = Jwts.parser() + .verifyWith(pKey) + .requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 .build() - .parseClaimsJws(jwt).getBody().getIssuer(); - System.out.println(issuer); + .parseSignedClaims(jwt).getPayload().get("userId", Integer.class); + System.out.println(userId); + String alg = Jwts.parser() + .verifyWith(pKey) + .requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 + .build() + .parseSignedClaims(jwt).getHeader().getAlgorithm(); + System.out.println(alg); } } diff --git a/security/README.md b/security/README.md new file mode 100644 index 0000000..995502d --- /dev/null +++ b/security/README.md @@ -0,0 +1,3 @@ +# Security + +https://github.com/jwtk/jjwt diff --git a/security/build.gradle.kts b/security/build.gradle.kts new file mode 100644 index 0000000..5e78a24 --- /dev/null +++ b/security/build.gradle.kts @@ -0,0 +1,14 @@ +/* + * Examples for Java + * + * Copyright (c) 2023. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +plugins { + id("elex-java") +} + +dependencies { + +} diff --git a/security/logback.xml b/security/logback.xml new file mode 100644 index 0000000..74ff8a3 --- /dev/null +++ b/security/logback.xml @@ -0,0 +1,20 @@ + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/security/src/main/java/kr/pe/elex/examples/AESKeys.java b/security/src/main/java/kr/pe/elex/examples/AESKeys.java new file mode 100644 index 0000000..2f684bf --- /dev/null +++ b/security/src/main/java/kr/pe/elex/examples/AESKeys.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024. Elex. All Rights Reesrved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; + +public class AESKeys { + public static SecretKey generateSecretKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(128, new SecureRandom()); + return keyGenerator.generateKey(); + } + + public static SecretKey generateSecretKey(char[] password, byte[] salt, int iteration) throws NoSuchAlgorithmException, InvalidKeySpecException { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iteration); + return SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128") + .generateSecret(keySpec); + } + + public static String encodeSecretKey(final SecretKey secretKey){ + return Base64.getEncoder().encodeToString(secretKey.getEncoded()); + } + public static SecretKey decodeSecretKey(final String secretKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKey keySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKey), "AES"); + return keySpec; + } + + public static IvParameterSpec generateIV(byte[] iv){ + IvParameterSpec parameterSpec = new IvParameterSpec(iv); + return parameterSpec; + } +} diff --git a/security/src/main/java/kr/pe/elex/examples/RSAKeys.java b/security/src/main/java/kr/pe/elex/examples/RSAKeys.java new file mode 100644 index 0000000..96c017d --- /dev/null +++ b/security/src/main/java/kr/pe/elex/examples/RSAKeys.java @@ -0,0 +1,44 @@ +/* + * Examples for Java + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import lombok.extern.slf4j.Slf4j; + +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +@Slf4j +public class RSAKeys { + public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(2048, new SecureRandom()); + return keyPairGen.generateKeyPair(); + } + + public static String encodePrivateKey(final PrivateKey privateKey) { + return Base64.getEncoder().encodeToString(privateKey.getEncoded()); + } + + public static String encodePublicKey(final PublicKey publicKey) { + return Base64.getEncoder().encodeToString(publicKey.getEncoded()); + } + + public static PublicKey decodePublicKey(final String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)); + return KeyFactory.getInstance("RSA").generatePublic(keySpec); + } + + public static PrivateKey decodePrivateKey(final String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(privateKey)); + return KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } + + +} diff --git a/security/src/main/java/kr/pe/elex/examples/package-info.java b/security/src/main/java/kr/pe/elex/examples/package-info.java new file mode 100644 index 0000000..bfb14b8 --- /dev/null +++ b/security/src/main/java/kr/pe/elex/examples/package-info.java @@ -0,0 +1,8 @@ +/* + * Examples for Java + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; diff --git a/security/src/test/java/kr/pe/elex/examples/AESKeysTest.java b/security/src/test/java/kr/pe/elex/examples/AESKeysTest.java new file mode 100644 index 0000000..2b8f151 --- /dev/null +++ b/security/src/test/java/kr/pe/elex/examples/AESKeysTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024. Elex. All Rights Reesrved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import org.junit.jupiter.api.Test; + +import javax.crypto.SecretKey; + +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.*; + +class AESKeysTest { + + @Test + void test() throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKey secretKey = AESKeys.generateSecretKey(); + + String base64SecretKey = AESKeys.encodeSecretKey(secretKey); + + SecretKey secretKey1 = AESKeys.decodeSecretKey(base64SecretKey); + + assertEquals(secretKey, secretKey1); + } + @Test + void test1() throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] salt = new byte[8]; + new Random().nextBytes(salt); + SecretKey secretKey = AESKeys.generateSecretKey("Hello".toCharArray(), salt, 10); + + SecretKey secretKey1 = AESKeys.generateSecretKey("Hello".toCharArray(), salt, 10); + + assertEquals(secretKey, secretKey1); + } +} \ No newline at end of file diff --git a/security/src/test/java/kr/pe/elex/examples/SampleTest.java b/security/src/test/java/kr/pe/elex/examples/SampleTest.java new file mode 100644 index 0000000..d194c5a --- /dev/null +++ b/security/src/test/java/kr/pe/elex/examples/SampleTest.java @@ -0,0 +1,21 @@ +/* + * Examples for Java + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import org.junit.jupiter.api.Test; + +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +class SampleTest { + + @Test + void test() throws NoSuchAlgorithmException, InvalidKeySpecException { + + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 07afe4f..054e8a5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,5 +15,5 @@ include( "xml", "jackson", "jsoup", "markdown", "network", "httpd", "properties", "serial-io", "mustache", "thymeleaf", "locale", "quartz", "sysinfo", - "imaging", "stream", "sound", "midi", "gson" + "imaging", "stream", "sound", "midi", "gson", "security" )