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