Skip to content

Commit 4b4989c

Browse files
committed
Update jwt auth to use hmac secret
1 parent 84c3a68 commit 4b4989c

File tree

5 files changed

+80
-79
lines changed

5 files changed

+80
-79
lines changed

components/org.wso2.micro.integrator.initializer/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@
117117
<groupId>org.wso2.integration.transaction.counter</groupId>
118118
<artifactId>transaction-count-handler</artifactId>
119119
</dependency>
120+
<dependency>
121+
<groupId>org.wso2.orbit.com.nimbusds</groupId>
122+
<artifactId>nimbus-jose-jwt</artifactId>
123+
</dependency>
120124
</dependencies>
121125

122126
<build>
@@ -138,6 +142,10 @@
138142
org.wso2.micro.integrator.initializer.*;version="${project.version}"
139143
</Export-Package>
140144
<Import-Package>
145+
com.nimbusds.jose;version="${nimbus-jose.orbit.imp.pkg.version}",
146+
com.nimbusds.jose.jwk;version="${nimbus-jose.orbit.imp.pkg.version}",
147+
com.nimbusds.jose.crypto;version="${nimbus-jose.orbit.imp.pkg.version}",
148+
com.nimbusds.jwt;version="${nimbus-jose.orbit.imp.pkg.version}",
141149
org.wso2.micro.integrator.core.*;version="${project.version}",
142150
*;resolution:=optional
143151
</Import-Package>

components/org.wso2.micro.integrator.initializer/src/main/java/org/wso2/micro/integrator/initializer/dashboard/Constants.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,21 @@ private Constants() {
2424
public static final String ICP_CONFIG_ENABLED = "icp_config.enabled";
2525

2626
// JWT Configuration
27-
public static final String ICP_JWT_KEYSTORE_PATH = "icp_config.jwt_keystore_path";
28-
public static final String ICP_JWT_KEYSTORE_PASSWORD = "icp_config.jwt_keystore_password";
29-
public static final String ICP_JWT_KEY_ALIAS = "icp_config.jwt_key_alias";
30-
public static final String ICP_JWT_KEY_PASSWORD = "icp_config.jwt_key_password";
3127
public static final String ICP_JWT_ISSUER = "icp_config.jwt_issuer";
3228
public static final String ICP_JWT_AUDIENCE = "icp_config.jwt_audience";
29+
public static final String ICP_JWT_SCOPE = "icp_config.jwt_scope";
3330
public static final String ICP_JWT_EXPIRY_SECONDS = "icp_config.jwt_expiry_seconds";
31+
public static final String ICP_JWT_HMAC_SECRET = "icp_config.jwt_hmac_secret";
3432

3533
// Default ICP Configuration
3634
public static final String DEFAULT_ENVIRONMENT = "production";
3735
public static final String DEFAULT_PROJECT = "default";
3836
public static final String DEFAULT_COMPONENT = "default";
3937
public static final String DEFAULT_JWT_ISSUER = "icp-runtime-jwt-issuer";
4038
public static final String DEFAULT_JWT_AUDIENCE = "icp-server";
39+
public static final String DEFAULT_JWT_SCOPE = "runtime_agent";
4140
public static final long DEFAULT_JWT_EXPIRY_SECONDS = 3600;
41+
public static final String DEFAULT_JWT_HMAC_SECRET = "default-secret-key-at-least-32-characters-long-for-hs256";
4242
public static final String RUNTIME_TYPE_MI = "MI";
4343
public static final String RUNTIME_STATUS_RUNNING = "RUNNING";
4444

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.wso2.micro.integrator.initializer.dashboard;
2+
3+
import com.nimbusds.jose.JOSEException;
4+
import com.nimbusds.jose.JWSAlgorithm;
5+
import com.nimbusds.jose.JWSHeader;
6+
import com.nimbusds.jose.JWSSigner;
7+
import com.nimbusds.jose.crypto.MACSigner;
8+
import com.nimbusds.jwt.JWTClaimsSet;
9+
import com.nimbusds.jwt.SignedJWT;
10+
11+
import java.nio.charset.StandardCharsets;
12+
import java.util.Date;
13+
14+
public class HMACJWTTokenGenerator {
15+
16+
private final String hmacSecret;
17+
18+
public HMACJWTTokenGenerator(String hmacSecret) {
19+
if (hmacSecret == null || hmacSecret.getBytes(StandardCharsets.UTF_8).length < 32) {
20+
throw new IllegalArgumentException("HMAC secret must be at least 256 bits (32 bytes)");
21+
}
22+
this.hmacSecret = hmacSecret;
23+
}
24+
25+
/**
26+
* Generate JWT Token with HMAC SHA256
27+
*/
28+
public String generateToken(String issuer, String audience, String scope, long expiryTimeSeconds)
29+
throws JOSEException {
30+
31+
// Calculate expiry
32+
long expiryMillis = System.currentTimeMillis() + (expiryTimeSeconds * 1000);
33+
34+
// Build claims
35+
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
36+
.issuer(issuer)
37+
.audience(audience)
38+
.expirationTime(new Date(expiryMillis))
39+
.issueTime(new Date())
40+
.claim("scope", scope)
41+
.build();
42+
43+
// Create HMAC signer
44+
JWSSigner signer = new MACSigner(hmacSecret.getBytes(StandardCharsets.UTF_8));
45+
46+
// Create and sign JWT
47+
SignedJWT signedJWT = new SignedJWT(
48+
new JWSHeader.Builder(JWSAlgorithm.HS256).build(),
49+
claimsSet
50+
);
51+
52+
signedJWT.sign(signer);
53+
return signedJWT.serialize();
54+
}
55+
56+
}

components/org.wso2.micro.integrator.initializer/src/main/java/org/wso2/micro/integrator/initializer/dashboard/ICPHeartBeatComponent.java

Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -564,77 +564,20 @@ private static String calculateHash(JsonObject payload) {
564564
*/
565565
private static String generateOrGetCachedJwtToken() throws Exception {
566566
long currentTime = System.currentTimeMillis();
567-
568567
// Return cached token if it's still valid (with 5 minute buffer)
569568
if (cachedJwtToken != null && currentTime < (jwtTokenExpiry - 300000)) {
570569
return cachedJwtToken;
571570
}
572-
573-
// Generate new token
574-
cachedJwtToken = generateJwtToken();
575-
jwtTokenExpiry = currentTime + (getJwtExpirySeconds() * 1000);
576-
577-
return cachedJwtToken;
578-
}
579-
580-
/**
581-
* Generates a JWT token signed with RS256 algorithm using built-in Java libraries.
582-
*/
583-
private static String generateJwtToken() throws Exception {
584-
String keystorePath = getConfigValue(ICP_JWT_KEYSTORE_PATH,
585-
"repository/resources/security/wso2carbon.jks");
586-
String keystorePassword = getConfigValue(ICP_JWT_KEYSTORE_PASSWORD, "ballerina");
587-
String keyAlias = getConfigValue(ICP_JWT_KEY_ALIAS, "ballerina");
588-
String keyPassword = getConfigValue(ICP_JWT_KEY_PASSWORD, keystorePassword);
571+
String jwtHmacSecret = getConfigValue(ICP_JWT_HMAC_SECRET, DEFAULT_JWT_HMAC_SECRET);
572+
HMACJWTTokenGenerator hmacJWTTokenGenerator = new HMACJWTTokenGenerator(jwtHmacSecret);
589573
String issuer = getConfigValue(ICP_JWT_ISSUER, DEFAULT_JWT_ISSUER);
590574
String audience = getConfigValue(ICP_JWT_AUDIENCE, DEFAULT_JWT_AUDIENCE);
575+
String scope = getConfigValue(ICP_JWT_SCOPE, DEFAULT_JWT_SCOPE);
591576
long expirySeconds = getJwtExpirySeconds();
592-
593-
// Resolve keystore path relative to carbon.home if not absolute
594-
if (!keystorePath.startsWith("/")) {
595-
String carbonHome = System.getProperty("carbon.home");
596-
keystorePath = carbonHome + "/" + keystorePath;
597-
}
598-
599-
// Load keystore
600-
KeyStore keyStore = KeyStore.getInstance("JKS");
601-
try (FileInputStream fis = new FileInputStream(keystorePath)) {
602-
keyStore.load(fis, keystorePassword.toCharArray());
603-
}
604-
605-
// Get private key
606-
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray());
607-
608-
// Create JWT manually using built-in Java (matching Ballerina jwt:issue() output)
609-
long currentTimeSeconds = System.currentTimeMillis() / 1000;
610-
long expirationTime = currentTimeSeconds + expirySeconds;
611-
612-
// JWT Header
613-
JsonObject header = new JsonObject();
614-
header.addProperty("alg", "RS256");
615-
header.addProperty("typ", "JWT");
616-
String encodedHeader = Base64.getUrlEncoder().withoutPadding()
617-
.encodeToString(header.toString().getBytes("UTF-8"));
618-
619-
// JWT Payload (matching Ballerina structure)
620-
JsonObject payload = new JsonObject();
621-
payload.addProperty("iss", issuer);
622-
payload.addProperty("aud", audience);
623-
payload.addProperty("scope", "runtime_agent");
624-
payload.addProperty("iat", currentTimeSeconds);
625-
payload.addProperty("exp", expirationTime);
626-
String encodedPayload = Base64.getUrlEncoder().withoutPadding()
627-
.encodeToString(payload.toString().getBytes("UTF-8"));
628-
629-
// Create signature
630-
String signingInput = encodedHeader + "." + encodedPayload;
631-
java.security.Signature signature = java.security.Signature.getInstance("SHA256withRSA");
632-
signature.initSign(privateKey);
633-
signature.update(signingInput.getBytes("UTF-8"));
634-
byte[] signatureBytes = signature.sign();
635-
String encodedSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes);
636-
637-
return signingInput + "." + encodedSignature;
577+
// Generate new token
578+
cachedJwtToken = hmacJWTTokenGenerator.generateToken(issuer, audience, scope, expirySeconds);
579+
jwtTokenExpiry = currentTime + (expirySeconds * 1000);
580+
return cachedJwtToken;
638581
}
639582

640583
/**
@@ -1437,14 +1380,14 @@ private static JsonArray collectListeners() {
14371380
// HTTP Listener
14381381
JsonObject httpListener = new JsonObject();
14391382
httpListener.addProperty("protocol", "http");
1440-
httpListener.addProperty("port", Integer.toString(ConfigurationLoader.getInternalInboundHttpPort()));
1383+
httpListener.addProperty("port", ConfigurationLoader.getInternalInboundHttpPort());
14411384
httpListener.addProperty("host", "0.0.0.0");
14421385
listeners.add(httpListener);
14431386

14441387
// HTTPS Listener
14451388
JsonObject httpsListener = new JsonObject();
14461389
httpsListener.addProperty("protocol", "https");
1447-
httpsListener.addProperty("port", Integer.toString(ConfigurationLoader.getInternalInboundHttpsPort()));
1390+
httpsListener.addProperty("port", ConfigurationLoader.getInternalInboundHttpsPort());
14481391
httpsListener.addProperty("host", "0.0.0.0");
14491392
listeners.add(httpsListener);
14501393
} catch (Exception e) {

deployment-icp-sample.toml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ project = "sample_project"
4747

4848
# Component Identification
4949
# Specific component or service area within the project
50-
component = "sample_mi_component"
50+
component = "sample-mi-component"
5151

5252
# Heartbeat Configuration
5353
# Interval between heartbeat transmissions (in seconds)
@@ -57,18 +57,12 @@ heartbeat_interval = 10
5757
# ----------------------------------------
5858
# JWT Authentication Configuration
5959
# ----------------------------------------
60-
# Keystore Configuration
61-
# Path can be relative to $MI_HOME or absolute
62-
jwt_keystore_path = "repository/resources/security/wso2carbon.jks"
63-
jwt_keystore_password = "wso2carbon"
64-
jwt_key_alias = "wso2carbon"
65-
jwt_key_password = "wso2carbon"
66-
67-
# JWT Token Settings
6860
jwt_issuer = "icp-runtime-jwt-issuer"
6961
jwt_audience = "icp-server"
62+
jwt_scope = "runtime_agent"
7063
# Token validity in seconds (default: 3600 = 1 hour)
7164
jwt_expiry_seconds = 3600
65+
jwt_hmac_secret = "default-secret-key-at-least-32-characters-long-for-hs256"
7266

7367
# ----------------------------------------
7468
# Legacy Dashboard Configuration (Optional)

0 commit comments

Comments
 (0)