提交 4c79d01a 编写于 作者: M mullan

7102686: Restructure timestamp code so that jars and modules can more easily share the same code

Reviewed-by: mchung
上级 f0bc2fbe
......@@ -27,6 +27,7 @@ package sun.security.pkcs;
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.CRLException;
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 @@ public class PKCS7 {
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 class PKCS7 {
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();
}
}
......@@ -28,10 +28,14 @@ package sun.security.pkcs;
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 @@ public class SignerInfo implements DerEncoder {
AlgorithmId digestAlgorithmId;
AlgorithmId digestEncryptionAlgorithmId;
byte[] encryptedDigest;
Timestamp timestamp;
private boolean hasTimestamp = true;
PKCS9Attributes authenticatedAttributes;
PKCS9Attributes unauthenticatedAttributes;
......@@ -442,6 +448,62 @@ public class SignerInfo implements DerEncoder {
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 @@ public class SignerInfo implements DerEncoder {
}
return out;
}
}
......@@ -28,13 +28,13 @@ package sun.security.timestamp;
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 @@ public class HttpTimestamper implements Timestamper {
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 class HttpTimestamper implements Timestamper {
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 @@ public class HttpTimestamper implements Timestamper {
// 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 @@ public class HttpTimestamper implements Timestamper {
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 @@ public class HttpTimestamper implements Timestamper {
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 @@ public class HttpTimestamper implements Timestamper {
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 {
......
/*
* 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 @@ package sun.security.timestamp;
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 @@ import sun.security.util.ObjectIdentifier;
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 @@ public class TSRequest {
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;
} 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;
this.hashAlgorithmId = AlgorithmId.get(messageDigest.getAlgorithm());
this.hashValue = messageDigest.digest(toBeTimeStamped);
}
}
// 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 @@ public class TSRequest {
// 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);
......
......@@ -27,6 +27,7 @@ package sun.security.timestamp;
import java.io.IOException;
import sun.security.pkcs.PKCS7;
import sun.security.util.Debug;
import sun.security.util.DerValue;
/**
......@@ -175,18 +176,20 @@ public class TSResponse {
*/
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 @@ public class TSResponse {
}
/**
* 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 @@ public class TSResponse {
}
}
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.";
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);
}
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) {}
return ("unknown failure code");
}
/**
......@@ -297,6 +296,10 @@ public class TSResponse {
return tsToken;
}
public TimestampToken getTimestampToken() {
return tstInfo;
}
/**
* Retrieve the ASN.1 BER encoded timestamp token returned by the TSA.
*
......@@ -323,29 +326,30 @@ public class TSResponse {
// 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 @@ public class TSResponse {
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
......
......@@ -1277,11 +1277,10 @@ public class JarSigner {
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));
......
......@@ -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.*;
......@@ -56,36 +48,12 @@ import sun.security.x509.*;
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.
*/
......@@ -100,26 +68,6 @@ public final class TimestampedSigner extends ContentSigner {
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 @@ public final class TimestampedSigner extends ContentSigner {
* 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 @@ public final class TimestampedSigner extends ContentSigner {
* @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 @@ public final class TimestampedSigner extends ContentSigner {
* <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 @@ public final class TimestampedSigner extends ContentSigner {
uri = (URIName) location.getName();
if (uri.getScheme().equalsIgnoreCase("http") ||
uri.getScheme().equalsIgnoreCase("https")) {
return uri.getName();
return uri.getURI();
}
}
}
......@@ -292,97 +183,4 @@ public final class TimestampedSigner extends ContentSigner {
}
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();
}
}
/*
* 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 @@ public class Debug {
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();
......
......@@ -35,7 +35,6 @@ import java.util.*;
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 @@ public class SignatureFileVerifier {
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 @@ public class SignatureFileVerifier {
}
}
/*
* 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'};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册