Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,8 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.core.util.CryptoUtil;
import org.wso2.carbon.core.util.KeyStoreUtil;
import org.wso2.carbon.keystore.mgt.util.RealmServiceHolder;
import org.wso2.carbon.security.keystore.KeyStoreAdmin;
import org.wso2.carbon.user.core.service.RealmService;
Expand All @@ -36,13 +31,20 @@

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Date;


import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.EC_KEY_ALG;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.EC_SHA256;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_KEY_ALG;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_MD5;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_SHA1;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_SHA256;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_SHA384;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_SHA512;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairUtil.addKeyEntry;

/**
* This class is used to generate a key store for a tenant and store it in the governance registry.
Expand All @@ -56,18 +58,10 @@ public class KeyStoreGenerator {
private String password;
private static final String SIGNING_ALG = "Tenant.SigningAlgorithm";

// Supported signature algorithms for public certificate generation.
private static final String RSA_MD5 = "MD5withRSA";
private static final String RSA_SHA1 = "SHA1withRSA";
private static final String RSA_SHA256 = "SHA256withRSA";
private static final String RSA_SHA384 = "SHA384withRSA";
private static final String RSA_SHA512 = "SHA512withRSA";
private static final String[] signatureAlgorithms = new String[]{
RSA_MD5, RSA_SHA1, RSA_SHA256, RSA_SHA384, RSA_SHA512
};



public KeyStoreGenerator(int tenantId) throws KeyStoreMgtException {

this.tenantId = tenantId;
Expand All @@ -85,8 +79,12 @@ public void generateKeyStore() throws KeyStoreMgtException {
password = generatePassword();
KeyStore keyStore = KeystoreUtils.getKeystoreInstance(KeystoreUtils.StoreFileType.defaultFileType());
keyStore.load(null, password.toCharArray());
X509Certificate pubCert = generateKeyPair(keyStore);
persistKeyStore(keyStore, pubCert);
// RSA based key pair entry
X509Certificate pubCertRSA = addKeyEntry(tenantDomain, password, keyStore, tenantDomain,
RSA_KEY_ALG, getSignatureAlgorithm());
// EC P-256 based key pair entry
addKeyEntry(tenantDomain, password, keyStore, KeyStoreUtil.getTenantECKeyAlias(tenantDomain), EC_KEY_ALG, EC_SHA256);
persistKeyStore(keyStore, pubCertRSA);
} catch (Exception e) {
String msg = "Error while instantiating a keystore";
log.error(msg, e);
Expand Down Expand Up @@ -136,62 +134,6 @@ public boolean isKeyStoreExists(int tenantId) throws KeyStoreMgtException{
return false;
}

/**
* This method generates the keypair and stores it in the keystore
*
* @param keyStore A keystore instance
* @return Generated public key for the tenant
* @throws KeyStoreMgtException Error when generating key pair
*/
private X509Certificate generateKeyPair(KeyStore keyStore) throws KeyStoreMgtException {
try {
CryptoUtil.getDefaultCryptoUtil();
//generate key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

// Common Name and alias for the generated certificate
String commonName = "CN=" + tenantDomain + ", OU=None, O=None, L=None, C=None";

//generate certificates
X500Name distinguishedName = new X500Name(commonName);

Date notBefore = new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30);
Date notAfter = new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365 * 10));

SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
BigInteger serialNumber = new BigInteger(32, new SecureRandom());

X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(
distinguishedName,
serialNumber,
notBefore,
notAfter,
distinguishedName,
subPubKeyInfo
);

String algorithmName = getSignatureAlgorithm();
JcaContentSignerBuilder signerBuilder =
new JcaContentSignerBuilder(algorithmName).setProvider(getJCEProvider());
PrivateKey privateKey = keyPair.getPrivate();
X509Certificate x509Cert = new JcaX509CertificateConverter().setProvider(getJCEProvider())
.getCertificate(certificateBuilder.build(signerBuilder.build(privateKey)));

//add private key to KS
keyStore.setKeyEntry(tenantDomain, keyPair.getPrivate(), password.toCharArray(),
new java.security.cert.Certificate[]{x509Cert});
return x509Cert;
} catch (Exception ex) {
String msg = "Error while generating the certificate for tenant :" +
tenantDomain + ".";
log.error(msg, ex);
throw new KeyStoreMgtException(msg, ex);
}

}

/**
* Persist the keystore in the gov.registry
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.keystore.mgt.util;

/*
* Constants required for Tenant KeyPair Utility.
*/
public class TenantKeyPairConstants {

// Constants required for EC key pair generation
public static final String EC_KEY_ALG = "EC";
public static final String EC_SHA256 = "SHA256withECDSA";
public static final String EC_CURVE = "secp256r1";

// Supported signature algorithms for public certificate generation.
public static final String RSA_MD5 = "MD5withRSA";
public static final String RSA_SHA1 = "SHA1withRSA";
Comment on lines +31 to +32

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Consider removing or deprecating weak signature algorithm constants.

MD5withRSA and SHA1withRSA use hash functions that are no longer recommended for new cryptographic applications. Including them as constants may encourage continued use. If backward compatibility is required, consider annotating these with @Deprecated and adding documentation discouraging their use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@components/tenant-mgt/org.wso2.carbon.tenant.keystore.mgt/src/main/java/org/wso2/carbon/keystore/mgt/util/TenantKeyPairConstants.java`
around lines 31 - 32, The constants RSA_MD5 and RSA_SHA1 in the
TenantKeyPairConstants class use weak hash algorithms (MD5 and SHA1) that are no
longer recommended for cryptographic applications. If these constants are
required for backward compatibility, annotate both RSA_MD5 and RSA_SHA1 with the
`@Deprecated` annotation and add JavaDoc comments explaining why these algorithms
are weak and should not be used for new implementations. If backward
compatibility is not required, consider removing these constants entirely from
the class.

Source: Linters/SAST tools

public static final String RSA_SHA256 = "SHA256withRSA";
public static final String RSA_SHA384 = "SHA384withRSA";
public static final String RSA_SHA512 = "SHA512withRSA";

public static final String RSA_KEY_ALG = "RSA";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.keystore.mgt.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.wso2.carbon.core.util.CryptoUtil;
import org.wso2.carbon.keystore.mgt.KeyStoreMgtException;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.util.Date;

import static org.wso2.carbon.core.util.CryptoUtil.getJCEProvider;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.EC_CURVE;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.EC_KEY_ALG;
import static org.wso2.carbon.keystore.mgt.util.TenantKeyPairConstants.RSA_KEY_ALG;

/**
* Utility for provisioning and validating tenant specific key pairs required.
*/
public class TenantKeyPairUtil {

private static final Log LOG = LogFactory.getLog(TenantKeyPairUtil.class);

private TenantKeyPairUtil() {
}

/**
* Generate and store key pair with self-signed certificate in tenant keystore.
*
* @param tenantDomain tenant domain
* @param ksPassword keystore password
* @param keyStore tenant keystore
* @param alias key alias
* @param keyType key type
* @param sigAlgId signature algorithm ID
* @return generated certificate
* @throws KeyStoreMgtException keystore exception
*/
public static X509Certificate addKeyEntry(String tenantDomain, String ksPassword, KeyStore keyStore,
String alias, String keyType, String sigAlgId)
throws KeyStoreMgtException {

try {
CryptoUtil.getDefaultCryptoUtil();
KeyPairGenerator kpg;

if (RSA_KEY_ALG.equals(keyType)) {
kpg = KeyPairGenerator.getInstance(RSA_KEY_ALG);
kpg.initialize(2048);
} else if (EC_KEY_ALG.equals(keyType)) {
kpg = KeyPairGenerator.getInstance(EC_KEY_ALG);
kpg.initialize(new ECGenParameterSpec(EC_CURVE));
} else {
throw new IllegalArgumentException("Unsupported key type: " + keyType);
}

KeyPair keyPair = kpg.generateKeyPair();
X509Certificate cert = generateCertificate(tenantDomain, keyPair, sigAlgId);

keyStore.setKeyEntry(alias, keyPair.getPrivate(), ksPassword.toCharArray(),
new Certificate[]{cert});
return cert;
} catch (Exception e) {
String errorMsg = "Error while generating the certificate for tenant :" + tenantDomain + ".";
LOG.error(errorMsg, e);
throw new KeyStoreMgtException(errorMsg, e);
}
}

private static X509Certificate generateCertificate(String tenantDomain, KeyPair keyPair, String algorithmId)
throws Exception {

String commonName = "CN=" + tenantDomain + ", OU=None, O=None, L=None, C=None";
X500Name dn = new X500Name(commonName);

Date notBefore = new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30);
Date notAfter = new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365 * 10));
BigInteger serialNumber = new BigInteger(32, new SecureRandom());

PublicKey publicKey = keyPair.getPublic();
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());

X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
dn, serialNumber, notBefore, notAfter, dn, publicKeyInfo);
certBuilder.addExtension(Extension.subjectAlternativeName, false,
new GeneralNames(new GeneralName(GeneralName.dNSName, tenantDomain)));

JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(algorithmId);
signerBuilder.setProvider(getJCEProvider());

return new JcaX509CertificateConverter()
.setProvider(getJCEProvider())
.getCertificate(certBuilder.build(signerBuilder.build(keyPair.getPrivate())));
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@

<properties>
<!-- Carbon kernel version-->
<carbon.kernel.version>4.10.108</carbon.kernel.version>
<carbon.kernel.version>4.12.39</carbon.kernel.version>
<carbon.kernel.feature.version>${carbon.kernel.version}</carbon.kernel.feature.version>
<carbon.kernel.imp.pkg.version>[4.10.22, 5.0.0)</carbon.kernel.imp.pkg.version>
<carbon.kernel.registry.imp.pkg.version>[1.0.1, 2.0.0)</carbon.kernel.registry.imp.pkg.version>
Expand Down
Loading