Skip to content

Commit

Permalink
feat: Added Jwt utility functions
Browse files Browse the repository at this point in the history
  • Loading branch information
priyanka-affinidi committed Jan 13, 2025
1 parent 096eea7 commit 800593e
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 37 deletions.
29 changes: 20 additions & 9 deletions packages/java/auth.provider/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,59 @@
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<apache-http-version>5.4.1</apache-http-version>
<junit-version>5.11.0</junit-version>
<cdimascio-version>2.2.0</cdimascio-version>
<jersey-version>3.1.7</jersey-version>
<affinidi-common-version>1.0</affinidi-common-version>
<jsonwebtoken-version>0.12.6</jsonwebtoken-version>
<mockito-version>3.12.4</mockito-version>
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>2.2.0</version>
<version>${cdimascio-version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>3.1.7</version>
<version>${jersey-version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<version>${mockito-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.affinidi.tdk</groupId>
<artifactId>common</artifactId>
<version>1.0</version>
<version>${affinidi-common-version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
<version>${jsonwebtoken-version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>${apache-http-version}</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -84,7 +102,6 @@ public AuthProvider build() throws Exception{
}
return new AuthProvider(this);
}

}

public String getProjectId() {
Expand All @@ -107,7 +124,6 @@ public String getPassphrase() {
return passphrase;
}


public String getApiGatewayUrl() {
return apiGatewayUrl;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, Object> 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<String, Object> 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<String, Object> 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);
}



}
Original file line number Diff line number Diff line change
@@ -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;
}


}
Original file line number Diff line number Diff line change
@@ -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() {

}

}
Original file line number Diff line number Diff line change
@@ -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() {

}




}

This file was deleted.

Binary file not shown.
Binary file not shown.

0 comments on commit 800593e

Please sign in to comment.