Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate IdentityKeyStoreResolver #2524

Merged
merged 7 commits into from
Oct 31, 2024
Merged
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 @@ -30,15 +30,15 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.core.IdentityKeyStoreResolver;
import org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.utils.security.KeystoreUtils;

import java.security.KeyStore;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -79,21 +79,22 @@ public String jwks() {

try {
final KeyStore keystore;
List<CertificateInfo> certificateInfoList = new ArrayList<>();

if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equalsIgnoreCase(tenantDomain)) {
KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(MultitenantConstants.SUPER_TENANT_ID);
keystore = keyStoreManager.getPrimaryKeyStore();
keystore = IdentityKeyStoreResolver.getInstance().getKeyStore(
tenantDomain, IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH);
} else {
try {
int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
IdentityTenantUtil.initializeRegistry(tenantId);
FrameworkUtils.startTenantFlow(tenantDomain);
KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId);
keystore = keyStoreManager.getKeyStore(generateKSNameFromDomainName(tenantDomain));
keystore = IdentityKeyStoreResolver.getInstance().getKeyStore(
tenantDomain, IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH);
} finally {
FrameworkUtils.endTenantFlow();
}
}
List<CertificateInfo> certificateInfoList = new ArrayList<>();
Enumeration enumeration = keystore.aliases();
while (enumeration.hasMoreElements()) {
String alias = (String) enumeration.nextElement();
Expand Down Expand Up @@ -250,16 +251,6 @@ private String logAndReturnError(String errorMesage, Exception e) {
return errorMesage;
}

/**
* This method generates the key store file name from the Domain Name.
*
* @return key store file name
*/
private String generateKSNameFromDomainName(String tenantDomain) {

return KeystoreUtils.getKeyStoreFileLocation(tenantDomain);
}

/**
* This method generates the base64 encoded certificate list from a Certificate array.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
import org.testng.annotations.Test;
import org.wso2.carbon.base.CarbonBaseConstants;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.core.IdentityKeyStoreResolver;
import org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
Expand All @@ -44,7 +44,6 @@
import org.wso2.carbon.identity.oauth2.keyidprovider.DefaultKeyIDProviderImpl;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.security.KeystoreUtils;

import java.io.FileInputStream;
import java.lang.reflect.Field;
Expand All @@ -58,7 +57,6 @@
import java.util.Map;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mockStatic;
Expand All @@ -69,17 +67,14 @@
@Listeners(MockitoTestNGListener.class)
public class JwksEndpointTest {

@Mock
ServerConfiguration serverConfiguration;

@Mock
OAuthServerConfiguration mockOAuthServerConfiguration;

@Mock
TokenPersistenceProcessor tokenPersistenceProcessor;

@Mock
KeyStoreManager mockKeyStoreManager;
IdentityKeyStoreResolver mockIdentityKeyStoreResolver;

private static final String CERT_THUMB_PRINT = "generatedCertThrumbPrint";
private static final String ALG = "RS256";
Expand Down Expand Up @@ -149,22 +144,16 @@ public void testJwks(String tenantDomain, int tenantId) throws Exception {
OAuthServerConfiguration.class);
MockedStatic<CarbonUtils> carbonUtils = mockStatic(CarbonUtils.class);
MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class);
MockedStatic<FrameworkUtils> frameworkUtils = mockStatic(FrameworkUtils.class);
MockedStatic<KeystoreUtils> keystoreUtils = mockStatic(KeystoreUtils.class);) {
MockedStatic<FrameworkUtils> frameworkUtils = mockStatic(FrameworkUtils.class);) {

Path keystorePath =
Paths.get(System.getProperty(CarbonBaseConstants.CARBON_HOME), "repository", "resources",
"security", "wso2carbon.jks");
keystoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("foo.com")).thenReturn("foo-com.jks");
mockOAuthServerConfiguration(oAuthServerConfiguration);

// When the OAuth2Util is mocked, OAuthServerConfiguration instance should be available.
try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
MockedStatic<KeyStoreManager> keyStoreManager = mockStatic(KeyStoreManager.class);
MockedStatic<IdentityKeyStoreResolver> identityKeyStoreResolver =
mockStatic(IdentityKeyStoreResolver.class);
MockedStatic<IdentityUtil> identityUtil = mockStatic(IdentityUtil.class)) {

carbonUtils.when(CarbonUtils::getServerConfiguration).thenReturn(serverConfiguration);

ThreadLocal<Map<String, Object>> threadLocalProperties = new ThreadLocal() {
protected Map<String, Object> initialValue() {

Expand Down Expand Up @@ -218,11 +207,16 @@ protected Map<String, Object> initialValue() {
.thenReturn("YmUwN2EzOGI3ZTI0Y2NiNTNmZWFlZjI5Mm" +
"VjZjdjZTYzZjI0M2MxNDQ1YjQwNjI3NjYyZmZlYzkwNzY0YjU4NQ");

keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(mockKeyStoreManager);
lenient().when(mockKeyStoreManager.getKeyStore("foo-com.jks")).thenReturn(
getKeyStoreFromFile("foo-com.jks", "foo.com"));
lenient().when(mockKeyStoreManager.getPrimaryKeyStore()).thenReturn(
getKeyStoreFromFile("wso2carbon.jks", "wso2carbon"));
identityKeyStoreResolver.when(() -> IdentityKeyStoreResolver.getInstance())
.thenReturn(mockIdentityKeyStoreResolver);

lenient().when(mockIdentityKeyStoreResolver
.getKeyStore("carbon.super", IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH))
.thenReturn(getKeyStoreFromFile("wso2carbon.jks", "wso2carbon"));
lenient().when(mockIdentityKeyStoreResolver
.getKeyStore("foo.com", IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH))
.thenReturn(getKeyStoreFromFile("foo-com.jks", "foo.com"));

identityUtil.when(() -> IdentityUtil.getProperty(ENABLE_X5C_IN_RESPONSE)).thenReturn("true");

String result = jwksEndpoint.jwks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.IdentityKeyStoreResolver;
import org.wso2.carbon.identity.core.util.IdentityCoreConstants;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
Expand All @@ -60,10 +59,8 @@
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.carbon.utils.security.KeystoreUtils;

import java.security.Key;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
Expand All @@ -72,12 +69,10 @@
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
* This class represents the JSON Web Token generator.
Expand Down Expand Up @@ -105,9 +100,6 @@ public class JWTTokenGenerator implements AuthorizationContextTokenGenerator {

private boolean enableSigning = true;

private static Map<Integer, Key> privateKeys = new ConcurrentHashMap<Integer, Key>();
private static Map<Integer, Certificate> publicCerts = new ConcurrentHashMap<Integer, Certificate>();

private ClaimCache claimsLocalCache;

public JWTTokenGenerator() {
Expand Down Expand Up @@ -319,7 +311,8 @@ protected SignedJWT signJWTWithRSA(SignedJWT signedJWT, JWSAlgorithm jwsAlgorith
int tenantId)
throws IdentityOAuth2Exception {
try {
Key privateKey = getPrivateKey(tenantDomain, tenantId);
Key privateKey = IdentityKeyStoreResolver.getInstance()
.getPrivateKey(tenantDomain, IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH);
JWSSigner signer = OAuth2Util.createJWSSigner((RSAPrivateKey) privateKey);
signedJWT.sign(signer);
return signedJWT;
Expand Down Expand Up @@ -407,8 +400,8 @@ private long getTTL() {
private String getThumbPrint(String tenantDomain, int tenantId) throws IdentityOAuth2Exception {

try {

Certificate certificate = getCertificate(tenantDomain, tenantId);
Certificate certificate = IdentityKeyStoreResolver.getInstance()
Binara-Sachin marked this conversation as resolved.
Show resolved Hide resolved
.getCertificate(tenantDomain, IdentityKeyStoreResolverConstants.InboundProtocol.OAUTH);

// TODO: maintain a hashmap with tenants' pubkey thumbprints after first initialization

Expand All @@ -429,94 +422,6 @@ private String getThumbPrint(String tenantDomain, int tenantId) throws IdentityO
}
}

private Key getPrivateKey(String tenantDomain, int tenantId) throws IdentityOAuth2Exception {

if (tenantDomain == null) {
tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
}

if (tenantId == 0) {
tenantId = OAuth2Util.getTenantId(tenantDomain);
}

Key privateKey = null;

if (!(privateKeys.containsKey(tenantId))) {

try {
IdentityTenantUtil.initializeRegistry(tenantId, tenantDomain);
} catch (IdentityException e) {
throw new IdentityOAuth2Exception("Error occurred while loading registry for tenant " + tenantDomain,
e);
}

// get tenant's key store manager
KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
// derive key store name
String fileName = KeystoreUtils.getKeyStoreFileLocation(tenantDomain);
// obtain private key
privateKey = tenantKSM.getPrivateKey(fileName, tenantDomain);

} else {
try {
privateKey = tenantKSM.getDefaultPrivateKey();
} catch (Exception e) {
log.error("Error while obtaining private key for super tenant", e);
}
}
if (privateKey != null) {
privateKeys.put(tenantId, privateKey);
}
} else {
privateKey = privateKeys.get(tenantId);
}
return privateKey;
}

private Certificate getCertificate(String tenantDomain, int tenantId) throws Exception {

if (tenantDomain == null) {
tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
}

if (tenantId == 0) {
tenantId = OAuth2Util.getTenantId(tenantDomain);
}

Certificate publicCert = null;

if (!(publicCerts.containsKey(tenantId))) {

try {
IdentityTenantUtil.initializeRegistry(tenantId, tenantDomain);
} catch (IdentityException e) {
throw new IdentityOAuth2Exception("Error occurred while loading registry for tenant " + tenantDomain,
e);
}

// get tenant's key store manager
KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

KeyStore keyStore = null;
if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
// derive key store name
String fileName = KeystoreUtils.getKeyStoreFileLocation(tenantDomain);
keyStore = tenantKSM.getKeyStore(fileName);
publicCert = keyStore.getCertificate(tenantDomain);
} else {
publicCert = tenantKSM.getDefaultPrimaryCertificate();
}
if (publicCert != null) {
publicCerts.put(tenantId, publicCert);
}
} else {
publicCert = publicCerts.get(tenantId);
}
return publicCert;
}

/**
* Helper method to hexify a byte array.
* TODO:need to verify the logic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
Expand Down Expand Up @@ -459,17 +458,16 @@ protected String signJWTWithRSA(JWTClaimsSet jwtClaimsSet, OAuthTokenReqMessageC

try {
String tenantDomain = resolveSigningTenantDomain(tokenContext, authorizationContext);
int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);

// Add claim with signer tenant to jwt claims set.
jwtClaimsSet = setSignerRealm(tenantDomain, jwtClaimsSet);

Key privateKey = getPrivateKey(tenantDomain, tenantId);
Key privateKey = getPrivateKey(tenantDomain);
JWSSigner signer = OAuth2Util.createJWSSigner((RSAPrivateKey) privateKey);
JWSHeader.Builder headerBuilder = new JWSHeader.Builder((JWSAlgorithm) signatureAlgorithm);
Certificate certificate = OAuth2Util.getCertificate(tenantDomain, tenantId);
Certificate certificate = OAuth2Util.getCertificate(tenantDomain);
String certThumbPrint = OAuth2Util.getThumbPrintWithPrevAlgorithm(certificate, false);
headerBuilder.keyID(OAuth2Util.getKID(OAuth2Util.getCertificate(tenantDomain, tenantId),
headerBuilder.keyID(OAuth2Util.getKID(OAuth2Util.getCertificate(tenantDomain),
(JWSAlgorithm) signatureAlgorithm, tenantDomain));

if (authorizationContext != null && authorizationContext.isSubjectTokenFlow()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,12 @@ public static X509Certificate resolveSignerCertificate(IdentityProvider idp) thr
X509Certificate x509Certificate;
String tenantDomain = getTenantDomain();
try {
x509Certificate = (X509Certificate) IdentityApplicationManagementUtil
.decodeCertificate(idp.getCertificate());
if (IdentityApplicationConstants.RESIDENT_IDP_RESERVED_NAME.equals(idp.getIdentityProviderName())) {
x509Certificate = (X509Certificate) OAuth2Util.getCertificate(tenantDomain);
} else {
x509Certificate = (X509Certificate) IdentityApplicationManagementUtil
.decodeCertificate(idp.getCertificate());
}
} catch (CertificateException e) {
throw new IdentityOAuth2Exception("Error occurred while decoding public certificate of Identity Provider "
+ idp.getIdentityProviderName() + " for tenant domain " + tenantDomain, e);
Binara-Sachin marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Loading
Loading