Skip to content

Commit

Permalink
Merge pull request #12455 from RusJaI/master
Browse files Browse the repository at this point in the history
Support separate client side MTLS for production and sandbox endpoints
  • Loading branch information
RusJaI authored Jul 22, 2024
2 parents e33a5ba + 96dd874 commit 3fa67dc
Show file tree
Hide file tree
Showing 39 changed files with 1,641 additions and 438 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ String addBlockCondition(String conditionType, String conditionValue, boolean co
* @param apiTypeWrapper : API Type Wrapper.
* @param certificate : Relevant public certificate.
* @param alias : Alias of the certificate.
* @param keyType : Key type for the certificate (PRODUCTION or SANDBOX).
* @param organization : Organization
* @return SUCCESS : If operation succeeded,
* INTERNAL_SERVER_ERROR : If any internal error occurred,
Expand All @@ -813,7 +814,7 @@ String addBlockCondition(String conditionType, String conditionValue, boolean co
* @throws APIManagementException API Management Exception.
*/
int addClientCertificate(String userName, ApiTypeWrapper apiTypeWrapper, String certificate, String alias,
String tierName, String organization) throws APIManagementException;
String tierName, String keyType, String organization) throws APIManagementException;

/**
* Method to remove the certificate which mapped to the given alias, endpoint from publisher and gateway nodes.
Expand All @@ -835,7 +836,7 @@ int addClientCertificate(String userName, ApiTypeWrapper apiTypeWrapper, String
* 4 : If certificate is not found in the trust store.
* @throws APIManagementException API Management Exception.
*/
int deleteClientCertificate(String userName, ApiTypeWrapper apiTypeWrapper, String alias)
int deleteClientCertificate(String userName, ApiTypeWrapper apiTypeWrapper, String alias, String keyType)
throws APIManagementException;

/**
Expand Down Expand Up @@ -882,8 +883,8 @@ List<CertificateMetadataDTO> searchCertificates(int tenantId, String alias, Stri
* @return list of client certificates that match search criteria.
* @throws APIManagementException API Management Exception.
*/
List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias, APIIdentifier apiIdentifier,
String organization) throws APIManagementException;
List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias, String keyType,
APIIdentifier apiIdentifier, String organization) throws APIManagementException;

/**
* Method to search the client certificates for the provided tenant id, alias and api product identifier.
Expand All @@ -895,7 +896,7 @@ List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias,
* @return list of client certificates that match search criteria.
* @throws APIManagementException API Management Exception.
*/
List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias,
List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias, String keyType,
APIProductIdentifier apiProductIdentifier, String organization) throws APIManagementException;

/**
Expand All @@ -913,7 +914,7 @@ List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias,
* @return count of client certificates that exists for a particular tenant.
* @throws APIManagementException API Management Exception.
*/
int getClientCertificateCount(int tenantId) throws APIManagementException;
int getClientCertificateCount(int tenantId, String keyType) throws APIManagementException;

/**
* Method to check whether an certificate for the given alias is present in the trust store and the database.
Expand All @@ -929,13 +930,14 @@ List<ClientCertificateDTO> searchClientCertificates(int tenantId, String alias,
* be modified by current user.
*
* @param alias : Relevant alias.
* @param keyType : Key type of the certificate
* @param apiTypeWrapper : The identifier of the api.
* @param organization : Organization
* @return Instance of {@link ClientCertificateDTO} if the client certificate is present and
* modifiable by current user.
* @throws APIManagementException API Management Exception.
*/
ClientCertificateDTO getClientCertificate(String alias, ApiTypeWrapper apiTypeWrapper,
ClientCertificateDTO getClientCertificate(String alias, String keyType, ApiTypeWrapper apiTypeWrapper,
String organization) throws APIManagementException;


Expand Down Expand Up @@ -967,6 +969,7 @@ ClientCertificateDTO getClientCertificate(String alias, ApiTypeWrapper apiTypeWr
* @param alias : Alias of the certificate.
* @param apiTypeWrapper : API Identifier of the certificate.
* @param tier : tier name.
* @param keyType : Key type for the certificate (PRODUCTION or SANDBOX).
* @param tenantId : Id of tenant.
* @param organization : organization
* @return : 1 : If client certificate update is successful,
Expand All @@ -976,7 +979,7 @@ ClientCertificateDTO getClientCertificate(String alias, ApiTypeWrapper apiTypeWr
* @throws APIManagementException API Management Exception.
*/
int updateClientCertificate(String certificate, String alias, ApiTypeWrapper apiTypeWrapper, String tier,
int tenantId, String organization) throws APIManagementException;
String keyType, int tenantId, String organization) throws APIManagementException;

/**
* Retrieve the certificate which matches the given alias.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ public enum ExceptionCodes implements ErrorHandler {
"{apiName}#{apiVersion}#{tenantDomain}"),
INVALID_API_NAME(900854, "Invalid API Name",400 ,"Invalid API Name"),
ALIAS_CANNOT_BE_EMPTY(900855, "The alias cannot be empty", 400, "The alias cannot be empty"),

KEY_TYPE_CANNOT_BE_EMPTY(900856, "The key type cannot be empty", 400, "The key type cannot be empty"),
// API import/export related codes
ERROR_READING_META_DATA(900907, "Error while reading meta information from the definition", 400,
"Error while reading meta information from the definition"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class MutualSSLAuthenticator implements Authenticator {
private boolean isMandatory;

// <UniqueIdentifierName,Tier> -Format
private HashMap<String, String> certificates;
private Map<String, Map<String, String>> certificates;

/**
* Initialized the mutual SSL authenticator.
Expand All @@ -74,11 +74,15 @@ public MutualSSLAuthenticator(String apiLevelPolicy, boolean isMandatory, String
if (StringUtils.isNotEmpty(certificateDetails)) {
String[] certificateParts = certificateDetails.substring(1, certificateDetails.length() - 1).split(",");
for (String certificatePart : certificateParts) {
HashMap<String, String> certificateData = new HashMap<>();
int tierDivisionIndex = certificatePart.lastIndexOf("=");
if (tierDivisionIndex > 0) {
String uniqueIdentifier = certificatePart.substring(0, tierDivisionIndex).trim();
String tier = certificatePart.substring(tierDivisionIndex + 1);
certificates.put(uniqueIdentifier, tier);
String tierAndKeyTypeString = certificatePart.substring(tierDivisionIndex + 1);
String[] tierAndKeyType = tierAndKeyTypeString.split(APIConstants.DELEM_COLON);
certificateData.put(APIConstants.CLIENT_CERTIFICATE_TIER, tierAndKeyType[0]);
certificateData.put(APIConstants.CLIENT_CERTIFICATE_KEY_TYPE, tierAndKeyType[1]);
certificates.put(uniqueIdentifier, certificateData);
}
}
}
Expand Down Expand Up @@ -177,11 +181,18 @@ private void setAuthContext(MessageContext messageContext, Certificate certifica
String subjectDN = x509Certificate.getSubjectDN().getName();
String uniqueIdentifier = (x509Certificate.getSerialNumber() + "_" + x509Certificate.getSubjectDN()).replaceAll(",",
"#").replaceAll("\"", "'").trim();
String tier = certificates.get(uniqueIdentifier);
/* Since there can be previously deleted certificates persisted in the trust store that matches with the
certificate object but not in the certificates list for the particular API.
*/
if (certificates.get(uniqueIdentifier) == null ) {
handleCertificateNotAssociatedToAPIFailure(messageContext);
}
String tier = certificates.get(uniqueIdentifier).get(APIConstants.CLIENT_CERTIFICATE_TIER);
String keyType = certificates.get(uniqueIdentifier).get(APIConstants.CLIENT_CERTIFICATE_KEY_TYPE);
if (StringUtils.isEmpty(tier)) {
handleCertificateNotAssociatedToAPIFailure(messageContext);
}
setAuthenticationContext(messageContext, subjectDN, uniqueIdentifier, tier);
setAuthenticationContext(messageContext, subjectDN, uniqueIdentifier, tier, keyType);
}

/**
Expand All @@ -195,6 +206,7 @@ private void setAuthContext(MessageContext messageContext, Certificate[] certifi
throws APISecurityException, APIManagementException {

String tier = null;
String keyType = null;
List<X509Certificate> x509Certificates = Utils.convertCertificatesToX509Certificates(certificatesArray);
String subjectDN = x509Certificates.get(0).getSubjectDN().getName();
String issuerDNIdentifier = x509Certificates.get(x509Certificates.size() - 1).getIssuerDN().getName()
Expand All @@ -205,11 +217,12 @@ private void setAuthContext(MessageContext messageContext, Certificate[] certifi
String subjectDNIdentifier = (x509Certificate.getSerialNumber() + "_"
+ x509Certificate.getSubjectDN()).replaceAll(",", "#").replaceAll("\"", "'").trim();
subjectDNIdentifiers.add(subjectDNIdentifier);
for (Map.Entry<String, String> entry : certificates.entrySet()) {
for (Map.Entry<String, Map<String, String>> entry : certificates.entrySet()) {
String key = entry.getKey();
if (StringUtils.equals(subjectDNIdentifier, key)) {
uniqueIdentifier = key;
tier = entry.getValue();
tier = entry.getValue().get(APIConstants.CLIENT_CERTIFICATE_TIER);
keyType = entry.getValue().get(APIConstants.CLIENT_CERTIFICATE_KEY_TYPE);
break;
}
}
Expand All @@ -218,36 +231,38 @@ private void setAuthContext(MessageContext messageContext, Certificate[] certifi
}
}
if (StringUtils.isEmpty(tier)) {
for (Map.Entry<String, String> entry : certificates.entrySet()) {
for (Map.Entry<String, Map<String, String>> entry : certificates.entrySet()) {
String key = entry.getKey();
if (key.contains(issuerDNIdentifier)) {
uniqueIdentifier = key;
tier = entry.getValue();
tier = entry.getValue().get(APIConstants.CLIENT_CERTIFICATE_TIER);
keyType = entry.getValue().get(APIConstants.CLIENT_CERTIFICATE_KEY_TYPE);
}
}
}
if (StringUtils.isEmpty(tier)) {
tier = getTierFromCompleteCertificateChain(x509Certificates, subjectDNIdentifiers);
if (StringUtils.isEmpty(tier) || StringUtils.isEmpty(keyType)) {
subjectDNIdentifiers = getUniqueIdentifierFromCompleteCertificateChain(x509Certificates, subjectDNIdentifiers);
tier = getTierFromCompleteCertificateChain(subjectDNIdentifiers);
keyType = getKeyTypeFromCompleteCertificateChain(subjectDNIdentifiers);
}
if (StringUtils.isEmpty(tier)) {

if (StringUtils.isEmpty(tier) || StringUtils.isEmpty(keyType)) {
handleCertificateNotAssociatedToAPIFailure(messageContext);
}
setAuthenticationContext(messageContext, subjectDN, uniqueIdentifier, tier);
setAuthenticationContext(messageContext, subjectDN, uniqueIdentifier, tier, keyType);
}

/**
* Fetches tier assigned to the client certificate after making the complete certificate chain using certificates
* in truststore.
* Fetches the list of uniqueIdentifiers for complete certificate chain using certificates in truststore.
*
* @param x509Certificates Client certificate chain
* @param x509Certificates client certificates chain
* @param uniqueIdentifiers Unique identifiers list for client certificate chain
* @return Tier
* @return uniqueIdentifiers
* @throws APIManagementException
*/
private String getTierFromCompleteCertificateChain(List<X509Certificate> x509Certificates,
List<String> uniqueIdentifiers) throws APIManagementException {
private List<String> getUniqueIdentifierFromCompleteCertificateChain(List<X509Certificate> x509Certificates,
List<String> uniqueIdentifiers) throws APIManagementException {

String tier = null;
X509Certificate certificate = x509Certificates.get(x509Certificates.size() - 1);
String subjectDN = certificate.getSubjectDN().getName();
String issuerDN = certificate.getIssuerDN().getName();
Expand All @@ -266,16 +281,49 @@ private String getTierFromCompleteCertificateChain(List<X509Certificate> x509Cer
isIssuerCertificateUpdated = !StringUtils.equals(subjectDN, issuerDN);
}
}
return uniqueIdentifiers;
}

/**
* Fetches tier assigned to the client certificate after making the complete certificate chain using certificates
* in truststore.
*
* @param uniqueIdentifiers Unique identifiers list for client certificate chain
* @return Tier
*/
private String getTierFromCompleteCertificateChain(List<String> uniqueIdentifiers) {

String tier = null;
for (String uniqueIdentifier : uniqueIdentifiers) {
tier = certificates.get(uniqueIdentifier);
tier = certificates.get(uniqueIdentifier) == null ? null :
certificates.get(uniqueIdentifier).get(APIConstants.CLIENT_CERTIFICATE_TIER);
if (StringUtils.isNotEmpty(tier)) {
break;
}
}
return tier;
}

/**
* Fetches keyType assigned to the client certificate after making the complete certificate chain using certificates
* in truststore.
*
* @param uniqueIdentifiers Unique identifiers list for client certificate chain
* @return keyType
*/
private String getKeyTypeFromCompleteCertificateChain(List<String> uniqueIdentifiers) {

String keyType = null;
for (String uniqueIdentifier : uniqueIdentifiers) {
keyType = certificates.get(uniqueIdentifier) == null ? null :
certificates.get(uniqueIdentifier).get(APIConstants.CLIENT_CERTIFICATE_KEY_TYPE);
if (StringUtils.isNotEmpty(keyType)) {
break;
}
}
return keyType;
}

/**
* Handles failures where message context does not contain client certificates.
* @param messageContext Relevant message context
Expand Down Expand Up @@ -321,7 +369,7 @@ private void handleCertificateNotAssociatedToAPIFailure(MessageContext messageCo
* @param tier Throttling policy tier
*/
private void setAuthenticationContext(MessageContext messageContext, String subjectDN, String uniqueIdentifier,
String tier) {
String tier, String keyType) {

AuthenticationContext authContext = new AuthenticationContext();
authContext.setAuthenticated(true);
Expand All @@ -340,7 +388,7 @@ private void setAuthenticationContext(MessageContext messageContext, String subj
}
authContext.setApiTier(apiLevelPolicy);
APIIdentifier apiIdentifier = getAPIIdentifier(messageContext);
authContext.setKeyType(APIConstants.API_KEY_TYPE_PRODUCTION);
authContext.setKeyType(keyType);
authContext.setStopOnQuotaReach(true);
authContext.setApiKey(uniqueIdentifier + "_" + apiIdentifier.toString());
authContext.setTier(tier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,9 @@ private Permissions() {
public static final String API_KEY_VALIDATOR_URL = API_KEY_VALIDATOR + "ServerURL";
public static final String API_KEY_VALIDATOR_USERNAME = API_KEY_VALIDATOR + "Username";
public static final String API_KEY_VALIDATOR_PASSWORD = API_KEY_VALIDATOR + "Password";
public static final String CLIENT_CERTIFICATE_TIER = "TIER";
public static final String CLIENT_CERTIFICATE_KEY_TYPE = "KEY_TYPE";

public static final String ENABLE_DEFAULT_KEY_MANAGER_REGISTRATION = API_KEY_VALIDATOR +
"EnableDefaultKeyManagerRegistration";
public static final String ENABLE_KEY_MANAGER_RETRIVAL = API_KEY_VALIDATOR +
Expand Down
Loading

0 comments on commit 3fa67dc

Please sign in to comment.