Java tutorial
/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.mdm.mobileservices.windowspc.services.wstep.impl; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.wso2.carbon.mdm.mobileservices.windowspc.common.Constants; import org.wso2.carbon.mdm.mobileservices.windowspc.common.exceptions.CertificateGenerationException; import org.wso2.carbon.mdm.mobileservices.windowspc.common.exceptions.KeyStoreGenerationException; import org.wso2.carbon.mdm.mobileservices.windowspc.common.exceptions.WindowsDeviceEnrolmentException; import org.wso2.carbon.mdm.mobileservices.windowspc.common.exceptions.XMLFileOperationException; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.CertificateEnrollmentService; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.beans.AdditionalContext; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.beans.BinarySecurityToken; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.beans.RequestSecurityTokenResponse; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.beans.RequestedSecurityToken; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.util.CertificateSigningService; import org.wso2.carbon.mdm.mobileservices.windowspc.services.wstep.util.KeyStoreGenerator; import sun.misc.BASE64Encoder; import javax.annotation.Resource; import javax.jws.WebService; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.ws.BindingType; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.soap.Addressing; import javax.xml.ws.soap.SOAPBinding; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.security.*; import java.security.cert.Certificate; import java.security.cert.*; import java.util.ArrayList; import java.util.List; /** * Implementation class of CertificateEnrollmentService interface. This class implements MS-WSTEP * protocol. */ @WebService(endpointInterface = Constants.CertificateEnrollment.CERTIFICATE_ENROLLMENT_SERVICE_ENDPOINT, targetNamespace = Constants.CertificateEnrollment.DEVICE_ENROLLMENT_SERVICE_TARGET_NAMESPACE) @Addressing(enabled = true, required = true) @BindingType(value = SOAPBinding.SOAP12HTTP_BINDING) public class CertificateEnrollmentServiceImpl implements CertificateEnrollmentService { public static final int FIRST_ITEM = 0; private static final int REQUEST_ID = FIRST_ITEM; private static final int CA_CERTIFICATE_POSITION = FIRST_ITEM; private static final int SIGNED_CERTIFICATE_POSITION = 1; private static Log logger = LogFactory.getLog(CertificateEnrollmentServiceImpl.class); private PrivateKey privateKey; private X509Certificate rootCACertificate; @Resource private WebServiceContext context; /** * This method implements MS-WSTEP for Certificate Enrollment Service. * * @param tokenType - Device Enrolment Token type is received via device * @param requestType - WS-Trust request type * @param binarySecurityToken - CSR from device * @param additionalContext - Device type and OS version is received * @param response - Response will include wap-provisioning xml */ @Override public void requestSecurityToken(String tokenType, String requestType, String binarySecurityToken, AdditionalContext additionalContext, Holder<RequestSecurityTokenResponse> response) throws WindowsDeviceEnrolmentException { ServletContext ctx = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT); File wapProvisioningFile = (File) ctx.getAttribute(Constants.CONTEXT_WAP_PROVISIONING_FILE); String storePassword = (String) ctx.getAttribute(Constants.CONTEXT_MDM_PASSWORD); String keyPassword = (String) ctx.getAttribute(Constants.CONTEXT_MDM_PRIVATE_KEY_PASSWORD); List certPropertyList = new ArrayList(); String commonName = (String) ctx.getAttribute(Constants.CONTEXT_COMMON_NAME); certPropertyList.add(commonName); int notBeforeDate = (Integer) ctx.getAttribute(Constants.CONTEXT_NOT_BEFORE_DATE); certPropertyList.add(notBeforeDate); int notAfterDate = (Integer) ctx.getAttribute(Constants.CONTEXT_NOT_AFTER_DATE); certPropertyList.add(notAfterDate); try { setRootCertAndKey(storePassword, keyPassword); } //Generic exception is caught here as there is no need of taking different actions for // different exceptions. catch (Exception e) { throw new WindowsDeviceEnrolmentException( "Root certificate and private key couldn't be extracted from keystore.", e); } if (logger.isDebugEnabled()) { logger.debug("Received CSR from Device:" + binarySecurityToken); } String wapProvisioningFilePath = wapProvisioningFile.getPath(); RequestSecurityTokenResponse requestSecurityTokenResponse = new RequestSecurityTokenResponse(); requestSecurityTokenResponse.setTokenType(Constants.CertificateEnrollment.TOKEN_TYPE); String encodedWap; try { encodedWap = prepareWapProvisioningXML(binarySecurityToken, certPropertyList, wapProvisioningFilePath); } //Generic exception is caught here as there is no need of taking different actions for // different exceptions. catch (Exception e) { throw new WindowsDeviceEnrolmentException("Wap provisioning file couldn't be " + "prepared.", e); } RequestedSecurityToken requestedSecurityToken = new RequestedSecurityToken(); BinarySecurityToken binarySecToken = new BinarySecurityToken(); binarySecToken.setValueType(Constants.CertificateEnrollment.VALUE_TYPE); binarySecToken.setEncodingType(Constants.CertificateEnrollment.ENCODING_TYPE); binarySecToken.setToken(encodedWap); requestedSecurityToken.setBinarySecurityToken(binarySecToken); requestSecurityTokenResponse.setRequestedSecurityToken(requestedSecurityToken); requestSecurityTokenResponse.setRequestID(REQUEST_ID); response.value = requestSecurityTokenResponse; } /** * @param document - Wap provisioning XML document * @return - String representation of wap provisioning XML document * @throws Exception */ private String convertDocumentToString(Document document) throws TransformerException { DOMSource DOMSource = new DOMSource(document); StringWriter stringWriter = new StringWriter(); StreamResult streamResult = new StreamResult(stringWriter); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(DOMSource, streamResult); String wapProvisioningString = stringWriter.toString(); return wapProvisioningString; } /** * Method for setting privateKey and rootCACertificate variables. * * @throws KeyStoreGenerationException * @throws org.wso2.carbon.mdm.mobileservices.windows.common.exceptions * .XMLFileOperationException * @throws CertificateGenerationException */ public void setRootCertAndKey(String storePassword, String keyPassword) throws KeyStoreGenerationException, XMLFileOperationException, CertificateGenerationException { File JKSFile = new File(getClass().getClassLoader() .getResource(Constants.CertificateEnrollment.WSO2_MDM_JKS_FILE).getFile()); String JKSFilePath = JKSFile.getPath(); KeyStore securityJKS; try { securityJKS = KeyStoreGenerator.getKeyStore(); } catch (KeyStoreGenerationException e) { throw new KeyStoreGenerationException("Cannot retrieve the MDM key store.", e); } try { KeyStoreGenerator.loadToStore(securityJKS, storePassword.toCharArray(), JKSFilePath); } catch (KeyStoreGenerationException e) { throw new KeyStoreGenerationException("Cannot load the MDM key store.", e); } PrivateKey CAPrivateKey; try { CAPrivateKey = (PrivateKey) securityJKS.getKey(Constants.CertificateEnrollment.CA_CERT, keyPassword.toCharArray()); } catch (KeyStoreException e) { throw new CertificateGenerationException("Cannot generate private key due to Key store error.", e); } catch (NoSuchAlgorithmException e) { throw new CertificateGenerationException( "Requested cryptographic algorithm is not available in the environment.", e); } catch (UnrecoverableKeyException e) { throw new CertificateGenerationException("Cannot recover private key.", e); } privateKey = CAPrivateKey; Certificate CACertificate; try { CACertificate = securityJKS.getCertificate(Constants.CertificateEnrollment.CA_CERT); } catch (KeyStoreException e) { throw new KeyStoreGenerationException("Keystore cannot be accessed.", e); } CertificateFactory certificateFactory; try { certificateFactory = CertificateFactory.getInstance(Constants.CertificateEnrollment.X_509); } catch (CertificateException e) { throw new CertificateGenerationException("Cannot initiate certificate factory.", e); } ByteArrayInputStream byteArrayInputStream; try { byteArrayInputStream = new ByteArrayInputStream(CACertificate.getEncoded()); } catch (CertificateEncodingException e) { throw new CertificateGenerationException("CA certificate cannot be encoded.", e); } X509Certificate X509CACertificate; try { X509CACertificate = (X509Certificate) certificateFactory.generateCertificate(byteArrayInputStream); } catch (CertificateException e) { throw new CertificateGenerationException("X509 CA certificate cannot be generated.", e); } rootCACertificate = X509CACertificate; } /** * This method prepares the wap-provisioning file by including relevant certificates etc * * @param binarySecurityToken - CSR from device * @param certPropertyList - property list for signed certificate * @param wapProvisioningFilePath - File path of wap-provisioning file * @return - base64 encoded final wap-provisioning file * @throws CertificateGenerationException * @throws XMLFileOperationException */ public String prepareWapProvisioningXML(String binarySecurityToken, List certPropertyList, String wapProvisioningFilePath) throws CertificateGenerationException, XMLFileOperationException { byte[] DERByteArray = javax.xml.bind.DatatypeConverter.parseBase64Binary(binarySecurityToken); PKCS10CertificationRequest certificationRequest; try { certificationRequest = new PKCS10CertificationRequest(DERByteArray); } catch (IOException e) { throw new CertificateGenerationException("CSR cannot be recovered.", e); } JcaPKCS10CertificationRequest CSRRequest = new JcaPKCS10CertificationRequest(certificationRequest); X509Certificate signedCertificate = CertificateSigningService.signCSR(CSRRequest, privateKey, rootCACertificate, certPropertyList); BASE64Encoder base64Encoder = new BASE64Encoder(); String rootCertEncodedString; try { rootCertEncodedString = base64Encoder.encode(rootCACertificate.getEncoded()); } catch (CertificateEncodingException e) { throw new CertificateGenerationException("CA certificate cannot be encoded.", e); } String signedCertEncodedString; try { signedCertEncodedString = base64Encoder.encode(signedCertificate.getEncoded()); } catch (CertificateEncodingException e) { throw new CertificateGenerationException("Singed certificate cannot be encoded.", e); } DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; String wapProvisioningString; try { builder = domFactory.newDocumentBuilder(); Document document = builder.parse(wapProvisioningFilePath); NodeList wapParm = document.getElementsByTagName(Constants.CertificateEnrollment.PARM); Node CACertificatePosition = wapParm.item(CA_CERTIFICATE_POSITION); //Adding SHA1 CA certificate finger print to wap-provisioning xml. CACertificatePosition.getParentNode().getAttributes().getNamedItem(Constants.CertificateEnrollment.TYPE) .setTextContent( String.valueOf(DigestUtils.sha1Hex(rootCACertificate.getEncoded())).toUpperCase()); //Adding encoded CA certificate to wap-provisioning file after removing new line // characters. NamedNodeMap rootCertAttributes = CACertificatePosition.getAttributes(); Node rootCertNode = rootCertAttributes.getNamedItem(Constants.CertificateEnrollment.VALUE); rootCertEncodedString = rootCertEncodedString.replaceAll("\n", ""); rootCertNode.setTextContent(rootCertEncodedString); if (logger.isDebugEnabled()) { logger.debug("Root certificate:" + rootCertEncodedString); } Node signedCertificatePosition = wapParm.item(SIGNED_CERTIFICATE_POSITION); //Adding SHA1 signed certificate finger print to wap-provisioning xml. signedCertificatePosition.getParentNode().getAttributes() .getNamedItem(Constants.CertificateEnrollment.TYPE).setTextContent( String.valueOf(DigestUtils.sha1Hex(signedCertificate.getEncoded())).toUpperCase()); //Adding encoded signed certificate to wap-provisioning file after removing new line // characters. NamedNodeMap clientCertAttributes = signedCertificatePosition.getAttributes(); Node clientEncodedNode = clientCertAttributes.getNamedItem(Constants.CertificateEnrollment.VALUE); signedCertEncodedString = signedCertEncodedString.replaceAll("\n", ""); clientEncodedNode.setTextContent(signedCertEncodedString); if (logger.isDebugEnabled()) { logger.debug("Signed certificate:" + signedCertEncodedString); } wapProvisioningString = convertDocumentToString(document); //Generic exception is caught here as there is no need of taking different actions for // different exceptions. } catch (Exception e) { throw new XMLFileOperationException("Problem occurred with wap-provisioning.xml file.", e); } String encodedWap = base64Encoder.encode(wapProvisioningString.getBytes()); return encodedWap; } }