/** * Copyright 2014 Microsoft Open Technologies Inc. * * Licensed 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 com.microsoftopentechnologies.intellij.helpers; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.util.io.FileUtil; import com.microsoftopentechnologies.intellij.forms.OpenSSLFinderForm; import com.microsoftopentechnologies.intellij.helpers.azure.AzureCmdException; import com.microsoftopentechnologies.intellij.helpers.azure.AzureRestAPIHelper; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPathConstants; import java.io.*; public class OpenSSLHelper { public static final String PASSWORD = "Java6NeedsPwd"; public static String processCertificate(String xmlPublishSettings) throws AzureCmdException { try { Node publishProfileNode = ((NodeList) XmlHelper.getXMLValue(xmlPublishSettings, "/PublishData/PublishProfile", XPathConstants.NODESET)).item(0); String version = XmlHelper.getAttributeValue(publishProfileNode, "SchemaVersion"); boolean isFirstVersion = (version == null || Float.parseFloat(version) < 2); NodeList subscriptionList = (NodeList) XmlHelper.getXMLValue(xmlPublishSettings, "//Subscription", XPathConstants.NODESET); Document ownerDocument = null; for (int i = 0; i != subscriptionList.getLength(); i++) { //Gets the pfx info Element node = (Element) subscriptionList.item(i); ownerDocument = node.getOwnerDocument(); String pfx = XmlHelper.getAttributeValue(isFirstVersion ? publishProfileNode : node, "ManagementCertificate"); byte[] decodedBuffer = new BASE64Decoder().decodeBuffer(pfx); //Create pfxFile File tmpPath = new File(System.getProperty("java.io.tmpdir") + File.separator + "tempAzureCert"); tmpPath.mkdirs(); tmpPath.setWritable(true); File pfxFile = new File(tmpPath.getPath() + File.separator + "temp.pfx"); pfxFile.createNewFile(); pfxFile.setWritable(true); FileOutputStream pfxOutputStream = new FileOutputStream(pfxFile); pfxOutputStream.write(decodedBuffer); pfxOutputStream.flush(); pfxOutputStream.close(); String path = getOpenSSLPath(); if (path == null || path.isEmpty()) { throw new Exception("Please configure a valid OpenSSL executable location."); } else if (!path.endsWith(File.separator)) { path = path + File.separator; } //Export to pem with OpenSSL runCommand(new String[]{path + "openssl", "pkcs12", "-in", "temp.pfx", "-out", "temp.pem", "-nodes", "-password", "pass:"}, tmpPath); //Export to pfx again and change password runCommand(new String[]{path + "openssl", "pkcs12", "-export", "-out", "temppwd.pfx", "-in", "temp.pem", "-password", "pass:" + PASSWORD}, tmpPath); //Read file and replace pfx with password protected pfx File pwdPfxFile = new File(tmpPath.getPath() + File.separator + "temppwd.pfx"); byte[] buf = new byte[(int) pwdPfxFile.length()]; FileInputStream pfxInputStream = new FileInputStream(pwdPfxFile); pfxInputStream.read(buf, 0, (int) pwdPfxFile.length()); pfxInputStream.close(); FileUtil.delete(tmpPath); node.setAttribute("ManagementCertificate", new BASE64Encoder().encode(buf).replace("\r", "").replace("\n", "")); if (isFirstVersion) { node.setAttribute("ServiceManagementUrl", XmlHelper.getAttributeValue(publishProfileNode, "Url")); } } if (ownerDocument == null) { return null; } Transformer tf = TransformerFactory.newInstance().newTransformer(); Writer out = new StringWriter(); tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); tf.transform(new DOMSource(ownerDocument), new StreamResult(out)); return out.toString(); } catch (Exception ex) { throw new AzureCmdException("Error processing publish settings file.", ex.getMessage()); } } private static String getOpenSSLPath() { PropertiesComponent pc = PropertiesComponent.getInstance(); String opendSSLlPath = pc.getValue("MSOpenSSLPath", ""); if (!validOpenSSLPath(opendSSLlPath)) { opendSSLlPath = getOpenSSLPathFromEnvironment(); if (!validOpenSSLPath(opendSSLlPath)) { opendSSLlPath = promptForOpenSSLPath(pc); if (!validOpenSSLPath(opendSSLlPath)) { opendSSLlPath = null; } } } return opendSSLlPath; } private static String getOpenSSLPathFromEnvironment() { String osslPath = null; try { String mOsVersion = System.getProperty("os.name"); String osName = mOsVersion.split(" ")[0]; String cmd = osName.equals("Windows") ? "where openssl" : "which openssl"; Process p = Runtime.getRuntime().exec(cmd); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); osslPath = new File(reader.readLine()).getParent(); } catch (Throwable ignored) { } return osslPath; } private static String promptForOpenSSLPath(PropertiesComponent pc) { OpenSSLFinderForm openSSLFinderForm = new OpenSSLFinderForm(); openSSLFinderForm.setModal(true); UIHelper.packAndCenterJDialog(openSSLFinderForm); openSSLFinderForm.setVisible(true); return pc.getValue("MSOpenSSLPath", ""); } private static boolean validOpenSSLPath(String osslPath) { boolean result = false; if (osslPath != null && !osslPath.isEmpty()) { try { result = new File(osslPath).exists(); } catch (Throwable ignored) { } } return result; } private static void runCommand(String[] cmd, File path) throws AzureCmdException, IOException, InterruptedException { final Process p; Runtime runtime = Runtime.getRuntime(); p = runtime.exec( cmd, null, //new String[] {"PRECOMPILE_STREAMLINE_FILES=1"}, path); String errResponse = new String(FileUtil.adaptiveLoadBytes(p.getErrorStream())); if (p.waitFor() != 0) { AzureCmdException ex = new AzureCmdException("Error executing OpenSSL command \n", errResponse); ex.printStackTrace(); throw ex; } } }