7102686: Restructure timestamp code so that jars and modules can more easily share the same code
Reviewed-by: mchung
--- a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java Wed Oct 19 10:15:23 2011 -0400
@@ -27,6 +27,7 @@
import java.io.*;
import java.math.BigInteger;
+import java.net.URI;
import java.util.*;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
@@ -35,6 +36,7 @@
import java.security.cert.CertificateFactory;
import java.security.*;
+import sun.security.timestamp.*;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateIssuerName;
@@ -68,6 +70,30 @@
private Principal[] certIssuerNames;
+ /*
+ * Random number generator for creating nonce values
+ */
+ private static final SecureRandom RANDOM;
+ static {
+ SecureRandom tmp = null;
+ try {
+ tmp = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // should not happen
+ }
+ RANDOM = tmp;
+ }
+
+ /*
+ * Object identifier for the timestamping key purpose.
+ */
+ 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";
+
/**
* Unmarshals a PKCS7 block from its encoded form, parsing the
* encoded bytes from the InputStream.
@@ -733,4 +759,164 @@
public boolean isOldStyle() {
return this.oldStyle;
}
+
+ /**
+ * Assembles a PKCS #7 signed data message that optionally includes a
+ * signature timestamp.
+ *
+ * @param signature the signature bytes
+ * @param signerChain the signer's X.509 certificate chain
+ * @param content the content that is signed; specify null to not include
+ * it in the PKCS7 data
+ * @param signatureAlgorithm the name of the signature algorithm
+ * @param tsaURI the URI of the Timestamping Authority; or null if no
+ * timestamp is requested
+ * @return the bytes of the encoded PKCS #7 signed data message
+ * @throws NoSuchAlgorithmException The exception is thrown if the signature
+ * algorithm is unrecognised.
+ * @throws CertificateException The exception is thrown if an error occurs
+ * while processing the signer's certificate or the TSA's
+ * certificate.
+ * @throws IOException The exception is thrown if an error occurs while
+ * generating the signature timestamp or while generating the signed
+ * data message.
+ */
+ public static byte[] generateSignedData(byte[] signature,
+ X509Certificate[] signerChain,
+ byte[] content,
+ String signatureAlgorithm,
+ URI tsaURI)
+ throws CertificateException, IOException, NoSuchAlgorithmException
+ {
+
+ // Generate the timestamp token
+ PKCS9Attributes unauthAttrs = null;
+ if (tsaURI != null) {
+ // Timestamp the signature
+ HttpTimestamper tsa = new HttpTimestamper(tsaURI);
+ byte[] tsToken = generateTimestampToken(tsa, signature);
+
+ // Insert the timestamp token into the PKCS #7 signer info element
+ // (as an unsigned attribute)
+ unauthAttrs =
+ new PKCS9Attributes(new PKCS9Attribute[]{
+ new PKCS9Attribute(
+ PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
+ tsToken)});
+ }
+
+ // Create the SignerInfo
+ X500Name issuerName =
+ X500Name.asX500Name(signerChain[0].getIssuerX500Principal());
+ BigInteger serialNumber = signerChain[0].getSerialNumber();
+ String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
+ String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
+ SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
+ AlgorithmId.get(digAlg), null,
+ AlgorithmId.get(encAlg),
+ signature, unauthAttrs);
+
+ // Create the PKCS #7 signed data message
+ SignerInfo[] signerInfos = {signerInfo};
+ AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
+ // Include or exclude content
+ ContentInfo contentInfo = (content == null)
+ ? new ContentInfo(ContentInfo.DATA_OID, null)
+ : new ContentInfo(content);
+ PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,
+ signerChain, signerInfos);
+ ByteArrayOutputStream p7out = new ByteArrayOutputStream();
+ pkcs7.encodeSignedData(p7out);
+
+ return p7out.toByteArray();
+ }
+
+ /**
+ * Requests, processes and validates a timestamp token from a TSA using
+ * common defaults. Uses the following defaults in the timestamp request:
+ * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
+ * set to true.
+ *
+ * @param tsa the timestamping authority to use
+ * @param toBeTimestamped the token that is to be timestamped
+ * @return the encoded timestamp token
+ * @throws IOException The exception is thrown if an error occurs while
+ * communicating with the TSA.
+ * @throws CertificateException The exception is thrown if the TSA's
+ * certificate is not permitted for timestamping.
+ */
+ private static byte[] generateTimestampToken(Timestamper tsa,
+ byte[] toBeTimestamped)
+ throws IOException, CertificateException
+ {
+ // Generate a timestamp
+ MessageDigest messageDigest = null;
+ TSRequest tsQuery = null;
+ try {
+ // SHA-1 is always used.
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ tsQuery = new TSRequest(toBeTimestamped, messageDigest);
+ } catch (NoSuchAlgorithmException e) {
+ // ignore
+ }
+
+ // Generate a nonce
+ BigInteger nonce = null;
+ if (RANDOM != null) {
+ nonce = new BigInteger(64, RANDOM);
+ tsQuery.setNonce(nonce);
+ }
+ tsQuery.requestCertificate(true);
+
+ TSResponse tsReply = tsa.generateTimestamp(tsQuery);
+ int status = tsReply.getStatusCode();
+ // Handle TSP error
+ if (status != 0 && status != 1) {
+ throw new IOException("Error generating timestamp: " +
+ tsReply.getStatusCodeAsText() + " " +
+ tsReply.getFailureCodeAsText());
+ }
+ PKCS7 tsToken = tsReply.getToken();
+
+ TimestampToken tst = tsReply.getTimestampToken();
+ if (!tst.getHashAlgorithm().getName().equals("SHA")) {
+ throw new IOException("Digest algorithm not SHA-1 in "
+ + "timestamp token");
+ }
+ if (!MessageDigest.isEqual(tst.getHashedMessage(),
+ tsQuery.getHashedMessage())) {
+ throw new IOException("Digest octets changed in timestamp token");
+ }
+
+ BigInteger replyNonce = tst.getNonce();
+ if (replyNonce == null && nonce != null) {
+ throw new IOException("Nonce missing in timestamp token");
+ }
+ if (replyNonce != null && !replyNonce.equals(nonce)) {
+ throw new IOException("Nonce changed in timestamp token");
+ }
+
+ // Examine the TSA's certificate (if present)
+ 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");
+ }
+ List<String> keyPurposes = cert.getExtendedKeyUsage();
+ if (keyPurposes == null ||
+ !keyPurposes.contains(KP_TIMESTAMPING_OID)) {
+ throw new CertificateException(
+ "Certificate is not valid for timestamping");
+ }
+ }
+ }
+ return tsReply.getEncodedToken();
+ }
}
--- a/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java Wed Oct 19 10:15:23 2011 -0400
@@ -28,10 +28,14 @@
import java.io.OutputStream;
import java.io.IOException;
import java.math.BigInteger;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.security.*;
import java.util.ArrayList;
+import sun.security.timestamp.TimestampToken;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;
@@ -51,6 +55,8 @@
AlgorithmId digestAlgorithmId;
AlgorithmId digestEncryptionAlgorithmId;
byte[] encryptedDigest;
+ Timestamp timestamp;
+ private boolean hasTimestamp = true;
PKCS9Attributes authenticatedAttributes;
PKCS9Attributes unauthenticatedAttributes;
@@ -442,6 +448,62 @@
return unauthenticatedAttributes;
}
+ /*
+ * Extracts a timestamp from a PKCS7 SignerInfo.
+ *
+ * Examines the signer's unsigned attributes for a
+ * <tt>signatureTimestampToken</tt> attribute. If present,
+ * then it is parsed to extract the date and time at which the
+ * timestamp was generated.
+ *
+ * @param info A signer information element of a PKCS 7 block.
+ *
+ * @return A timestamp token or null if none is present.
+ * @throws IOException if an error is encountered while parsing the
+ * PKCS7 data.
+ * @throws NoSuchAlgorithmException if an error is encountered while
+ * verifying the PKCS7 object.
+ * @throws SignatureException if an error is encountered while
+ * verifying the PKCS7 object.
+ * @throws CertificateException if an error is encountered while generating
+ * the TSA's certpath.
+ */
+ public Timestamp getTimestamp()
+ throws IOException, NoSuchAlgorithmException, SignatureException,
+ CertificateException
+ {
+ if (timestamp != null || !hasTimestamp)
+ return timestamp;
+
+ if (unauthenticatedAttributes == null) {
+ hasTimestamp = false;
+ return null;
+ }
+ PKCS9Attribute tsTokenAttr =
+ unauthenticatedAttributes.getAttribute(
+ PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
+ if (tsTokenAttr == null) {
+ hasTimestamp = false;
+ return null;
+ }
+
+ PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue());
+ // Extract the content (an encoded timestamp token info)
+ byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
+ // Extract the signer (the Timestamping Authority)
+ // while verifying the content
+ SignerInfo[] tsa = tsToken.verify(encTsTokenInfo);
+ // Expect only one signer
+ ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ CertPath tsaChain = cf.generateCertPath(chain);
+ // Create a timestamp token info object
+ TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
+ // Create a timestamp object
+ timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain);
+ return timestamp;
+ }
+
public String toString() {
HexDumpEncoder hexDump = new HexDumpEncoder();
@@ -467,5 +529,4 @@
}
return out;
}
-
}
--- a/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java Wed Oct 19 10:15:23 2011 -0400
@@ -28,13 +28,13 @@
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.net.URI;
import java.net.URL;
import java.net.HttpURLConnection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import sun.misc.IOUtils;
+import sun.security.util.Debug;
/**
* A timestamper that communicates with a Timestamping Authority (TSA)
@@ -58,20 +58,23 @@
private static final String TS_REPLY_MIME_TYPE =
"application/timestamp-reply";
- private static final boolean DEBUG = false;
+ private static final Debug debug = Debug.getInstance("ts");
/*
- * HTTP URL identifying the location of the TSA
+ * HTTP URI identifying the location of the TSA
*/
- private String tsaUrl = null;
+ private URI tsaURI = null;
/**
* Creates a timestamper that connects to the specified TSA.
*
- * @param tsa The location of the TSA. It must be an HTTP URL.
+ * @param tsa The location of the TSA. It must be an HTTP URI.
+ * @throws IllegalArgumentException if tsaURI is not an HTTP URI
*/
- public HttpTimestamper(String tsaUrl) {
- this.tsaUrl = tsaUrl;
+ public HttpTimestamper(URI tsaURI) {
+ if (!tsaURI.getScheme().equalsIgnoreCase("http"))
+ throw new IllegalArgumentException("TSA must be an HTTP URI");
+ this.tsaURI = tsaURI;
}
/**
@@ -85,7 +88,7 @@
public TSResponse generateTimestamp(TSRequest tsQuery) throws IOException {
HttpURLConnection connection =
- (HttpURLConnection) new URL(tsaUrl).openConnection();
+ (HttpURLConnection) tsaURI.toURL().openConnection();
connection.setDoOutput(true);
connection.setUseCaches(false); // ignore cache
connection.setRequestProperty("Content-Type", TS_QUERY_MIME_TYPE);
@@ -93,15 +96,15 @@
// Avoids the "hang" when a proxy is required but none has been set.
connection.setConnectTimeout(CONNECT_TIMEOUT);
- if (DEBUG) {
+ if (debug != null) {
Set<Map.Entry<String, List<String>>> headers =
- connection.getRequestProperties().entrySet();
- System.out.println(connection.getRequestMethod() + " " + tsaUrl +
+ connection.getRequestProperties().entrySet();
+ debug.println(connection.getRequestMethod() + " " + tsaURI +
" HTTP/1.1");
- for (Map.Entry<String, List<String>> entry : headers) {
- System.out.println(" " + entry);
+ for (Map.Entry<String, List<String>> e : headers) {
+ debug.println(" " + e);
}
- System.out.println();
+ debug.println();
}
connection.connect(); // No HTTP authentication is performed
@@ -112,8 +115,8 @@
byte[] request = tsQuery.encode();
output.write(request, 0, request.length);
output.flush();
- if (DEBUG) {
- System.out.println("sent timestamp query (length=" +
+ if (debug != null) {
+ debug.println("sent timestamp query (length=" +
request.length + ")");
}
} finally {
@@ -127,17 +130,17 @@
byte[] replyBuffer = null;
try {
input = new BufferedInputStream(connection.getInputStream());
- if (DEBUG) {
+ if (debug != null) {
String header = connection.getHeaderField(0);
- System.out.println(header);
+ debug.println(header);
int i = 1;
while ((header = connection.getHeaderField(i)) != null) {
String key = connection.getHeaderFieldKey(i);
- System.out.println(" " + ((key==null) ? "" : key + ": ") +
+ debug.println(" " + ((key==null) ? "" : key + ": ") +
header);
i++;
}
- System.out.println();
+ debug.println();
}
verifyMimeType(connection.getContentType());
@@ -145,8 +148,8 @@
int contentLength = connection.getContentLength();
replyBuffer = IOUtils.readFully(input, contentLength, false);
- if (DEBUG) {
- System.out.println("received timestamp response (length=" +
+ if (debug != null) {
+ debug.println("received timestamp response (length=" +
total + ")");
}
} finally {
--- a/jdk/src/share/classes/sun/security/timestamp/TSRequest.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/TSRequest.java Wed Oct 19 10:15:23 2011 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. 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
@@ -27,10 +27,13 @@
import java.io.IOException;
import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Extension;
import sun.security.util.DerValue;
import sun.security.util.DerOutputStream;
import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AlgorithmId;
/**
* This class provides a timestamp request, as defined in
@@ -64,24 +67,9 @@
public class TSRequest {
- private static final ObjectIdentifier SHA1_OID;
- private static final ObjectIdentifier MD5_OID;
- static {
- ObjectIdentifier sha1 = null;
- ObjectIdentifier md5 = null;
- try {
- sha1 = new ObjectIdentifier("1.3.14.3.2.26");
- md5 = new ObjectIdentifier("1.2.840.113549.2.5");
- } catch (IOException ioe) {
- // should not happen
- }
- SHA1_OID = sha1;
- MD5_OID = md5;
- }
-
private int version = 1;
- private ObjectIdentifier hashAlgorithmId = null;
+ private AlgorithmId hashAlgorithmId = null;
private byte[] hashValue;
@@ -94,30 +82,21 @@
private X509Extension[] extensions = null;
/**
- * Constructs a timestamp request for the supplied hash value..
+ * Constructs a timestamp request for the supplied data.
*
- * @param hashValue The hash value. This is the data to be timestamped.
- * @param hashAlgorithm The name of the hash algorithm.
+ * @param toBeTimeStamped The data to be timestamped.
+ * @param messageDigest The MessageDigest of the hash algorithm to use.
+ * @throws NoSuchAlgorithmException if the hash algorithm is not supported
*/
- public TSRequest(byte[] hashValue, String hashAlgorithm) {
+ public TSRequest(byte[] toBeTimeStamped, MessageDigest messageDigest)
+ throws NoSuchAlgorithmException {
- // Check the common hash algorithms
- if ("MD5".equalsIgnoreCase(hashAlgorithm)) {
- hashAlgorithmId = MD5_OID;
- // Check that the hash value matches the hash algorithm
- assert hashValue.length == 16;
+ this.hashAlgorithmId = AlgorithmId.get(messageDigest.getAlgorithm());
+ this.hashValue = messageDigest.digest(toBeTimeStamped);
+ }
- } else if ("SHA-1".equalsIgnoreCase(hashAlgorithm) ||
- "SHA".equalsIgnoreCase(hashAlgorithm) ||
- "SHA1".equalsIgnoreCase(hashAlgorithm)) {
- hashAlgorithmId = SHA1_OID;
- // Check that the hash value matches the hash algorithm
- assert hashValue.length == 20;
-
- }
- // Clone the hash value
- this.hashValue = new byte[hashValue.length];
- System.arraycopy(hashValue, 0, this.hashValue, 0, hashValue.length);
+ public byte[] getHashedMessage() {
+ return hashValue.clone();
}
/**
@@ -176,9 +155,7 @@
// encode messageImprint
DerOutputStream messageImprint = new DerOutputStream();
- DerOutputStream hashAlgorithm = new DerOutputStream();
- hashAlgorithm.putOID(hashAlgorithmId);
- messageImprint.write(DerValue.tag_Sequence, hashAlgorithm);
+ hashAlgorithmId.encode(messageImprint);
messageImprint.putOctetString(hashValue);
request.write(DerValue.tag_Sequence, messageImprint);
--- a/jdk/src/share/classes/sun/security/timestamp/TSResponse.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/TSResponse.java Wed Oct 19 10:15:23 2011 -0400
@@ -27,6 +27,7 @@
import java.io.IOException;
import sun.security.pkcs.PKCS7;
+import sun.security.util.Debug;
import sun.security.util.DerValue;
/**
@@ -175,18 +176,20 @@
*/
public static final int SYSTEM_FAILURE = 25;
- private static final boolean DEBUG = false;
+ private static final Debug debug = Debug.getInstance("ts");
private int status;
private String[] statusString = null;
- private int failureInfo = -1;
+ private boolean[] failureInfo = null;
private byte[] encodedTsToken = null;
private PKCS7 tsToken = null;
+ private TimestampToken tstInfo;
+
/**
* Constructs an object to store the response to a timestamp request.
*
@@ -215,11 +218,11 @@
}
/**
- * Retrieve the failure code returned by the TSA.
+ * Retrieve the failure info returned by the TSA.
*
- * @return If -1 then no failure code was received.
+ * @return the failure info, or null if no failure code was received.
*/
- public int getFailureCode() {
+ public boolean[] getFailureInfo() {
return failureInfo;
}
@@ -250,42 +253,38 @@
}
}
+ private boolean isSet(int position) {
+ return failureInfo[position];
+ }
+
public String getFailureCodeAsText() {
- if (failureInfo == -1) {
- return null;
+ if (failureInfo == null) {
+ return "";
}
- switch (failureInfo) {
-
- case BAD_ALG:
- return "Unrecognized or unsupported alrorithm identifier.";
-
- case BAD_REQUEST:
- return "The requested transaction is not permitted or supported.";
-
- case BAD_DATA_FORMAT:
- return "The data submitted has the wrong format.";
-
- case TIME_NOT_AVAILABLE:
- return "The TSA's time source is not available.";
+ try {
+ if (isSet(BAD_ALG))
+ return "Unrecognized or unsupported algorithm identifier.";
+ if (isSet(BAD_REQUEST))
+ return "The requested transaction is not permitted or " +
+ "supported.";
+ if (isSet(BAD_DATA_FORMAT))
+ return "The data submitted has the wrong format.";
+ if (isSet(TIME_NOT_AVAILABLE))
+ return "The TSA's time source is not available.";
+ if (isSet(UNACCEPTED_POLICY))
+ return "The requested TSA policy is not supported by the TSA.";
+ if (isSet(UNACCEPTED_EXTENSION))
+ return "The requested extension is not supported by the TSA.";
+ if (isSet(ADD_INFO_NOT_AVAILABLE))
+ return "The additional information requested could not be " +
+ "understood or is not available.";
+ if (isSet(SYSTEM_FAILURE))
+ return "The request cannot be handled due to system failure.";
+ } catch (ArrayIndexOutOfBoundsException ex) {}
- case UNACCEPTED_POLICY:
- return "The requested TSA policy is not supported by the TSA.";
-
- case UNACCEPTED_EXTENSION:
- return "The requested extension is not supported by the TSA.";
-
- case ADD_INFO_NOT_AVAILABLE:
- return "The additional information requested could not be " +
- "understood or is not available.";
-
- case SYSTEM_FAILURE:
- return "The request cannot be handled due to system failure.";
-
- default:
- return ("unknown status code " + status);
- }
+ return ("unknown failure code");
}
/**
@@ -297,6 +296,10 @@
return tsToken;
}
+ public TimestampToken getTimestampToken() {
+ return tstInfo;
+ }
+
/**
* Retrieve the ASN.1 BER encoded timestamp token returned by the TSA.
*
@@ -323,29 +326,30 @@
// Parse status
- DerValue status = derValue.data.getDerValue();
- // Parse status
- this.status = status.data.getInteger();
- if (DEBUG) {
- System.out.println("timestamp response: status=" + this.status);
+ DerValue statusInfo = derValue.data.getDerValue();
+ this.status = statusInfo.data.getInteger();
+ if (debug != null) {
+ debug.println("timestamp response: status=" + this.status);
}
// Parse statusString, if present
- if (status.data.available() > 0) {
- DerValue[] strings = status.data.getSequence(1);
- statusString = new String[strings.length];
- for (int i = 0; i < strings.length; i++) {
- statusString[i] = strings[i].data.getUTF8String();
+ if (statusInfo.data.available() > 0) {
+ byte tag = (byte)statusInfo.data.peekByte();
+ if (tag == DerValue.tag_SequenceOf) {
+ DerValue[] strings = statusInfo.data.getSequence(1);
+ statusString = new String[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ statusString[i] = strings[i].getUTF8String();
+ if (debug != null) {
+ debug.println("timestamp response: statusString=" +
+ statusString[i]);
+ }
+ }
}
}
// Parse failInfo, if present
- if (status.data.available() > 0) {
- byte[] failInfo = status.data.getBitString();
- int failureInfo = (new Byte(failInfo[0])).intValue();
- if (failureInfo < 0 || failureInfo > 25 || failInfo.length != 1) {
- throw new IOException("Bad encoding for timestamp response: " +
- "unrecognized value for the failInfo element");
- }
- this.failureInfo = failureInfo;
+ if (statusInfo.data.available() > 0) {
+ this.failureInfo
+ = statusInfo.data.getUnalignedBitString().toBooleanArray();
}
// Parse timeStampToken, if present
@@ -353,6 +357,7 @@
DerValue timestampToken = derValue.data.getDerValue();
encodedTsToken = timestampToken.toByteArray();
tsToken = new PKCS7(encodedTsToken);
+ tstInfo = new TimestampToken(tsToken.getContentInfo().getData());
}
// Check the format of the timestamp response
--- a/jdk/src/share/classes/sun/security/tools/JarSigner.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java Wed Oct 19 10:15:23 2011 -0400
@@ -1277,11 +1277,10 @@
System.out.println(rb.getString("TSA.location.") + tsaUrl);
}
if (tsaCert != null) {
- String certUrl =
- TimestampedSigner.getTimestampingUrl(tsaCert);
- if (certUrl != null) {
+ URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
+ if (tsaURI != null) {
System.out.println(rb.getString("TSA.location.") +
- certUrl);
+ tsaURI);
}
System.out.println(rb.getString("TSA.certificate.") +
printCert("", tsaCert, false, 0, false));
--- a/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java Wed Oct 19 10:15:23 2011 -0400
@@ -25,22 +25,14 @@
package sun.security.tools;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.math.BigInteger;
import java.net.URI;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
-import java.util.List;
import com.sun.jarsigner.*;
-import java.util.Arrays;
-import sun.security.pkcs.*;
-import sun.security.timestamp.*;
+import sun.security.pkcs.PKCS7;
import sun.security.util.*;
import sun.security.x509.*;
@@ -57,36 +49,12 @@
public final class TimestampedSigner extends ContentSigner {
/*
- * Random number generator for creating nonce values
- */
- private static final SecureRandom RANDOM;
- static {
- SecureRandom tmp = null;
- try {
- tmp = SecureRandom.getInstance("SHA1PRNG");
- } catch (NoSuchAlgorithmException e) {
- // should not happen
- }
- RANDOM = tmp;
- }
-
- /*
* Object identifier for the subject information access X.509 certificate
* extension.
*/
private static final String SUBJECT_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.11";
/*
- * Object identifier for the timestamping key purpose.
- */
- 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.
*/
private static final ObjectIdentifier AD_TIMESTAMPING_Id;
@@ -100,26 +68,6 @@
AD_TIMESTAMPING_Id = tmp;
}
- /*
- * Location of the TSA.
- */
- private String tsaUrl = null;
-
- /*
- * TSA's X.509 certificate.
- */
- private X509Certificate tsaCertificate = null;
-
- /*
- * Generates an SHA-1 hash value for the data to be timestamped.
- */
- private MessageDigest messageDigest = null;
-
- /*
- * Parameters for the timestamping protocol.
- */
- private boolean tsRequestCertificate = true;
-
/**
* Instantiates a content signer that supports timestamped signatures.
*/
@@ -134,7 +82,7 @@
* and optionally the content that was signed, are packaged into a PKCS #7
* signed data message.
*
- * @param parameters The non-null input parameters.
+ * @param params The non-null input parameters.
* @param omitContent true if the content should be omitted from the
* signed data message. Otherwise the content is included.
* @param applyTimestamp true if the signature should be timestamped.
@@ -151,98 +99,41 @@
* @throws NullPointerException The exception is thrown if parameters is
* null.
*/
- public byte[] generateSignedData(ContentSignerParameters parameters,
+ public byte[] generateSignedData(ContentSignerParameters params,
boolean omitContent, boolean applyTimestamp)
throws NoSuchAlgorithmException, CertificateException, IOException {
- if (parameters == null) {
+ if (params == null) {
throw new NullPointerException();
}
- // Parse the signature algorithm to extract the digest and key
- // algorithms. The expected format is:
+ // Parse the signature algorithm to extract the digest
+ // algorithm. The expected format is:
// "<digest>with<encryption>"
// or "<digest>with<encryption>and<mgf>"
- String signatureAlgorithm = parameters.getSignatureAlgorithm();
- String keyAlgorithm =
- AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
- String digestAlgorithm =
- AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
- AlgorithmId digestAlgorithmId = AlgorithmId.get(digestAlgorithm);
+ String signatureAlgorithm = params.getSignatureAlgorithm();
- // Examine signer's certificate
- X509Certificate[] signerCertificateChain =
- parameters.getSignerCertificateChain();
- Principal issuerName = signerCertificateChain[0].getIssuerDN();
- if (!(issuerName instanceof X500Name)) {
- // must extract the original encoded form of DN for subsequent
- // name comparison checks (converting to a String and back to
- // an encoded DN could cause the types of String attribute
- // values to be changed)
- X509CertInfo tbsCert = new
- X509CertInfo(signerCertificateChain[0].getTBSCertificate());
- issuerName = (Principal)
- tbsCert.get(CertificateIssuerName.NAME + "." +
- CertificateIssuerName.DN_NAME);
- }
- BigInteger serialNumber = signerCertificateChain[0].getSerialNumber();
+ X509Certificate[] signerChain = params.getSignerCertificateChain();
+ byte[] signature = params.getSignature();
// Include or exclude content
- byte[] content = parameters.getContent();
- ContentInfo contentInfo;
- if (omitContent) {
- contentInfo = new ContentInfo(ContentInfo.DATA_OID, null);
- } else {
- contentInfo = new ContentInfo(content);
- }
+ byte[] content = (omitContent == true) ? null : params.getContent();
- // Generate the timestamp token
- byte[] signature = parameters.getSignature();
- SignerInfo signerInfo = null;
+ URI tsaURI = null;
if (applyTimestamp) {
-
- tsaCertificate = parameters.getTimestampingAuthorityCertificate();
- URI tsaUri = parameters.getTimestampingAuthority();
- if (tsaUri != null) {
- tsaUrl = tsaUri.toString();
- } else {
+ tsaURI = params.getTimestampingAuthority();
+ if (tsaURI == null) {
// Examine TSA cert
- String certUrl = getTimestampingUrl(tsaCertificate);
- if (certUrl == null) {
+ tsaURI = getTimestampingURI(
+ params.getTimestampingAuthorityCertificate());
+ if (tsaURI == null) {
throw new CertificateException(
"Subject Information Access extension not found");
}
- tsaUrl = certUrl;
}
-
- // Timestamp the signature
- byte[] tsToken = generateTimestampToken(signature);
-
- // Insert the timestamp token into the PKCS #7 signer info element
- // (as an unsigned attribute)
- PKCS9Attributes unsignedAttrs =
- new PKCS9Attributes(new PKCS9Attribute[]{
- new PKCS9Attribute(
- PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
- tsToken)});
- signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
- digestAlgorithmId, null, AlgorithmId.get(keyAlgorithm),
- signature, unsignedAttrs);
- } else {
- signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
- digestAlgorithmId, AlgorithmId.get(keyAlgorithm), signature);
}
-
- SignerInfo[] signerInfos = {signerInfo};
- AlgorithmId[] algorithms = {digestAlgorithmId};
-
- // Create the PKCS #7 signed data message
- PKCS7 p7 = new PKCS7(algorithms, contentInfo, signerCertificateChain,
- null, signerInfos);
- ByteArrayOutputStream p7out = new ByteArrayOutputStream();
- p7.encodeSignedData(p7out);
-
- return p7out.toByteArray();
+ return PKCS7.generateSignedData(signature, signerChain, content,
+ params.getSignatureAlgorithm(), tsaURI);
}
/**
@@ -253,9 +144,9 @@
* <tt>accessLocation</tt> field should contain an HTTP or HTTPS URL.
*
* @param tsaCertificate An X.509 certificate for the TSA.
- * @return An HTTP or HTTPS URL or null if none was found.
+ * @return An HTTP or HTTPS URI or null if none was found.
*/
- public static String getTimestampingUrl(X509Certificate tsaCertificate) {
+ public static URI getTimestampingURI(X509Certificate tsaCertificate) {
if (tsaCertificate == null) {
return null;
@@ -282,7 +173,7 @@
uri = (URIName) location.getName();
if (uri.getScheme().equalsIgnoreCase("http") ||
uri.getScheme().equalsIgnoreCase("https")) {
- return uri.getName();
+ return uri.getURI();
}
}
}
@@ -292,97 +183,4 @@
}
return null;
}
-
- /*
- * Returns a timestamp token from a TSA for the given content.
- * Performs a basic check on the token to confirm that it has been signed
- * by a certificate that is permitted to sign timestamps.
- *
- * @param toBeTimestamped The data to be timestamped.
- * @throws IOException The exception is throw if an error occurs while
- * communicating with the TSA.
- * @throws CertificateException The exception is throw if the TSA's
- * certificate is not permitted for timestamping.
- */
- private byte[] generateTimestampToken(byte[] toBeTimestamped)
- throws CertificateException, IOException {
-
- // Generate hash value for the data to be timestamped
- // SHA-1 is always used.
- if (messageDigest == null) {
- try {
- messageDigest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- // ignore
- }
- }
- byte[] digest = messageDigest.digest(toBeTimestamped);
-
- // Generate a timestamp
- TSRequest tsQuery = new TSRequest(digest, "SHA-1");
- // Generate a nonce
- BigInteger nonce = null;
- if (RANDOM != null) {
- nonce = new BigInteger(64, RANDOM);
- tsQuery.setNonce(nonce);
- }
- tsQuery.requestCertificate(tsRequestCertificate);
-
- Timestamper tsa = new HttpTimestamper(tsaUrl); // use supplied TSA
- TSResponse tsReply = tsa.generateTimestamp(tsQuery);
- int status = tsReply.getStatusCode();
- // Handle TSP error
- if (status != 0 && status != 1) {
- int failureCode = tsReply.getFailureCode();
- if (failureCode == -1) {
- throw new IOException("Error generating timestamp: " +
- tsReply.getStatusCodeAsText());
- } else {
- throw new IOException("Error generating timestamp: " +
- tsReply.getStatusCodeAsText() + " " +
- tsReply.getFailureCodeAsText());
- }
- }
- PKCS7 tsToken = tsReply.getToken();
-
- TimestampToken tst = new TimestampToken(tsToken.getContentInfo().getData());
- if (!tst.getHashAlgorithm().equals(
- new AlgorithmId(new ObjectIdentifier("1.3.14.3.2.26")))) {
- throw new IOException("Digest algorithm not SHA-1 in timestamp token");
- }
- if (!Arrays.equals(tst.getHashedMessage(), digest)) {
- throw new IOException("Digest octets changed in timestamp token");
- }
-
- BigInteger replyNonce = tst.getNonce();
- if (replyNonce == null && nonce != null) {
- throw new IOException("Nonce missing in timestamp token");
- }
- if (replyNonce != null && !replyNonce.equals(nonce)) {
- throw new IOException("Nonce changed in timestamp token");
- }
-
- // Examine the TSA's certificate (if present)
- 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");
- }
- List<String> keyPurposes = cert.getExtendedKeyUsage();
- if (keyPurposes == null ||
- ! keyPurposes.contains(KP_TIMESTAMPING_OID)) {
- throw new CertificateException(
- "Certificate is not valid for timestamping");
- }
- }
- }
- return tsReply.getEncodedToken();
- }
}
--- a/jdk/src/share/classes/sun/security/util/Debug.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/util/Debug.java Wed Oct 19 10:15:23 2011 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2011, Oracle and/or its affiliates. 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
@@ -80,6 +80,7 @@
System.err.println("policy loading and granting");
System.err.println("provider security provider debugging");
System.err.println("scl permissions SecureClassLoader assigns");
+ System.err.println("ts timestamping");
System.err.println();
System.err.println("The following can be used with access:");
System.err.println();
--- a/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java Wed Oct 19 10:15:23 2011 -0400
@@ -35,7 +35,6 @@
import java.util.jar.*;
import sun.security.pkcs.*;
-import sun.security.timestamp.TimestampToken;
import sun.misc.BASE64Decoder;
import sun.security.jca.Providers;
@@ -485,7 +484,7 @@
signers = new ArrayList<CodeSigner>();
}
// Append the new code signer
- signers.add(new CodeSigner(certChain, getTimestamp(info)));
+ signers.add(new CodeSigner(certChain, info.getTimestamp()));
if (debug != null) {
debug.println("Signature Block Certificate: " +
@@ -500,62 +499,6 @@
}
}
- /*
- * Examines a signature timestamp token to generate a timestamp object.
- *
- * Examines the signer's unsigned attributes for a
- * <tt>signatureTimestampToken</tt> attribute. If present,
- * then it is parsed to extract the date and time at which the
- * timestamp was generated.
- *
- * @param info A signer information element of a PKCS 7 block.
- *
- * @return A timestamp token or null if none is present.
- * @throws IOException if an error is encountered while parsing the
- * PKCS7 data.
- * @throws NoSuchAlgorithmException if an error is encountered while
- * verifying the PKCS7 object.
- * @throws SignatureException if an error is encountered while
- * verifying the PKCS7 object.
- * @throws CertificateException if an error is encountered while generating
- * the TSA's certpath.
- */
- private Timestamp getTimestamp(SignerInfo info)
- throws IOException, NoSuchAlgorithmException, SignatureException,
- CertificateException {
-
- Timestamp timestamp = null;
-
- // Extract the signer's unsigned attributes
- PKCS9Attributes unsignedAttrs = info.getUnauthenticatedAttributes();
- if (unsignedAttrs != null) {
- PKCS9Attribute timestampTokenAttr =
- unsignedAttrs.getAttribute("signatureTimestampToken");
- if (timestampTokenAttr != null) {
- PKCS7 timestampToken =
- new PKCS7((byte[])timestampTokenAttr.getValue());
- // Extract the content (an encoded timestamp token info)
- byte[] encodedTimestampTokenInfo =
- timestampToken.getContentInfo().getData();
- // Extract the signer (the Timestamping Authority)
- // while verifying the content
- SignerInfo[] tsa =
- timestampToken.verify(encodedTimestampTokenInfo);
- // Expect only one signer
- ArrayList<X509Certificate> chain =
- tsa[0].getCertificateChain(timestampToken);
- CertPath tsaChain = certificateFactory.generateCertPath(chain);
- // Create a timestamp token info object
- TimestampToken timestampTokenInfo =
- new TimestampToken(encodedTimestampTokenInfo);
- // Create a timestamp object
- timestamp =
- new Timestamp(timestampTokenInfo.getDate(), tsaChain);
- }
- }
- return timestamp;
- }
-
// for the toHex function
private static final char[] hexc =
{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};