From 85a28854c83f230dcee1bd55395a4fcff9284744 Mon Sep 17 00:00:00 2001 From: weijun Date: Fri, 16 Apr 2010 10:13:23 +0800 Subject: [PATCH] 6939248: Jarsigner can't extract Extended Key Usage from Timestamp Reply correctly Reviewed-by: xuelei, mullan --- .../sun/security/tools/TimestampedSigner.java | 45 ++- .../tools/jarsigner/TimestampCheck.java | 294 ++++++++++++++++++ test/sun/security/tools/jarsigner/ts.sh | 91 ++++++ 3 files changed, 406 insertions(+), 24 deletions(-) create mode 100644 test/sun/security/tools/jarsigner/TimestampCheck.java create mode 100644 test/sun/security/tools/jarsigner/ts.sh diff --git a/src/share/classes/sun/security/tools/TimestampedSigner.java b/src/share/classes/sun/security/tools/TimestampedSigner.java index 549ed8ea2..a77f2878d 100644 --- a/src/share/classes/sun/security/tools/TimestampedSigner.java +++ b/src/share/classes/sun/security/tools/TimestampedSigner.java @@ -81,6 +81,11 @@ public final class TimestampedSigner extends ContentSigner { */ private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8"; + /* + * Object identifier for extendedKeyUsage extension + */ + private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; + /* * Object identifier for the timestamping access descriptors. */ @@ -357,34 +362,26 @@ public final class TimestampedSigner extends ContentSigner { } // Examine the TSA's certificate (if present) - List keyPurposes = null; - X509Certificate[] certs = tsToken.getCertificates(); - if (certs != null && certs.length > 0) { - // Use certficate from the TSP reply - // Pick out the cert for the TS server, which is the end-entity - // one inside the chain. - for (X509Certificate cert: certs) { - boolean isSigner = false; - for (X509Certificate cert2: certs) { - if (cert != cert2) { - if (cert.getSubjectDN().equals(cert2.getIssuerDN())) { - isSigner = true; - break; - } - } + for (SignerInfo si: tsToken.getSignerInfos()) { + X509Certificate cert = si.getCertificate(tsToken); + if (cert == null) { + // Error, we've already set tsRequestCertificate = true + throw new CertificateException( + "Certificate not included in timestamp token"); + } else { + if (!cert.getCriticalExtensionOIDs().contains( + EXTENDED_KEY_USAGE_OID)) { + throw new CertificateException( + "Certificate is not valid for timestamping"); } - if (!isSigner) { - keyPurposes = cert.getExtendedKeyUsage(); - if (keyPurposes == null || - ! keyPurposes.contains(KP_TIMESTAMPING_OID)) { - throw new CertificateException( - "Certificate is not valid for timestamping"); - } - break; + List keyPurposes = cert.getExtendedKeyUsage(); + if (keyPurposes == null || + ! keyPurposes.contains(KP_TIMESTAMPING_OID)) { + throw new CertificateException( + "Certificate is not valid for timestamping"); } } } - return tsReply.getEncodedToken(); } } diff --git a/test/sun/security/tools/jarsigner/TimestampCheck.java b/test/sun/security/tools/jarsigner/TimestampCheck.java new file mode 100644 index 000000000..18dec0dc7 --- /dev/null +++ b/test/sun/security/tools/jarsigner/TimestampCheck.java @@ -0,0 +1,294 @@ +/* + * Copyright 2003-2010 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import com.sun.net.httpserver.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import sun.security.pkcs.ContentInfo; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X500Name; + +public class TimestampCheck { + static final String TSKS = "tsks"; + static final String JAR = "old.jar"; + + static class Handler implements HttpHandler { + public void handle(HttpExchange t) throws IOException { + int len = 0; + for (String h: t.getRequestHeaders().keySet()) { + if (h.equalsIgnoreCase("Content-length")) { + len = Integer.valueOf(t.getRequestHeaders().get(h).get(0)); + } + } + byte[] input = new byte[len]; + t.getRequestBody().read(input); + + try { + int path = 0; + if (t.getRequestURI().getPath().length() > 1) { + path = Integer.parseInt( + t.getRequestURI().getPath().substring(1)); + } + byte[] output = sign(input, path); + Headers out = t.getResponseHeaders(); + out.set("Content-Type", "application/timestamp-reply"); + + t.sendResponseHeaders(200, output.length); + OutputStream os = t.getResponseBody(); + os.write(output); + } catch (Exception e) { + e.printStackTrace(); + t.sendResponseHeaders(500, 0); + } + t.close(); + } + + /** + * @param input The data to sign + * @param path different cases to simulate, impl on URL path + * 0: normal + * 1: Missing nonce + * 2: Different nonce + * 3: Bad digets octets in messageImprint + * 4: Different algorithmId in messageImprint + * 5: whole chain in cert set + * 6: extension is missing + * 7: extension is non-critical + * 8: extension does not have timestamping + * @returns the signed + */ + byte[] sign(byte[] input, int path) throws Exception { + // Read TSRequest + DerValue value = new DerValue(input); + System.err.println("\nIncoming Request\n==================="); + System.err.println("Version: " + value.data.getInteger()); + DerValue messageImprint = value.data.getDerValue(); + AlgorithmId aid = AlgorithmId.parse( + messageImprint.data.getDerValue()); + System.err.println("AlgorithmId: " + aid); + + BigInteger nonce = null; + while (value.data.available() > 0) { + DerValue v = value.data.getDerValue(); + if (v.tag == DerValue.tag_Integer) { + nonce = v.getBigInteger(); + System.err.println("nonce: " + nonce); + } else if (v.tag == DerValue.tag_Boolean) { + System.err.println("certReq: " + v.getBoolean()); + } + } + + // Write TSResponse + System.err.println("\nResponse\n==================="); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new FileInputStream(TSKS), "changeit".toCharArray()); + + String alias = "ts"; + if (path == 6) alias = "tsbad1"; + if (path == 7) alias = "tsbad2"; + if (path == 8) alias = "tsbad3"; + + DerOutputStream statusInfo = new DerOutputStream(); + statusInfo.putInteger(0); + + DerOutputStream token = new DerOutputStream(); + AlgorithmId[] algorithms = {aid}; + Certificate[] chain = ks.getCertificateChain(alias); + X509Certificate[] signerCertificateChain = null; + X509Certificate signer = (X509Certificate)chain[0]; + if (path == 5) { // Only case 5 uses full chain + signerCertificateChain = new X509Certificate[chain.length]; + for (int i=0; i A +rm old.jar +$JAR cvf old.jar A + +# ca is CA +# old is signer for code +# ts is signer for timestamp +# tsbad1 has no extendedKeyUsage +# tsbad2's extendedKeyUsage is non-critical +# tsbad3's extendedKeyUsage has no timestamping + +$KT -alias ca -genkeypair -ext bc -dname CN=CA +$KT -alias old -genkeypair -dname CN=old +$KT -alias ts -genkeypair -dname CN=ts +$KT -alias tsbad1 -genkeypair -dname CN=tsbad1 +$KT -alias tsbad2 -genkeypair -dname CN=tsbad2 +$KT -alias tsbad3 -genkeypair -dname CN=tsbad3 +$KT -alias ts -certreq | \ + $KT -alias ca -gencert -ext eku:critical=ts | \ + $KT -alias ts -importcert +$KT -alias tsbad1 -certreq | \ + $KT -alias ca -gencert | \ + $KT -alias tsbad1 -importcert +$KT -alias tsbad2 -certreq | \ + $KT -alias ca -gencert -ext eku=ts | \ + $KT -alias tsbad2 -importcert +$KT -alias tsbad3 -certreq | \ + $KT -alias ca -gencert -ext eku:critical=cs | \ + $KT -alias tsbad3 -importcert + +$JAVAC -d . ${TESTSRC}/TimestampCheck.java +$JAVA TimestampCheck + -- GitLab