This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 7.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 3bb2ed4e85093f9d24e6a25c1b71afcbd4ba57b0 Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Jan 18 21:57:40 2021 +0000 Update to new code signing service. --- build.properties.default | 16 + build.xml | 54 +-- java/org/apache/tomcat/buildutil/SignCode.java | 439 ------------------------- webapps/docs/changelog.xml | 3 + 4 files changed, 35 insertions(+), 477 deletions(-) diff --git a/build.properties.default b/build.properties.default index 8fabc40..9bb07bd 100644 --- a/build.properties.default +++ b/build.properties.default @@ -65,6 +65,11 @@ java.net.preferIPv4Stack=false # Location of GPG executable (used only for releases) gpg.exec=/path/to/gpg +# Code signing of Windows installer +do.codesigning=false +codesigning.exec=signtool.exe +codesigning.certificate.thumbprint=5a606116432aba614c246d15e792f9e4bcf19cbf + # ----- Settings to use when downloading files ----- trydownload.httpusecaches=true @@ -306,6 +311,7 @@ cobertura.home=${base.path}/cobertura-${cobertura.version} cobertura.jar=${cobertura.home}/cobertura-${cobertura.version}.jar cobertura.loc=${base-sf.loc}/cobertura/cobertura-${cobertura.version}-bin.tar.gz +<<<<<<< HEAD # ----- SAAJ API, used by Code Signing for releases ----- # ----- No longer part of JRE from Java 11 onwards ----- # ----- CDDL Licensed ----- @@ -316,3 +322,13 @@ saaj-api.checksum.value=caae8b4bf2c551155815331e9e96256f|1c399a7fea4d0262a6a3975 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 +======= +# ----- Findbugs ----- +findbugs.version=3.1.12 +findbugs.checksum.enabled=true +findbugs.checksum.algorithm=MD5|SHA-1 +findbugs.checksum.value=8c54502a8e1b78ea6b173a186ce6f379|95114d9aaeeba7bd4ea5a3d6a2167cd6c87bb943 +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 +>>>>>>> 4f25dd7cc0... Update to new code signing service. diff --git a/build.xml b/build.xml index 30a30ae..5b294c3 100644 --- a/build.xml +++ b/build.xml @@ -2391,20 +2391,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" @@ -2427,20 +2421,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" /> @@ -2925,16 +2913,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 e1e09f6..0000000 --- a/java/org/apache/tomcat/buildutil/SignCode.java +++ /dev/null @@ -1,439 +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<FileSet>(); - 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<File>(); - - // 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 e) { - throw new BuildException(e); - } catch (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<String>(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); - ZipOutputStream zos = null; - try { - zos = new ZipOutputStream(baos); - byte[] buf = new byte[32 * 1024]; - for (int i = 0; i < files.size(); i++) { - FileInputStream fis = null; - try { - 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); - } - } finally { - if (fis != null) { - fis.close(); - } - } - } - } finally { - if (zos != null) { - zos.close(); - } - } - - 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)); - ZipInputStream zis = null; - try { - zis = new ZipInputStream(bais); - byte[] buf = new byte[32 * 1024]; - for (File file : files) { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(file); - zis.getNextEntry(); - int numRead; - while ((numRead = zis.read(buf)) >= 0) { - fos.write(buf, 0, numRead); - } - } finally { - if (fos != null) { - fos.close(); - } - } - } - } finally { - if (zis != null) { - zis.close(); - } - } - } -} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index db0cc27..eec09cf 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -202,6 +202,9 @@ Update the packaged version of the Tomcat Native Library to 1.2.26. (markt) </update> + <update> + Migrate to new code signing service. (markt) + </update> </changelog> </subsection> </section> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org