Skip to content

Commit

Permalink
Merge pull request #13393 from SavinduDimal/mtls-cert-chain-tests
Browse files Browse the repository at this point in the history
[DNM] Add integration tests for mutual ssl certificate chain validation
  • Loading branch information
tharikaGitHub committed Mar 28, 2024
2 parents 46760fc + 2bb5a39 commit e131493
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved.
*
* 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.am.integration.tests.api.lifecycle;

import org.apache.commons.httpclient.HttpStatus;
import org.json.JSONException;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import org.wso2.am.integration.clients.publisher.api.ApiException;
import org.wso2.am.integration.clients.publisher.api.v1.dto.APIDTO;
import org.wso2.am.integration.clients.publisher.api.v1.dto.APIOperationsDTO;
import org.wso2.am.integration.test.utils.APIManagerIntegrationTestException;
import org.wso2.am.integration.test.utils.base.APIMIntegrationConstants;
import org.wso2.am.integration.test.utils.bean.APIRequest;
import org.wso2.am.integration.test.utils.http.HTTPSClientUtils;
import org.wso2.carbon.automation.engine.annotations.ExecutionEnvironment;
import org.wso2.carbon.automation.engine.annotations.SetEnvironment;
import org.wso2.carbon.automation.engine.context.TestUserMode;
import org.wso2.carbon.automation.test.utils.http.client.HttpResponse;
import org.wso2.carbon.integration.common.utils.exceptions.AutomationUtilException;
import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceUserStoreExceptionException;
import org.wso2.carbon.user.core.UserStoreException;

import javax.xml.xpath.XPathExpressionException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SetEnvironment(executionEnvironments = { ExecutionEnvironment.STANDALONE })
public class APISecurityMutualSSLCertificateChainValidationTestCase extends APIManagerLifecycleBaseTest {

private final String rootCertAPI = "rootCertAPI";
private final String intermediateCertAPI = "intermediateCertAPI";
private final String API_END_POINT_METHOD = "/customers/123";
private final String API_VERSION_1_0_0 = "1.0.0";
private final String API_END_POINT_POSTFIX_URL = "jaxrs_basic/services/customers/customerservice/";
private String apiEndPointUrl;
private String apiId1, apiId2;

@DataProvider
public static Object[][] userModeDataProvider() {

return new Object[][] { new Object[] { TestUserMode.SUPER_TENANT_ADMIN },
new Object[] { TestUserMode.TENANT_ADMIN } };
}

@Factory(dataProvider = "userModeDataProvider")
public APISecurityMutualSSLCertificateChainValidationTestCase(TestUserMode userMode) {

this.userMode = userMode;
}

@BeforeClass(alwaysRun = true)
public void initialize() throws APIManagerIntegrationTestException, IOException, ApiException,
org.wso2.am.integration.clients.store.api.ApiException, XPathExpressionException, AutomationUtilException,
InterruptedException, JSONException, RemoteUserStoreManagerServiceUserStoreExceptionException,
UserStoreException {

super.init(userMode);
apiEndPointUrl = backEndServerUrl.getWebAppURLHttp() + API_END_POINT_POSTFIX_URL;

APIRequest apiRequest1 = new APIRequest(rootCertAPI, rootCertAPI, new URL(apiEndPointUrl));
apiRequest1.setVersion(API_VERSION_1_0_0);
apiRequest1.setTiersCollection(APIMIntegrationConstants.API_TIER.UNLIMITED);
apiRequest1.setTier(APIMIntegrationConstants.API_TIER.UNLIMITED);
apiRequest1.setTags(API_TAGS);
apiRequest1.setVisibility(APIDTO.VisibilityEnum.PUBLIC.getValue());
apiRequest1.setProvider(user.getUserName());
APIOperationsDTO apiOperationsDTO1 = new APIOperationsDTO();
apiOperationsDTO1.setVerb("GET");
apiOperationsDTO1.setTarget("/customers/{id}");
apiOperationsDTO1.setAuthType("Application & Application User");
apiOperationsDTO1.setThrottlingPolicy("Unlimited");

List<APIOperationsDTO> operationsDTOS = new ArrayList<>();
operationsDTOS.add(apiOperationsDTO1);
apiRequest1.setOperationsDTOS(operationsDTOS);

List<String> securitySchemes = new ArrayList<>();
securitySchemes.add("mutualssl");
securitySchemes.add("mutualssl_mandatory");
apiRequest1.setSecurityScheme(securitySchemes);
apiRequest1.setDefault_version("true");
apiRequest1.setHttps_checked("https");
apiRequest1.setHttp_checked(null);
apiRequest1.setDefault_version_checked("true");
HttpResponse response1 = restAPIPublisher.addAPI(apiRequest1);
apiId1 = response1.getData();

String rootCertPath = getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_root.cer";
restAPIPublisher.uploadCertificate(new File(rootCertPath), "cert_chain_root", apiId1,
APIMIntegrationConstants.API_TIER.UNLIMITED);
createAPIRevisionAndDeployUsingRest(apiId1, restAPIPublisher);

APIRequest apiRequest2 = new APIRequest(intermediateCertAPI, intermediateCertAPI, new URL(apiEndPointUrl));
apiRequest2.setVersion(API_VERSION_1_0_0);
apiRequest2.setTiersCollection(APIMIntegrationConstants.API_TIER.UNLIMITED);
apiRequest2.setTier(APIMIntegrationConstants.API_TIER.UNLIMITED);
apiRequest2.setTags(API_TAGS);
apiRequest2.setVisibility(APIDTO.VisibilityEnum.PUBLIC.getValue());
apiRequest2.setProvider(user.getUserName());
apiRequest2.setOperationsDTOS(operationsDTOS);
apiRequest2.setSecurityScheme(securitySchemes);
apiRequest2.setDefault_version("true");
apiRequest2.setHttps_checked("https");
apiRequest2.setHttp_checked(null);
apiRequest2.setDefault_version_checked("true");
HttpResponse response2 = restAPIPublisher.addAPI(apiRequest2);
apiId2 = response2.getData();

String intermediateCertPath = getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_intermediate.cer";
restAPIPublisher.uploadCertificate(new File(intermediateCertPath), "cert_chain_intermediate", apiId2,
APIMIntegrationConstants.API_TIER.UNLIMITED);
createAPIRevisionAndDeployUsingRest(apiId2, restAPIPublisher);

waitForAPIDeploymentSync(user.getUserName(), rootCertAPI, API_VERSION_1_0_0,
APIMIntegrationConstants.IS_API_EXISTS);
waitForAPIDeploymentSync(user.getUserName(), intermediateCertAPI, API_VERSION_1_0_0,
APIMIntegrationConstants.IS_API_EXISTS);

// wait until certificates loaded
Thread.sleep(120000);
}

@Test(description = "Invoke mutual SSL only API with not supported certificate")
public void testAPIInvocationWithMutualSSLOnlyAPINegative()
throws IOException, XPathExpressionException, NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, UnrecoverableKeyException {

Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("accept", "text/xml");
HttpResponse response = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "test.jks",
getAPIInvocationURLHttps(rootCertAPI, API_VERSION_1_0_0) + API_END_POINT_METHOD, requestHeaders);
HttpResponse defaultResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "test.jks", getAPIInvocationURLHttps(rootCertAPI) + API_END_POINT_METHOD,
requestHeaders);
Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED);
Assert.assertEquals(defaultResponse.getResponseCode(), HttpStatus.SC_UNAUTHORIZED);
}

@Test(description = "API invocation with mutual ssl mandatory", dependsOnMethods = "testAPIInvocationWithMutualSSLOnlyAPINegative")
public void testAPIInvocationWithMutualSSLMandatory()
throws IOException, XPathExpressionException, NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, UnrecoverableKeyException {

Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("accept", "text/xml");

// Using root certificate
HttpResponse rootCertResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_root.jks",
getAPIInvocationURLHttps(rootCertAPI, API_VERSION_1_0_0) + API_END_POINT_METHOD, requestHeaders);
Assert.assertEquals(rootCertResponse.getResponseCode(), HttpStatus.SC_OK, "Mutual SSL Authentication has not succeed");

// Using client certificate with certificate chain
HttpResponse clientCertResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_client.jks",
getAPIInvocationURLHttps(rootCertAPI, API_VERSION_1_0_0) + API_END_POINT_METHOD, requestHeaders);
Assert.assertEquals(clientCertResponse.getResponseCode(), HttpStatus.SC_OK, "Mutual SSL Authentication has not succeed");

// Using client certificate with head only exported certificate
HttpResponse headOnlyClientCertResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_client_head_only.jks",
getAPIInvocationURLHttps(rootCertAPI, API_VERSION_1_0_0) + API_END_POINT_METHOD, requestHeaders);
Assert.assertEquals(headOnlyClientCertResponse.getResponseCode(), HttpStatus.SC_OK, "Mutual SSL Authentication has not succeed");

// For default API version with root certificate
HttpResponse defaultRootCertResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_root.jks",
getAPIInvocationURLHttps(rootCertAPI) + API_END_POINT_METHOD, requestHeaders);
Assert.assertEquals(defaultRootCertResponse.getResponseCode(), HttpStatus.SC_OK,
"Mutual SSL Authentication has not succeed");

// For default API version with client certificate
HttpResponse defaultClientCertResponse = HTTPSClientUtils.doMutulSSLGet(
getAMResourceLocation() + File.separator + "lifecycletest" + File.separator + "mutualssl"
+ File.separator + "cert_chain_client.jks",
getAPIInvocationURLHttps(rootCertAPI) + API_END_POINT_METHOD, requestHeaders);
Assert.assertEquals(defaultClientCertResponse.getResponseCode(), HttpStatus.SC_OK,
"Mutual SSL Authentication has not succeed");
}

@AfterClass(alwaysRun = true)
public void cleanUpArtifacts() throws Exception {

restAPIPublisher.deleteAPI(apiId1);
restAPIPublisher.deleteAPI(apiId2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,7 @@ password = "${admin.password}"

[apim.sync_runtime_artifacts.gateway.skip_list]
apis = ["admin--git2231head_v1.0.0.xml","admin--PizzaShackAPI_v1.0.0.xml","admin--ScriptMediatorAPI_v1.0.xml",
"APIThrottleBackendAPI.xml","BackEndSecurity.xml","DigestAuth_API.xml","git2231.xml","HttpPATCHSupport_API.xml","JWKS-Backend.xml","JWTBackendAPI.xml","multiVSR_v1.0.0.xml","Response_API_1.xml","Response_API_2.xml","Response_Custom_API.xml","Response_Error_API.xml","Response_Loc_API.xml","SpecialCRN_v1.0.0.xml","status_code_204_API.xml","stockquote.xml","XML_API.xml","Version1.xml","Version2.xml","schemaValidationAPI.xml"]
"APIThrottleBackendAPI.xml","BackEndSecurity.xml","DigestAuth_API.xml","git2231.xml","HttpPATCHSupport_API.xml","JWKS-Backend.xml","JWTBackendAPI.xml","multiVSR_v1.0.0.xml","Response_API_1.xml","Response_API_2.xml","Response_Custom_API.xml","Response_Error_API.xml","Response_Loc_API.xml","SpecialCRN_v1.0.0.xml","status_code_204_API.xml","stockquote.xml","XML_API.xml","Version1.xml","Version2.xml","schemaValidationAPI.xml"]

[apimgt.mutual_ssl]
enable_certificate_chain_validation = true
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID6DCCAtCgAwIBAgIUfUON+4ekletjetDwaT0BjeU3pVswDQYJKoZIhvcNAQEL
BQAwZzELMAkGA1UEBhMCTEsxDjAMBgNVBAgMBWFiY2RlMQ4wDAYDVQQHDAVhYmNk
ZTEOMAwGA1UECgwFYWJjZGUxDjAMBgNVBAsMBWFiY2RlMRgwFgYDVQQDDA9jZXJ0
LWNoYWluLXJvb3QwHhcNMjQwMjI2MTE0MDQ1WhcNMjgxMjMxMTgyOTU5WjBvMQsw
CQYDVQQGEwJMSzEOMAwGA1UECAwFYWJjZGUxDjAMBgNVBAcMBWFiY2RlMQ4wDAYD
VQQKDAVhYmNkZTEOMAwGA1UECwwFYWJjZGUxIDAeBgNVBAMMF2NlcnQtY2hhaW4t
aW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAur4k
JgUu7Iz3MVNhP/NPVzWBMzUU+/wg4cNcbiZlCnupZ+7O+1EFbKD0ym3GhBYFKX3z
oAfLVd6ul3SbeyvbWwG5Hv9Ahv+CYSsNOBqNfwz4Su+XNsaHukwEgr74YkFe6Uj8
W1paroz07Ra8lqx4iFzKAmsfMxNq2EmpMGPIwFXJde1E8R26J79jYWdYRMYjEKXw
J8hf+GDdTMn70d48EGjh+8q8wz3mf6VfNchXD60kywZjBWn5XwOowffULwR6Xaze
lc4JIsxhMalBNlE6auAI4U9N82tgLaP+YZ0fnsPWAxqVvSCyX0TZIVe/DQ/4cFxW
fM18VBYXHMZ1i7B8IQIDAQABo4GDMIGAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
AQH/BAQDAgGGMB8GA1UdIwQYMBaAFGwbNBqwR2b9p4jR2wQGUAHsFXH8MB0GA1Ud
DgQWBBRGpwm9vwksu7hT45jwfbspwLtbxTAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAMdGNDfSCsycTkxigszBdSOlpQ37
7+Pg2pSuCwSASZALmgIHOCquRrHv8Gb+NM0kfwPXUwKP+nzuVNF89P5kYtKfKpK5
DATe7o5OMQZt7PLqqC4YyTkVl/j55MySM4x7H1s3lZj4L7H8UOOoBAmtftOz2iR0
5s9260QdsUb9HOYBgHvmdbLS4FOiFfiVZByM/Iv3wNtGMDjleEj31EzBRbY1AgC6
VAfRF1Y2Lk+k/ONoEiy5MdFavmIn3SsEsRWDCtHZgS5Bk1uvPJ9s3E1IU7DwBsIE
W25P+PK7D0a+WB3s99CbfxbLm4rh01lbSFraDwv447qIZtILgXSDfqsgjPY=
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID4DCCAsigAwIBAgIUYfJ19DH+gJIjI3XVmDQBjeUu9UQwDQYJKoZIhvcNAQEL
BQAwZzELMAkGA1UEBhMCTEsxDjAMBgNVBAgMBWFiY2RlMQ4wDAYDVQQHDAVhYmNk
ZTEOMAwGA1UECgwFYWJjZGUxDjAMBgNVBAsMBWFiY2RlMRgwFgYDVQQDDA9jZXJ0
LWNoYWluLXJvb3QwHhcNMjQwMjI2MTEzMTE2WhcNMjgwMjI1MTEzMTE2WjBnMQsw
CQYDVQQGEwJMSzEOMAwGA1UECAwFYWJjZGUxDjAMBgNVBAcMBWFiY2RlMQ4wDAYD
VQQKDAVhYmNkZTEOMAwGA1UECwwFYWJjZGUxGDAWBgNVBAMMD2NlcnQtY2hhaW4t
cm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM9eeBl+roj7svOI
hvD4FtUjP2oQMtoRAwiEHGhJ1eKBEYE39qfWJ2A/9+UMmxeO/xblB3euyCj2rGzr
RVXFh8ou4zblf0SrcRl+HG+KG0BjPZS7ptStgVceqAS9sWaLUaGTo/TdEm4MIEcU
c40ef0XLQhIWLRLOgnDhX4ykR7hEU5/Y9cyiDXT4m4nuLQyug0gulEUZPjXlZfxD
R7vpyJINyeqtkbPCv6Nw/oZ03Cd/mYCqFEyBuO4qcpKI+sNTd08RoucsO1lt5mHe
xYo6Dxz8Csi6dibAiGlrqMCJg/xYYyShd0SBeEv3RGHNjoKT4TNndcS2D14R/vp5
mpPsUwUCAwEAAaOBgzCBgDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
hjAfBgNVHSMEGDAWgBRsGzQasEdm/aeI0dsEBlAB7BVx/DAdBgNVHQ4EFgQUbBs0
GrBHZv2niNHbBAZQAewVcfwwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MA0GCSqGSIb3DQEBCwUAA4IBAQCg/Toe27OMLZgD/BPjx7NPKX+01vWwSgQrRJEx
jSjljZJ1wKvv3uiQ6ijrP+gq+OgAXp6PV9QUTCT6rMKbKU0TI4TpPw9gPH8WNFIq
pQ0nrDPmoJkfqOJ5MwrJ0T/VqaUWzsCbCmfEVEf7SOezPRDhOc202jcTXdLfEiHt
sRJTdhAAFjLcpwQUhKLygh3S7t/TDZ/0E6qKlHNYADennx4aQh6WE8magx6x4o2F
0FGlf6gX7pSGpDtFsh/gBYGo54pwx0iabw6qcwK2U4L+a/U4ZYVgTlYnm2Aazjnq
zCtKuvSwJAyfp3kkYwtkjnfKcA0PktAwFWnM9+fseU2kRE1l
-----END CERTIFICATE-----
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@
<classes>
<class name="org.wso2.am.integration.tests.application.groupSharing.ApplicationSharingConfig"/>
<class name="org.wso2.am.integration.tests.application.groupSharing.ApplicationSharingTestCase"/>
<class name="org.wso2.am.integration.tests.api.lifecycle.APISecurityMutualSSLCertificateChainValidationTestCase" />
</classes>
</test>

Expand Down

0 comments on commit e131493

Please sign in to comment.