diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index df13241ee..0185b73f6 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -31,48 +31,59 @@ 1.8 ${java.version} ${java.version} + 5.4.1 + 5.11.0 + 2.2.0 + 3.1.7 + 1.0 + 0.12.6 + 3.12.4 org.junit.jupiter - junit-jupiter-api - 5.11.0 + junit-jupiter-api + ${junit-version} test org.junit.jupiter junit-jupiter-engine - 5.11.0 + ${junit-version} test io.github.cdimascio dotenv-java - 2.2.0 + ${cdimascio-version} org.glassfish.jersey.core jersey-common - 3.1.7 + ${jersey-version} org.mockito mockito-core - 3.12.4 + ${mockito-version} test com.affinidi.tdk common - 1.0 + ${affinidi-common-version} io.jsonwebtoken jjwt-api - 0.12.6 + ${jsonwebtoken-version} + + + org.apache.httpcomponents.client5 + httpclient5 + ${apache-http-version} - diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index 93e3ddb13..aba3326b3 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -1,7 +1,12 @@ package com.affinidi.tdk.authProvider; +import java.util.UUID; + +import com.affinidi.tdk.authProvider.helper.JwtUtil; +import com.affinidi.tdk.authProvider.type.IotaJwtOutput; import com.affinidi.tdk.common.EnvironmentUtil; + public class AuthProvider { private final String projectId; @@ -25,13 +30,16 @@ public class AuthProvider { this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); } - public boolean shouldRefreshToken(){ - // TODO Implement shouldRefreshToken - return false; + public boolean shouldRefreshToken() { + if(this.projectScopeToken == null){ + return true; + } + return !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); } public String fetchProjectScopedToken(){ boolean tokenFetchRequired = shouldRefreshToken(); + if(tokenFetchRequired){ this.projectScopeToken = fetchProjectScopedToken(apiGatewayUrl, projectId, tokenId, apiGatewayUrl, privateKey, passphrase, keyId); } @@ -50,6 +58,16 @@ private String fetchProjectScopedToken(String apiGatewayUrl, String projectId, S return null; } + public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSessionId) throws Exception { + + String iotaTokenId = "token/"+tokenId; + String iotaSessionID = (iotaSessionId != null) ? iotaSessionId : UUID.randomUUID().toString(); + + String iotaJwt = JwtUtil.signIotaPayload(iotaTokenId, did, privateKey, passphrase, keyId, projectId, iotaConfigId, iotaSessionID); + + return new IotaJwtOutput(iotaSessionID, iotaJwt); + } + public static class Configurations{ private String projectId; private String tokenId; @@ -84,7 +102,6 @@ public AuthProvider build() throws Exception{ } return new AuthProvider(this); } - } public String getProjectId() { @@ -107,7 +124,6 @@ public String getPassphrase() { return passphrase; } - public String getApiGatewayUrl() { return apiGatewayUrl; } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java index b9a0dcb92..1a7fc8319 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -1,21 +1,140 @@ package com.affinidi.tdk.authProvider.helper; +import java.security.AlgorithmParameters; +import java.security.Key; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Logger; +import javax.crypto.Cipher; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; public class JwtUtil { + private static Logger logger = Logger.getLogger(JwtUtil.class.getName()); - public String signPayload(String tokenId, String audience, String privateKey, String passphrase, String keyId){ - - return null; + public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId) throws Exception{ + + long issueTimeInSeconds = System.currentTimeMillis() / 1000; + + Map claims = new HashMap<>(); + claims.put("iss", tokenId); + claims.put("sub", tokenId); + claims.put("aud", audience); + claims.put("jti", UUID.randomUUID().toString()); + claims.put("exp", issueTimeInSeconds + (5 * 60)); + claims.put("iat", issueTimeInSeconds); + + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } + public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, + String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws Exception{ + long issueTimeInSeconds = System.currentTimeMillis() / 1000; + + Map claims = new HashMap<>(); + claims.put("iss", tokenId); + claims.put("sub", tokenId); + claims.put("kid", tokenId); + claims.put("aud", audience); + claims.put("jti", UUID.randomUUID().toString()); + claims.put("exp", issueTimeInSeconds + (5 * 60)); + claims.put("iat", issueTimeInSeconds); + claims.put("project_id", projectId); + claims.put("iota_configuration_id", iotaConfigId); + claims.put("iota_session_id", iotaSessionId); + claims.put("scope", "iota_channel"); + + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); } - public static String fetchPublicKey(String apiGatewayUrl){ + public static PublicKey fetchPublicKey(String apiGatewayUrl){ + // TODO Implement fetchPublicKey to call the apiGateway + // and extract public key from the received jwk + return null; } - public static boolean validateToken(String token, String publicKey){ + public static boolean validProjectTokenPresent(String token, String apiGatewayUrl){ + PublicKey publicKey = fetchPublicKey(apiGatewayUrl); + try{ + Jwts.parser().verifyWith(publicKey).build().parse(token); + }catch(ExpiredJwtException ejException){ + return true; + }catch(MalformedJwtException mjException){ + return true; + }catch(Exception eException){ + return true; + } return false; } + private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws Exception{ + + PrivateKey privateKey = null; + try{ + if (!passphrase.isEmpty()) { + privateKey = getPrivateKeyFromString(privateKeyString); + } else { + privateKey = getEncryptedPrivateKeyFromString(privateKeyString, passphrase); + } + }catch(Exception exception){ + logger.severe("extractPrivateKey : Issue in retrieving private key from the string passed"); + throw new Exception("Could not derive private key out of the configurations. Exception : "+exception.toString()); + } + + return privateKey; + } + + private static String generateJwt(PrivateKey privateKey, Map claims){ + return Jwts.builder() + .claims(claims) + .signWith(privateKey, Jwts.SIG.RS256) + .compact(); + } + + private static PrivateKey getPrivateKeyFromString(String privateKeyPEM) throws Exception { + String privateKeyContent = privateKeyPEM + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s", ""); + + byte[] encoded = Base64.getDecoder().decode(privateKeyContent); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + return keyFactory.generatePrivate(keySpec); + } + + private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPrivateKeyPEM, String password) + throws Exception { + String privateKeyContent = encryptedPrivateKeyPEM + .replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "") + .replace("-----END ENCRYPTED PRIVATE KEY-----", ""); + + byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent); + EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); + Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); + PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); + SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); + Key pbeKey = secFac.generateSecret(pbeKeySpec); + AlgorithmParameters algParams = encryptedPrivateKeyInfo.getAlgParameters(); + cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams); + PKCS8EncodedKeySpec pkcs8KeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + return keyFactory.generatePrivate(pkcs8KeySpec); + } + + + } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java new file mode 100644 index 000000000..da31b2092 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java @@ -0,0 +1,20 @@ +package com.affinidi.tdk.authProvider.type; + +public class IotaJwtOutput { + private final String iotaSessionId; + private final String iotaJwt; + + public IotaJwtOutput(String iotaSessionId, String iotaJwt){ + this.iotaJwt = iotaJwt; + this.iotaSessionId = iotaSessionId; + } + + public String getIotaSessionId() { + return iotaSessionId; + } + public String getIotaJwt() { + return iotaJwt; + } + + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java new file mode 100644 index 000000000..b0d721ead --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java @@ -0,0 +1,27 @@ +package com.affinidi.tdk.authProvider; + +import org.junit.jupiter.api.Test; + +public class AuthProviderTest { + + @Test + void testFetchProjectScopedToken() { + + } + + @Test + void testGetUserAccessToken() { + + } + + @Test + void testShouldRefreshToken() { + + } + + @Test + void testSignIotaJwt() { + + } + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java new file mode 100644 index 000000000..736c10d2d --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java @@ -0,0 +1,39 @@ +package com.affinidi.tdk.authProvider.helper; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class JwtUtilTest { + + + + @BeforeEach + void setUp() { + + } + + @Test + void testFetchPublicKey() { + + } + + @Test + void testSignIotaPayload() { + + } + + @Test + void testSignPayload() { + + } + + @Test + void testValidProjectTokenPresent() { + + } + + + + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java deleted file mode 100644 index 176e66f5e..000000000 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.affinidi.tdk.authProvider.helper; - - -import org.junit.jupiter.api.BeforeEach; - -public class ProjectTokenUtilTest { - - - - @BeforeEach - void setUp() { - - } - - - - -} diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class deleted file mode 100644 index 582b949c8..000000000 Binary files a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class and /dev/null differ diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class deleted file mode 100644 index f6d52cdbc..000000000 Binary files a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class and /dev/null differ