mirror of
https://repository.entgra.net/community/device-mgt-core.git
synced 2025-10-06 02:01:45 +00:00
Add client certificate authentication for Windows
This commit is contained in:
parent
35ab8ceb97
commit
31e9a6e457
@ -823,8 +823,14 @@ public class CertificateGenerator {
|
||||
}
|
||||
String subjectDn = joiner.toString();
|
||||
X500Name issuerName = new X500Name(subjectDn);
|
||||
|
||||
String commonName = certificationRequest.getSubject().getRDNs(BCStyle.CN)[0].getFirst()
|
||||
.getValue().toString();
|
||||
// CSR sent from a Windows device will have an '!' followed by the device ID in the CN
|
||||
if (commonName.contains("!")) {
|
||||
commonName = commonName.split("!")[1];
|
||||
}
|
||||
|
||||
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
|
||||
X500Name subjectName = new X500Name("O=" + commonName + " ,CN=" +
|
||||
serialNumber + ", OU=tenant_" + tenantId);
|
||||
|
||||
@ -42,5 +42,4 @@ public class CommonUtil {
|
||||
public static synchronized BigInteger generateSerialNumber() {
|
||||
return BigInteger.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -32,6 +32,9 @@ public final class Constants {
|
||||
public static final String HEADER_HTTP_ACCEPT = "Accept";
|
||||
public static final String HEADER_HTTP_AUTHORIZATION = "Authorization";
|
||||
public static final String ONE_TIME_TOKEN_HEADER = "one-time-token";
|
||||
public static final String MUTUAL_AUTH_HEADER = "mutual-auth-header";
|
||||
public static final String PROXY_MUTUAL_AUTH_HEADER = "proxy-mutual-auth-header";
|
||||
public static final String CERTIFICATE_VERIFICATION_HEADER = "Mdm-Signature";
|
||||
}
|
||||
|
||||
public static final class ContentTypes {
|
||||
@ -53,4 +56,12 @@ public final class Constants {
|
||||
public static final String DELETE = "delete";
|
||||
public static final String ACTION = "action";
|
||||
}
|
||||
|
||||
public static final class Certificate {
|
||||
private Certificate() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static final String ORGANIZATION_ATTRIBUTE = "O=";
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,12 @@
|
||||
|
||||
package io.entgra.device.mgt.core.webapp.authenticator.framework.Utils;
|
||||
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.SCEPException;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.SCEPManager;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.TenantedDeviceWrapper;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.DeviceIdentifier;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.DeviceManagementConstants;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.EnrolmentInfo;
|
||||
import io.entgra.device.mgt.core.device.mgt.core.util.DeviceManagerUtil;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.AuthenticationException;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.AuthenticationInfo;
|
||||
@ -26,6 +32,7 @@ import io.entgra.device.mgt.core.webapp.authenticator.framework.authenticator.oa
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.authenticator.oauth.OAuthValidationResponse;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.authenticator.oauth.OAuthValidatorFactory;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
@ -34,6 +41,7 @@ import org.wso2.carbon.user.api.UserStoreException;
|
||||
import org.wso2.carbon.user.core.service.RealmService;
|
||||
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -151,10 +159,60 @@ public class Utils {
|
||||
String sysPropertyName = matchPattern.group(1);
|
||||
String sysPropertyValue = System.getProperty(sysPropertyName);
|
||||
if (sysPropertyValue != null && !sysPropertyName.isEmpty()) {
|
||||
urlWithPlaceholders = urlWithPlaceholders.replaceAll("\\$\\{(" + sysPropertyName + ")\\}", sysPropertyValue);
|
||||
urlWithPlaceholders = urlWithPlaceholders.replaceAll("\\$\\{(" + sysPropertyName + ")\\}",
|
||||
sysPropertyValue);
|
||||
}
|
||||
}
|
||||
return urlWithPlaceholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the given attribute from the subject distinguished name. eg: "entgra.net"
|
||||
* from "CN=entgra.net"
|
||||
* @param requestCertificate {@link X509Certificate} that needs to extract an attribute from
|
||||
* @param attribute the attribute name that needs to be extracted from the cert. eg: "CN="
|
||||
* @return the value of the attribute
|
||||
*/
|
||||
public static String getSubjectDnAttribute(X509Certificate requestCertificate, String attribute) {
|
||||
String distinguishedName = requestCertificate.getSubjectDN().getName();
|
||||
if (StringUtils.isNotEmpty(distinguishedName)) {
|
||||
String[] dnSplits = distinguishedName.split(",");
|
||||
for (String dnSplit : dnSplits) {
|
||||
if (dnSplit.contains(attribute)) {
|
||||
String[] cnSplits = dnSplit.split("=");
|
||||
if (StringUtils.isNotEmpty(cnSplits[1])) {
|
||||
return cnSplits[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device identifier is valid and set the authentication info such as the tenant domain,
|
||||
* tenant id and username of the enrolled device.
|
||||
* @param deviceIdentifier {@link DeviceIdentifier} containing device id and type
|
||||
* @param authenticationInfo {@link AuthenticationInfo} containing tenant and user details
|
||||
* @throws SCEPException if the device or tenant does not exist
|
||||
*/
|
||||
public static void validateScepDevice(DeviceIdentifier deviceIdentifier, AuthenticationInfo authenticationInfo)
|
||||
throws SCEPException {
|
||||
SCEPManager scepManager = AuthenticatorFrameworkDataHolder.getInstance().getScepManager();
|
||||
TenantedDeviceWrapper tenantedDeviceWrapper = scepManager.getValidatedDevice(deviceIdentifier);
|
||||
authenticationInfo.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
|
||||
authenticationInfo.setTenantId(tenantedDeviceWrapper.getTenantId());
|
||||
|
||||
// To make sure the tenant flow is not initiated in the valve as the
|
||||
// tenant flows are initiated at the API level on iOS
|
||||
if (deviceIdentifier.getType().equals(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS)) {
|
||||
authenticationInfo.setTenantId(-1);
|
||||
}
|
||||
|
||||
if (tenantedDeviceWrapper.getDevice() != null &&
|
||||
tenantedDeviceWrapper.getDevice().getEnrolmentInfo() != null) {
|
||||
EnrolmentInfo enrolmentInfo = tenantedDeviceWrapper.getDevice().getEnrolmentInfo();
|
||||
authenticationInfo.setUsername(enrolmentInfo.getOwner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +94,16 @@ public class WebappAuthenticationValve extends CarbonTomcatValve {
|
||||
return;
|
||||
}
|
||||
AuthenticationInfo authenticationInfo = authenticator.authenticate(request, response);
|
||||
|
||||
// If the request header contains 'Mdm-Signature', then it is a iOS or Windows device trying to sync and
|
||||
// it is already authenticated from the CertificateAuthenticator. Hence, there is no need to check
|
||||
// if the user associated with the device has permission.
|
||||
if (request.getHeader(Constants.HTTPHeaders.CERTIFICATE_VERIFICATION_HEADER) != null &&
|
||||
authenticationInfo.getStatus() == WebappAuthenticator.Status.SUCCESS) {
|
||||
this.getNext().invoke(request, response, compositeValve);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isManagedAPI(request) && (authenticationInfo.getStatus() == WebappAuthenticator.Status.CONTINUE ||
|
||||
authenticationInfo.getStatus() == WebappAuthenticator.Status.SUCCESS)) {
|
||||
WebappAuthenticator.Status status = WebappTenantAuthorizer.authorize(request, authenticationInfo);
|
||||
|
||||
@ -21,13 +21,11 @@ package io.entgra.device.mgt.core.webapp.authenticator.framework.authenticator;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.dto.CertificateResponse;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.exception.KeystoreException;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.SCEPException;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.SCEPManager;
|
||||
import io.entgra.device.mgt.core.certificate.mgt.core.scep.TenantedDeviceWrapper;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.DeviceIdentifier;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.DeviceManagementConstants;
|
||||
import io.entgra.device.mgt.core.device.mgt.common.EnrolmentInfo;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.AuthenticationException;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.AuthenticationInfo;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.Constants;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.Utils.Utils;
|
||||
import io.entgra.device.mgt.core.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder;
|
||||
import org.apache.catalina.connector.Request;
|
||||
@ -45,9 +43,6 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(CertificateAuthenticator.class);
|
||||
private static final String CERTIFICATE_AUTHENTICATOR = "CertificateAuth";
|
||||
private static final String MUTUAL_AUTH_HEADER = "mutual-auth-header";
|
||||
private static final String PROXY_MUTUAL_AUTH_HEADER = "proxy-mutual-auth-header";
|
||||
private static final String CERTIFICATE_VERIFICATION_HEADER = "Mdm-Signature";
|
||||
private static final String CLIENT_CERTIFICATE_ATTRIBUTE = "javax.servlet.request.X509Certificate";
|
||||
|
||||
@Override
|
||||
@ -57,8 +52,9 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
|
||||
@Override
|
||||
public boolean canHandle(Request request) {
|
||||
return request.getHeader(CERTIFICATE_VERIFICATION_HEADER) != null
|
||||
|| request.getHeader(MUTUAL_AUTH_HEADER) != null || request.getHeader(PROXY_MUTUAL_AUTH_HEADER) != null;
|
||||
return request.getHeader(Constants.HTTPHeaders.CERTIFICATE_VERIFICATION_HEADER) != null
|
||||
|| request.getHeader(Constants.HTTPHeaders.MUTUAL_AUTH_HEADER) != null ||
|
||||
request.getHeader(Constants.HTTPHeaders.PROXY_MUTUAL_AUTH_HEADER) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,12 +69,14 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
try {
|
||||
// When there is a load balancer terminating mutual SSL, it should pass this header along and
|
||||
// as the value of this header, the client certificate subject dn should be passed.
|
||||
if (request.getHeader(PROXY_MUTUAL_AUTH_HEADER) != null) {
|
||||
if (request.getHeader(Constants.HTTPHeaders.PROXY_MUTUAL_AUTH_HEADER) != null) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("PROXY_MUTUAL_AUTH_HEADER " + request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
|
||||
log.debug("PROXY_MUTUAL_AUTH_HEADER " +
|
||||
request.getHeader(Constants.HTTPHeaders.PROXY_MUTUAL_AUTH_HEADER));
|
||||
}
|
||||
CertificateResponse certificateResponse = AuthenticatorFrameworkDataHolder.getInstance().
|
||||
getCertificateManagementService().verifySubjectDN(request.getHeader(PROXY_MUTUAL_AUTH_HEADER));
|
||||
getCertificateManagementService().verifySubjectDN(request.getHeader(
|
||||
Constants.HTTPHeaders.PROXY_MUTUAL_AUTH_HEADER));
|
||||
authenticationInfo = checkCertificateResponse(certificateResponse);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Certificate Serial : " + certificateResponse.getSerialNumber()
|
||||
@ -86,7 +84,7 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
+ " , username" + authenticationInfo.getUsername());
|
||||
}
|
||||
}
|
||||
else if (request.getHeader(MUTUAL_AUTH_HEADER) != null) {
|
||||
else if (request.getHeader(Constants.HTTPHeaders.MUTUAL_AUTH_HEADER) != null) {
|
||||
Object object = request.getAttribute(CLIENT_CERTIFICATE_ATTRIBUTE);
|
||||
X509Certificate[] clientCertificate = null;
|
||||
if (object instanceof X509Certificate[]) {
|
||||
@ -101,13 +99,11 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
authenticationInfo.setStatus(Status.FAILURE);
|
||||
authenticationInfo.setMessage("No client certificate is present");
|
||||
}
|
||||
} else if (request.getHeader(CERTIFICATE_VERIFICATION_HEADER) != null) {
|
||||
String certHeader = request.getHeader(CERTIFICATE_VERIFICATION_HEADER);
|
||||
} else if (request.getHeader(Constants.HTTPHeaders.CERTIFICATE_VERIFICATION_HEADER) != null) {
|
||||
String certHeader = request.getHeader(Constants.HTTPHeaders.CERTIFICATE_VERIFICATION_HEADER);
|
||||
if (certHeader != null &&
|
||||
AuthenticatorFrameworkDataHolder.getInstance().getCertificateManagementService().
|
||||
verifySignature(certHeader)) {
|
||||
AuthenticatorFrameworkDataHolder.getInstance().getCertificateManagementService().
|
||||
extractCertificateFromSignature(certHeader);
|
||||
X509Certificate certificate =
|
||||
AuthenticatorFrameworkDataHolder.getInstance().getCertificateManagementService().
|
||||
extractCertificateFromSignature(certHeader);
|
||||
@ -116,30 +112,37 @@ public class CertificateAuthenticator implements WebappAuthenticator {
|
||||
|
||||
if (challengeToken != null) {
|
||||
challengeToken = challengeToken.substring(challengeToken.indexOf("(") + 1).trim();
|
||||
SCEPManager scepManager = AuthenticatorFrameworkDataHolder.getInstance().getScepManager();
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
deviceIdentifier.setId(challengeToken);
|
||||
deviceIdentifier.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);
|
||||
TenantedDeviceWrapper tenantedDeviceWrapper = scepManager.getValidatedDevice(deviceIdentifier);
|
||||
authenticationInfo.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
|
||||
// To make sure the tenant flow is not initiated in the valve as the
|
||||
// tenant flows are initiated at the API level on iOS
|
||||
authenticationInfo.setTenantId(-1);
|
||||
|
||||
if (tenantedDeviceWrapper.getDevice() != null &&
|
||||
tenantedDeviceWrapper.getDevice().getEnrolmentInfo() != null) {
|
||||
|
||||
EnrolmentInfo enrolmentInfo = tenantedDeviceWrapper.getDevice().getEnrolmentInfo();
|
||||
authenticationInfo.setUsername(enrolmentInfo.getOwner());
|
||||
}
|
||||
Utils.validateScepDevice(deviceIdentifier, authenticationInfo);
|
||||
authenticationInfo.setStatus(Status.CONTINUE);
|
||||
} else {
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
String deviceId = Utils.getSubjectDnAttribute(certificate,
|
||||
Constants.Certificate.ORGANIZATION_ATTRIBUTE);
|
||||
if (deviceId == null) {
|
||||
authenticationInfo.setStatus(Status.FAILURE);
|
||||
return authenticationInfo;
|
||||
}
|
||||
deviceIdentifier.setId(deviceId);
|
||||
deviceIdentifier.setType(
|
||||
DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_WINDOWS);
|
||||
Utils.validateScepDevice(deviceIdentifier, authenticationInfo);
|
||||
authenticationInfo.setStatus(Status.SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (KeystoreException e) {
|
||||
log.error("KeystoreException occurred ", e);
|
||||
String msg = "Error occurred while validating device client certificate.";
|
||||
log.error(msg, e);
|
||||
authenticationInfo.setStatus(Status.FAILURE);
|
||||
authenticationInfo.setMessage(msg);
|
||||
} catch (SCEPException e) {
|
||||
log.error("SCEPException occurred ", e);
|
||||
String msg = "Error occurred while validating device identification.";
|
||||
log.error(msg, e);
|
||||
authenticationInfo.setStatus(Status.FAILURE);
|
||||
authenticationInfo.setMessage(msg);
|
||||
}
|
||||
return authenticationInfo;
|
||||
}
|
||||
|
||||
@ -139,9 +139,7 @@ public class PermissionAuthorizer {
|
||||
} else {
|
||||
return WebappAuthenticator.Status.FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user