You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/01/18 22:41:59 UTC

[tomcat] 01/02: Update to new code signing service.

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 4f25dd7cc0c1fec559f7b07d9ebdc115a0482319
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Jan 18 21:57:40 2021 +0000

    Update to new code signing service.
---
 build.properties.default                       |  19 +-
 build.xml                                      |  54 +---
 java/org/apache/tomcat/buildutil/SignCode.java | 413 -------------------------
 webapps/docs/changelog.xml                     |   3 +
 4 files changed, 21 insertions(+), 468 deletions(-)

diff --git a/build.properties.default b/build.properties.default
index cf0c748..9451b57 100644
--- a/build.properties.default
+++ b/build.properties.default
@@ -71,12 +71,8 @@ gpg.exec=/path/to/gpg
 
 # Code signing of Windows installer
 do.codesigning=false
-codesigning.user=request-via-pmc
-codesigning.pwd=request-via-pmc
-codesigning.partnercode=request-via-pmc
-codesigning.keyStore=request-via-pmc
-codesigning.keyStorePassword=request-via-pmc
-codesigning.service=Microsoft Windows Signing
+codesigning.exec=signtool.exe
+codesigning.certificate.thumbprint=5a606116432aba614c246d15e792f9e4bcf19cbf
 
 # ----- Settings to use when downloading files -----
 trydownload.httpusecaches=true
@@ -280,14 +276,3 @@ findbugs.checksum.value=8c54502a8e1b78ea6b173a186ce6f379|95114d9aaeeba7bd4ea5a3d
 findbugs.home=${base.path}/spotbugs-${findbugs.version}
 findbugs.jar=${findbugs.home}/lib/spotbugs-ant.jar
 findbugs.loc=${base-maven.loc}/com/github/spotbugs/spotbugs/${findbugs.version}/spotbugs-${findbugs.version}.tgz
-
-# ----- SAAJ API, used by Code Signing for releases -----
-# ----- No longer part of JRE from Java 11 onwards  -----
-# ----- CDDL Licensed                               -----
-saaj-api.version=1.3.5
-saaj-api.checksum.enabled=true
-saaj-api.checksum.algorithm=MD5|SHA-1
-saaj-api.checksum.value=caae8b4bf2c551155815331e9e96256f|1c399a7fea4d0262a6a39750e419c24f0c769586
-saaj-api.home=${base.path}/saaj-api-${saaj-api.version}
-saaj-api.jar=${saaj-api.home}/saaj-api-${saaj-api.version}.jar
-saaj-api.loc=${base-maven.loc}/javax/xml/soap/saaj-api/${saaj-api.version}/saaj-api-${saaj-api.version}.jar
diff --git a/build.xml b/build.xml
index a2051f5..e699833 100644
--- a/build.xml
+++ b/build.xml
@@ -2247,20 +2247,14 @@ skip.installer property in build.properties" />
   <target name="-installer-sign-uninstaller"
       unless="skip.installer" depends="-installer-create-uninstaller"
       if="${do.codesigning}">
-    <taskdef name="signcode"
-        classname="org.apache.tomcat.buildutil.SignCode"
-        classpath="${tomcat.classes}" />
-    <signcode userName="${codesigning.user}" password="${codesigning.pwd}"
-        partnerCode="${codesigning.partnercode}"
-        keyStore="${codesigning.keyStore}"
-        keyStorePassword="${codesigning.keyStorePassword}"
-        applicationName="Apache Tomcat ${version.major.minor} Uninstaller"
-        applicationversion="${version}"
-        signingService="${codesigning.service}">
-      <fileset dir="${tomcat.dist}">
-        <filename name="Uninstall.exe"/>
-      </fileset>
-    </signcode>
+    <exec executable="${codesigning.exec}" failonerror="true">
+      <arg value="sign"/>
+      <arg value="/sha1"/>
+      <arg value="${codesigning.certificate.thumbprint}"/>
+      <arg value="/tr"/>
+      <arg value="http://timestamp.digicert.com"/>
+      <arg value="${tomcat.dist}/Uninstall.exe"/>
+    </exec>
   </target>
 
   <target name="-installer" unless="skip.installer"
@@ -2283,20 +2277,14 @@ skip.installer property in build.properties" />
   <target name="installer-sign"
       description="Builds and optionally signs the Windows installer"
       depends="-installer" if="${do.codesigning}" >
-    <taskdef name="signcode"
-        classname="org.apache.tomcat.buildutil.SignCode"
-        classpath="${tomcat.classes}" />
-    <signcode userName="${codesigning.user}" password="${codesigning.pwd}"
-              partnerCode="${codesigning.partnercode}"
-              keyStore="${codesigning.keyStore}"
-              keyStorePassword="${codesigning.keyStorePassword}"
-              applicationName="Apache Tomcat ${version.major.minor}"
-              applicationversion="${version}"
-              signingService="${codesigning.service}">
-      <fileset dir="${tomcat.release}">
-        <filename name="v${version}/bin/${final.name}.exe"/>
-      </fileset>
-    </signcode>
+    <exec executable="${codesigning.exec}" failonerror="true">
+      <arg value="sign"/>
+      <arg value="/sha1"/>
+      <arg value="${codesigning.certificate.thumbprint}"/>
+      <arg value="/tr"/>
+      <arg value="http://timestamp.digicert.com"/>
+      <arg value="${tomcat.release}/v${version}/bin/${final.name}.exe"/>
+    </exec>
     <!-- .exe has changed so need to redo checksums and OpenPGP signature -->
     <delete file="${tomcat.release}/v${version}/bin/${final.name}.exe.asc" />
     <delete file="${tomcat.release}/v${version}/bin/${final.name}.exe.sha512" />
@@ -2789,16 +2777,6 @@ skip.installer property in build.properties" />
       <param name="checksum.value" value="${jdt.checksum.value}"/>
     </antcall>
 
-    <!-- Download SAAJ API -->
-    <antcall target="downloadfile">
-      <param name="sourcefile" value="${saaj-api.loc}"/>
-      <param name="destfile" value="${saaj-api.jar}"/>
-      <param name="destdir" value="${saaj-api.home}"/>
-      <param name="checksum.enabled" value="${saaj-api.checksum.enabled}"/>
-      <param name="checksum.algorithm" value="${saaj-api.checksum.algorithm}"/>
-      <param name="checksum.value" value="${saaj-api.checksum.value}"/>
-    </antcall>
-
   </target>
 
   <target name="download-test-compile"
diff --git a/java/org/apache/tomcat/buildutil/SignCode.java b/java/org/apache/tomcat/buildutil/SignCode.java
deleted file mode 100644
index 58e235f..0000000
--- a/java/org/apache/tomcat/buildutil/SignCode.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF 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.apache.tomcat.buildutil;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-import javax.xml.soap.MessageFactory;
-import javax.xml.soap.SOAPBody;
-import javax.xml.soap.SOAPConnection;
-import javax.xml.soap.SOAPConnectionFactory;
-import javax.xml.soap.SOAPConstants;
-import javax.xml.soap.SOAPElement;
-import javax.xml.soap.SOAPEnvelope;
-import javax.xml.soap.SOAPException;
-import javax.xml.soap.SOAPMessage;
-import javax.xml.soap.SOAPPart;
-
-import org.apache.tomcat.util.buf.StringUtils;
-import org.apache.tomcat.util.codec.binary.Base64;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.DirectoryScanner;
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.types.FileSet;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * Ant task that submits a file to the Digicert (formally Symantec) code-signing
- * service. The service is defined by the published
- * <a href="https://api.ws.digicert.com/webtrust/SigningService?wsdl">WSDL</a>.
- * Note that while the service has migrated to a Digicert domain, the namespace
- * continues to use a Symantec domain.
- */
-public class SignCode extends Task {
-
-    private static final URL SIGNING_SERVICE_URL;
-
-    private static final String NS = "cod";
-
-    private static final MessageFactory SOAP_MSG_FACTORY;
-
-    static {
-        try {
-            SIGNING_SERVICE_URL = new URL(
-                    "https://api-appsec.pki.digicert.com/webtrust/SigningService");
-        } catch (MalformedURLException e) {
-            throw new IllegalArgumentException(e);
-        }
-        try {
-            SOAP_MSG_FACTORY = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
-        } catch (SOAPException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private final List<FileSet> filesets = new ArrayList<>();
-    private String userName;
-    private String password;
-    private String partnerCode;
-    private String keyStore;
-    private String keyStorePassword;
-    private String applicationName;
-    private String applicationVersion;
-    private String signingService;
-    private boolean debug;
-
-    public void addFileset(FileSet fileset) {
-        filesets.add(fileset);
-    }
-
-
-    public void setUserName(String userName) {
-        this.userName = userName;
-    }
-
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-
-    public void setPartnerCode(String partnerCode) {
-        this.partnerCode = partnerCode;
-    }
-
-
-    public void setKeyStore(String keyStore) {
-        this.keyStore = keyStore;
-    }
-
-
-    public void setKeyStorePassword(String keyStorePassword) {
-        this.keyStorePassword = keyStorePassword;
-    }
-
-
-    public void setApplicationName(String applicationName) {
-        this.applicationName = applicationName;
-    }
-
-
-    public void setApplicationVersion(String applicationVersion) {
-        this.applicationVersion = applicationVersion;
-    }
-
-
-    public void setSigningService(String signingService) {
-        this.signingService = signingService;
-    }
-
-
-    public void setDebug(String debug) {
-        this.debug = Boolean.parseBoolean(debug);
-    }
-
-
-    @Override
-    public void execute() throws BuildException {
-
-        List<File> filesToSign = new ArrayList<>();
-
-        // Process the filesets and populate the list of files that need to be
-        // signed.
-        for (FileSet fileset : filesets) {
-            DirectoryScanner ds = fileset.getDirectoryScanner(getProject());
-            File basedir = ds.getBasedir();
-            String[] files = ds.getIncludedFiles();
-            if (files.length > 0) {
-                for (String s : files) {
-                    File file = new File(basedir, s);
-                    filesToSign.add(file);
-                }
-            }
-        }
-
-        // Set up the TLS client
-        System.setProperty("javax.net.ssl.keyStore", keyStore);
-        System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
-
-        try {
-            String signingSetID = makeSigningRequest(filesToSign);
-            downloadSignedFiles(filesToSign, signingSetID);
-        } catch (SOAPException | IOException e) {
-            throw new BuildException(e);
-        }
-    }
-
-
-    private String makeSigningRequest(List<File> filesToSign) throws SOAPException, IOException {
-        log("Constructing the code signing request");
-
-        SOAPMessage message = SOAP_MSG_FACTORY.createMessage();
-        SOAPBody body = populateEnvelope(message, NS);
-
-        SOAPElement requestSigning = body.addChildElement("requestSigning", NS);
-        SOAPElement requestSigningRequest =
-                requestSigning.addChildElement("requestSigningRequest", NS);
-
-        addCredentials(requestSigningRequest, this.userName, this.password, this.partnerCode);
-
-        SOAPElement applicationName =
-                requestSigningRequest.addChildElement("applicationName", NS);
-        applicationName.addTextNode(this.applicationName);
-
-        SOAPElement applicationVersion =
-                requestSigningRequest.addChildElement("applicationVersion", NS);
-        applicationVersion.addTextNode(this.applicationVersion);
-
-        SOAPElement signingServiceName =
-                requestSigningRequest.addChildElement("signingServiceName", NS);
-        signingServiceName.addTextNode(this.signingService);
-
-        List<String> fileNames = getFileNames(filesToSign);
-
-        SOAPElement commaDelimitedFileNames =
-                requestSigningRequest.addChildElement("commaDelimitedFileNames", NS);
-        commaDelimitedFileNames.addTextNode(StringUtils.join(fileNames));
-
-        SOAPElement application =
-                requestSigningRequest.addChildElement("application", NS);
-        application.addTextNode(getApplicationString(fileNames, filesToSign));
-
-        // Send the message
-        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
-        SOAPConnection connection = soapConnectionFactory.createConnection();
-
-        log("Sending signing request to server and waiting for response");
-        SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL);
-
-        if (debug) {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(2 * 1024);
-            response.writeTo(baos);
-            log(baos.toString("UTF-8"));
-        }
-
-        log("Processing response");
-        SOAPElement responseBody = response.getSOAPBody();
-
-        // Should come back signed
-        NodeList bodyNodes = responseBody.getChildNodes();
-        NodeList requestSigningResponseNodes = bodyNodes.item(0).getChildNodes();
-        NodeList returnNodes = requestSigningResponseNodes.item(0).getChildNodes();
-
-        String signingSetID = null;
-        String signingSetStatus = null;
-
-        for (int i = 0; i < returnNodes.getLength(); i++) {
-            Node returnNode = returnNodes.item(i);
-            if (returnNode.getLocalName().equals("signingSetID")) {
-                signingSetID = returnNode.getTextContent();
-            } else if (returnNode.getLocalName().equals("signingSetStatus")) {
-                signingSetStatus = returnNode.getTextContent();
-            }
-        }
-
-        if (!signingService.contains("TEST") && !"SIGNED".equals(signingSetStatus) ||
-                signingService.contains("TEST") && !"INITIALIZED".equals(signingSetStatus) ) {
-            throw new BuildException("Signing failed. Status was: " + signingSetStatus);
-        }
-
-        return signingSetID;
-    }
-
-
-    private void downloadSignedFiles(List<File> filesToSign, String id)
-            throws SOAPException, IOException {
-
-        log("Downloading signed files. The signing set ID is: " + id);
-
-        SOAPMessage message = SOAP_MSG_FACTORY.createMessage();
-        SOAPBody body = populateEnvelope(message, NS);
-
-        SOAPElement getSigningSetDetails = body.addChildElement("getSigningSetDetails", NS);
-        SOAPElement getSigningSetDetailsRequest =
-                getSigningSetDetails.addChildElement("getSigningSetDetailsRequest", NS);
-
-        addCredentials(getSigningSetDetailsRequest, this.userName, this.password, this.partnerCode);
-
-        SOAPElement signingSetID =
-                getSigningSetDetailsRequest.addChildElement("signingSetID", NS);
-        signingSetID.addTextNode(id);
-
-        SOAPElement returnApplication =
-                getSigningSetDetailsRequest.addChildElement("returnApplication", NS);
-        returnApplication.addTextNode("true");
-
-        // Send the message
-        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
-        SOAPConnection connection = soapConnectionFactory.createConnection();
-
-        log("Requesting signed files from server and waiting for response");
-        SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL);
-
-        log("Processing response");
-        SOAPElement responseBody = response.getSOAPBody();
-
-        // Check for success
-
-        // Extract the signed file(s) from the ZIP
-        NodeList bodyNodes = responseBody.getChildNodes();
-        NodeList getSigningSetDetailsResponseNodes = bodyNodes.item(0).getChildNodes();
-        NodeList returnNodes = getSigningSetDetailsResponseNodes.item(0).getChildNodes();
-
-        String result = null;
-        String data = null;
-
-        for (int i = 0; i < returnNodes.getLength(); i++) {
-            Node returnNode = returnNodes.item(i);
-            if (returnNode.getLocalName().equals("result")) {
-                result = returnNode.getChildNodes().item(0).getTextContent();
-            } else if (returnNode.getLocalName().equals("signingSet")) {
-                data = returnNode.getChildNodes().item(1).getTextContent();
-            }
-        }
-
-        if (!"0".equals(result)) {
-            throw new BuildException("Download failed. Result code was: " + result);
-        }
-
-        extractFilesFromApplicationString(data, filesToSign);
-    }
-
-
-    private static SOAPBody populateEnvelope(SOAPMessage message, String namespace)
-            throws SOAPException {
-        SOAPPart soapPart = message.getSOAPPart();
-        SOAPEnvelope envelope = soapPart.getEnvelope();
-        envelope.addNamespaceDeclaration(
-                "soapenv","http://schemas.xmlsoap.org/soap/envelope/");
-        envelope.addNamespaceDeclaration(
-                namespace,"http://api.ws.symantec.com/webtrust/codesigningservice");
-        return envelope.getBody();
-    }
-
-
-    private static void addCredentials(SOAPElement requestSigningRequest,
-            String user, String pwd, String code) throws SOAPException {
-        SOAPElement authToken = requestSigningRequest.addChildElement("authToken", NS);
-        SOAPElement userName = authToken.addChildElement("userName", NS);
-        userName.addTextNode(user);
-        SOAPElement password = authToken.addChildElement("password", NS);
-        password.addTextNode(pwd);
-        SOAPElement partnerCode = authToken.addChildElement("partnerCode", NS);
-        partnerCode.addTextNode(code);
-    }
-
-
-    /**
-     * Signing service requires unique files names. Since files will be returned
-     * in order, use dummy names that we know are unique but retain the file
-     * extension since the signing service appears to use it to figure out what
-     * to sign and how to sign it.
-     */
-    private static List<String> getFileNames(List<File> filesToSign) {
-        List<String> result = new ArrayList<>(filesToSign.size());
-
-        for (int i = 0; i < filesToSign.size(); i++) {
-            File f = filesToSign.get(i);
-            String fileName = f.getName();
-            int extIndex = fileName.lastIndexOf('.');
-            String newName;
-            if (extIndex < 0) {
-                newName = Integer.toString(i);
-            } else {
-                newName = Integer.toString(i) + fileName.substring(extIndex);
-            }
-            result.add(newName);
-        }
-        return result;
-    }
-
-
-    /**
-     * Zips the files, base 64 encodes the resulting zip and then returns the
-     * string. It would be far more efficient to stream this directly to the
-     * signing server but the files that need to be signed are relatively small
-     * and this simpler to write.
-     *
-     * @param fileNames Modified names of files
-     * @param files     Files to be signed
-     */
-    private static String getApplicationString(List<String> fileNames, List<File> files)
-            throws IOException {
-        // 16 MB should be more than enough for Tomcat
-        // TODO: Refactoring this entire class so it uses streaming rather than
-        //       buffering the entire set of files in memory would make it more
-        //       widely useful.
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(16 * 1024 * 1024);
-        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
-            byte[] buf = new byte[32 * 1024];
-            for (int i = 0; i < files.size(); i++) {
-                try (FileInputStream fis = new FileInputStream(files.get(i))) {
-                    ZipEntry zipEntry = new ZipEntry(fileNames.get(i));
-                    zos.putNextEntry(zipEntry);
-                    int numRead;
-                    while ( (numRead = fis.read(buf)) >= 0) {
-                        zos.write(buf, 0, numRead);
-                    }
-                }
-            }
-        }
-
-        return Base64.encodeBase64String(baos.toByteArray());
-    }
-
-
-    /**
-     * Removes base64 encoding, unzips the files and writes the new files over
-     * the top of the old ones.
-     */
-    private static void extractFilesFromApplicationString(String data, List<File> files)
-            throws IOException {
-        ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(data));
-        try (ZipInputStream zis = new ZipInputStream(bais)) {
-            byte[] buf = new byte[32 * 1024];
-            for (File file : files) {
-                try (FileOutputStream fos = new FileOutputStream(file)) {
-                    zis.getNextEntry();
-                    int numRead;
-                    while ((numRead = zis.read(buf)) >= 0) {
-                        fos.write(buf, 0, numRead);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 185c386..dc203ec 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -219,6 +219,9 @@
         Update the internal fork of Apache Commons DBCP to 2.9.0-SNAPSHOT
         (2021-01-15). (markt)
       </add>
+      <update>
+        Migrate to new code signing service. (markt)
+      </update>
     </changelog>
   </subsection>
 </section>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org