diff --git a/components/identity-core/org.wso2.carbon.identity.core/src/test/java/org/wso2/carbon/identity/core/util/IdentityUtilTest.java b/components/identity-core/org.wso2.carbon.identity.core/src/test/java/org/wso2/carbon/identity/core/util/IdentityUtilTest.java index e08b31d49668..cd833299e972 100644 --- a/components/identity-core/org.wso2.carbon.identity.core/src/test/java/org/wso2/carbon/identity/core/util/IdentityUtilTest.java +++ b/components/identity-core/org.wso2.carbon.identity.core/src/test/java/org/wso2/carbon/identity/core/util/IdentityUtilTest.java @@ -97,6 +97,7 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import static org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants.ErrorMessages.ERROR_CODE_ERROR_RETRIEVING_TENANT_PUBLIC_CERTIFICATE; import static org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants.ErrorMessages.ERROR_RETRIEVING_TENANT_CONTEXT_PUBLIC_CERTIFICATE_KEYSTORE_NOT_EXIST; @Listeners(MockitoTestNGListener.class) @@ -1132,7 +1133,8 @@ public void testValidateSignatureFromContextKeystore() throws Exception { assertTrue(result); } - @Test + @Test(description = "Validate signature when the context keystore does not exist. " + + "Expect the method to return false without throwing an exception.") public void testValidateSignatureFromContextKeystoreIfNotExists() throws Exception { String data = "testData"; @@ -1151,6 +1153,25 @@ public void testValidateSignatureFromContextKeystoreIfNotExists() throws Excepti assertFalse(result); } + @Test(description = "Validate signature when an unexpected exception occurs while retrieving the " + + "tenant's public certificate. Expect a SignatureException to be thrown.", + expectedExceptions = SignatureException.class) + public void testValidateSignatureFromContextKeystoreNegative() throws Exception { + + String data = "testData"; + byte[] signature = new byte[]{1, 2, 3}; + String tenantDomain = "carbon.super"; + String context = "cookie"; + + identityKeyStoreResolver.when(IdentityKeyStoreResolver::getInstance).thenReturn(mockIdentityKeyStoreResolver); + when(mockIdentityKeyStoreResolver.getCertificate(tenantDomain, null, context)) + .thenThrow(new IdentityKeyStoreResolverException + (ERROR_CODE_ERROR_RETRIEVING_TENANT_PUBLIC_CERTIFICATE.getCode(), + ERROR_CODE_ERROR_RETRIEVING_TENANT_PUBLIC_CERTIFICATE.getDescription())); + + IdentityUtil.validateSignatureFromTenant(data, signature, tenantDomain, context); + } + @Test public void testSignWithTenantKey() throws Exception { diff --git a/components/security-mgt/org.wso2.carbon.security.mgt/src/main/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImpl.java b/components/security-mgt/org.wso2.carbon.security.mgt/src/main/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImpl.java index 6b56e9290779..22f75c7e6905 100644 --- a/components/security-mgt/org.wso2.carbon.security.mgt/src/main/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImpl.java +++ b/components/security-mgt/org.wso2.carbon.security.mgt/src/main/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImpl.java @@ -29,6 +29,8 @@ import org.wso2.carbon.base.ServerConfiguration; import org.wso2.carbon.core.util.CryptoUtil; import org.wso2.carbon.core.util.KeyStoreManager; +import org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverConstants; +import org.wso2.carbon.identity.core.util.IdentityKeyStoreResolverException; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.security.keystore.KeyStoreManagementException; import org.wso2.carbon.utils.ServerConstants; @@ -127,9 +129,19 @@ private boolean isContextKeyStoreExists(String context, String tenantDomain, Key try { keyStoreManager.getKeyStore(keyStoreName); isKeyStoreExists = true; + } catch (SecurityException e) { + if (e.getMessage() != null && e.getMessage().contains("Key Store with a name: " + keyStoreName + + " does not exist.")) { + + String msg = "Key store not exits. Proceeding to create keystore : " + keyStoreName; + LOG.debug(msg + e.getMessage()); + } else { + String msg = "Error while checking the existence of keystore."; + throw new KeyStoreManagementException(msg, e); + } } catch (Exception e) { String msg = "Error while checking the existence of keystore."; - LOG.debug(msg + e.getMessage()); + throw new KeyStoreManagementException(msg, e); } return isKeyStoreExists; } @@ -165,12 +177,19 @@ private void persistContextKeyStore(KeyStore keyStore, String context, String te KeyStoreManager keyStoreManager) throws KeyStoreManagementException { String keyStoreName = generateContextKSNameFromDomainName(context, tenantDomain); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + char[] passwordChar = password.toCharArray(); try { - char[] passwordChar = password.toCharArray(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); keyStore.store(outputStream, passwordChar); outputStream.flush(); outputStream.close(); + } catch (Exception e) { + String msg = "Error occurred while storing the keystore or processing the public certificate for tenant: " + + tenantDomain + " and context: " + context + ". Ensure the keystore is valid and writable."; + throw new KeyStoreManagementException(msg, e); + } + + try { keyStoreManager.addKeyStore(outputStream.toByteArray(), keyStoreName, passwordChar, " ", KeystoreUtils.getKeyStoreFileType(tenantDomain), passwordChar); @@ -183,10 +202,6 @@ private void persistContextKeyStore(KeyStore keyStore, String context, String te String msg = "Error when adding a keyStore"; throw new KeyStoreManagementException(msg, e); } - } catch (Exception e) { - - String msg = "Error when processing keystore/pub. cert to be stored in registry"; - throw new KeyStoreManagementException(msg, e); } } diff --git a/components/security-mgt/org.wso2.carbon.security.mgt/src/test/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImplTest.java b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImplTest.java new file mode 100644 index 000000000000..a8dfb803f402 --- /dev/null +++ b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/java/org/wso2/carbon/security/keystore/service/IdentityKeyStoreGeneratorImplTest.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024, 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.security.keystore.service; + +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.stubbing.Answer; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.*; +import org.wso2.carbon.base.CarbonBaseConstants; +import org.wso2.carbon.core.util.KeyStoreManager; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.testutil.IdentityBaseTest; +import org.wso2.carbon.security.keystore.KeyStoreManagementException; +import org.wso2.carbon.utils.security.KeystoreUtils; + +import java.io.FileInputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Security; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@Listeners(MockitoTestNGListener.class) +public class IdentityKeyStoreGeneratorImplTest extends IdentityBaseTest { + + private static final String KEYSTORE_PASSWORD = "wso2carbon"; + + private IdentityKeyStoreGeneratorImpl identityKeyStoreGenerator; + + private MockedStatic identityTenantUtil; + @Mock + private KeyStoreManager keyStoreManager; + + @Mock + private KeyStore mockKeyStore; + + + @BeforeMethod + public void setUp() throws Exception { + + if (Security.getProvider("BC") == null) { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + + System.setProperty( + CarbonBaseConstants.CARBON_HOME, + Paths.get(System.getProperty("user.dir"), "src", "test", "resources").toString() + ); + identityTenantUtil = mockStatic(IdentityTenantUtil.class); + } + + @AfterMethod + public void tearDown() throws Exception { + + identityKeyStoreGenerator = null; + identityTenantUtil.close(); + } + + @Test(description = "Test the generation of a keystore for a given tenant domain and context if exits.") + public void testGenerateKeystoreIfExists() throws Exception { + + try (MockedStatic keyStoreManager = mockStatic(KeyStoreManager.class); + MockedStatic keyStoreUtils = mockStatic(KeystoreUtils.class)) { + + keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(this.keyStoreManager); + identityTenantUtil.when(()->IdentityTenantUtil.getTenantId("carbon.super")) + .thenReturn(-1234); + identityTenantUtil.when(() -> IdentityTenantUtil.initializeRegistry(anyInt())) + .thenAnswer((Answer) invocation -> null); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("carbon.super--cookie")) + .thenReturn("carbon-super--cookie.jks"); + when(this.keyStoreManager.getKeyStore("carbon-super--cookie.jks")) + .thenReturn(getKeyStoreFromFile("carbon-super--cookie.jks", KEYSTORE_PASSWORD)); + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + } + + /** + * Sets up the mock behavior for KeyStoreManager and KeystoreUtils. + * + * @param exceptionToThrow the exception to throw when `getKeyStore` is called. + * @throws Exception if any setup steps fail. + */ + private void setupKeyStoreMocksWithException(Exception exceptionToThrow) throws Exception { + try (MockedStatic keyStoreManager = mockStatic(KeyStoreManager.class); + MockedStatic keyStoreUtils = mockStatic(KeystoreUtils.class)) { + + keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(this.keyStoreManager); + identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId("carbon.super")).thenReturn(-1234); + identityTenantUtil.when(() -> IdentityTenantUtil.initializeRegistry(anyInt())) + .thenAnswer((Answer) invocation -> null); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("carbon.super--cookie")) + .thenReturn("wso2carbon--cookie.jks"); + + when(this.keyStoreManager.getKeyStore("wso2carbon--cookie.jks")).thenThrow(exceptionToThrow); + } + } + + @Test(description = "Test error creating a keystore for a given tenant domain and context with SecurityException.", + expectedExceptions = KeyStoreManagementException.class) + public void testGenerateKeystoreWithSecurityException() throws Exception { + + setupKeyStoreMocksWithException(new SecurityException("Error while creating keystore.")); + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + + @Test(description = "Test error creating a keystore for a given tenant domain and context with generic Exception.", + expectedExceptions = KeyStoreManagementException.class) + public void testGenerateKeystoreWithGenericException() throws Exception { + + setupKeyStoreMocksWithException(new Exception("Error while creating keystore.")); + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + + + @Test(description = "Test the generation of a keystore for a given tenant domain and context if not exits.") + public void testGenerateKeystoreIfNotExists() throws Exception { + + try (MockedStatic keyStoreManager = mockStatic(KeyStoreManager.class); + MockedStatic keyStoreUtils = mockStatic(KeystoreUtils.class)) { + + keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(this.keyStoreManager); + identityTenantUtil.when(()->IdentityTenantUtil.getTenantId("carbon.super")) + .thenReturn(-1234); + identityTenantUtil.when(() -> IdentityTenantUtil.initializeRegistry(anyInt())) + .thenAnswer((Answer) invocation -> null); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("carbon.super--cookie")) + .thenReturn("carbon-super--cookie.jks"); + when(this.keyStoreManager.getKeyStore("carbon-super--cookie.jks")) + .thenThrow(new SecurityException("Key Store with a name: carbon-super--cookie.jks" + + " does not exist.")); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileType("carbon.super")) + .thenReturn("JKS"); + keyStoreUtils.when(() -> KeystoreUtils.getKeystoreInstance("JKS")) + .thenReturn(this.mockKeyStore); + doNothing().when(this.mockKeyStore).setKeyEntry(anyString(), any(PrivateKey.class), any(), any()); + + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + } + + @Test(description = "Test the generation of a keystore for a given tenant domain and context if not exits.") + public void testGenerateKeystoreAlreadyExists() throws Exception { + + try (MockedStatic keyStoreManager = mockStatic(KeyStoreManager.class); + MockedStatic keyStoreUtils = mockStatic(KeystoreUtils.class)) { + + keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(this.keyStoreManager); + identityTenantUtil.when(()->IdentityTenantUtil.getTenantId("carbon.super")) + .thenReturn(-1234); + identityTenantUtil.when(() -> IdentityTenantUtil.initializeRegistry(anyInt())) + .thenAnswer((Answer) invocation -> null); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("carbon.super--cookie")) + .thenReturn("carbon-super--cookie.jks"); + when(this.keyStoreManager.getKeyStore("carbon-super--cookie.jks")) + .thenThrow(new SecurityException("Key Store with a name: carbon-super--cookie.jks" + + " does not exist.")); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileType("carbon.super")) + .thenReturn("JKS"); + keyStoreUtils.when(() -> KeystoreUtils.getKeystoreInstance("JKS")) + .thenReturn(this.mockKeyStore); + doNothing().when(this.mockKeyStore).setKeyEntry(anyString(), any(PrivateKey.class), any(), any()); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileExtension("carbon.super")) + .thenReturn(".jks"); + doThrow(new SecurityException("Key store carbon-super--cookie.jks already available")) + .when(this.keyStoreManager) + .addKeyStore( + any(byte[].class), // Match any byte array + anyString(), // Match any String + any(char[].class), // Match any char array + anyString(), // Match any String + anyString(), // Match any String + any(char[].class) // Match any char array + ); + + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + } + + @Test(description = "Test the generation of a keystore for a given tenant domain and context if not exits.", + expectedExceptions = KeyStoreManagementException.class) + public void testGenerateKeystoreIfNotExistsNegative() throws Exception { + + try (MockedStatic keyStoreManager = mockStatic(KeyStoreManager.class); + MockedStatic keyStoreUtils = mockStatic(KeystoreUtils.class)) { + + keyStoreManager.when(() -> KeyStoreManager.getInstance(anyInt())).thenReturn(this.keyStoreManager); + identityTenantUtil.when(()->IdentityTenantUtil.getTenantId("carbon.super")) + .thenReturn(-1234); + identityTenantUtil.when(() -> IdentityTenantUtil.initializeRegistry(anyInt())) + .thenAnswer((Answer) invocation -> null); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileLocation("carbon.super--cookie")) + .thenReturn("carbon-super--cookie.jks"); + when(this.keyStoreManager.getKeyStore("carbon-super--cookie.jks")) + .thenThrow(new SecurityException("Key Store with a name: carbon-super--cookie.jks" + + " does not exist.")); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileType("carbon.super")) + .thenReturn("JKS"); + keyStoreUtils.when(() -> KeystoreUtils.getKeystoreInstance("JKS")) + .thenReturn(this.mockKeyStore); + doNothing().when(this.mockKeyStore).setKeyEntry(anyString(), any(PrivateKey.class), any(), any()); + keyStoreUtils.when(() -> KeystoreUtils.getKeyStoreFileExtension("carbon.super")) + .thenReturn(".jks"); + doThrow(new SecurityException("Error while adding keystore")) + .when(this.keyStoreManager) + .addKeyStore( + any(byte[].class), // Match any byte array + anyString(), // Match any String + any(char[].class), // Match any char array + anyString(), // Match any String + anyString(), // Match any String + any(char[].class) // Match any char array + ); + + identityKeyStoreGenerator = new IdentityKeyStoreGeneratorImpl(); + identityKeyStoreGenerator.generateKeyStore("carbon.super", "cookie"); + } + } + + + private Path createPath(String keystoreName) { + + return Paths.get(System.getProperty(CarbonBaseConstants.CARBON_HOME), "repository", + "resources", "security", keystoreName); + } + + private KeyStore getKeyStoreFromFile(String keystoreName, String password) throws Exception { + + Path tenantKeystorePath = createPath(keystoreName); + FileInputStream file = new FileInputStream(tenantKeystorePath.toString()); + KeyStore keystore = KeyStore.getInstance("JKS"); + keystore.load(file, password.toCharArray()); + return keystore; + } +} diff --git a/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/repository/resources/security/carbon-super--cookie.jks b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/repository/resources/security/carbon-super--cookie.jks new file mode 100644 index 000000000000..d5af4f42973a Binary files /dev/null and b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/repository/resources/security/carbon-super--cookie.jks differ diff --git a/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/testng.xml b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/testng.xml index ccc588beafc0..b74b69a95034 100644 --- a/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/testng.xml +++ b/components/security-mgt/org.wso2.carbon.security.mgt/src/test/resources/testng.xml @@ -23,6 +23,7 @@ +