History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/tools/TimestampedSigner.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/tools/TimestampedSigner.java+387−0
@@ -0,0 +1,387 @@
1+/*
2+ * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.security.tools;
27+
28+import java.io.ByteArrayOutputStream;
29+import java.io.IOException;
30+import java.math.BigInteger;
31+import java.net.URI;
32+import java.security.MessageDigest;
33+import java.security.NoSuchAlgorithmException;
34+import java.security.Principal;
35+import java.security.SecureRandom;
36+import java.security.cert.CertificateException;
37+import java.security.cert.X509Certificate;
38+import java.util.List;
39+
40+import com.sun.jarsigner.*;
41+import java.util.Arrays;
42+import sun.security.pkcs.*;
43+import sun.security.timestamp.*;
44+import sun.security.util.*;
45+import sun.security.x509.*;
46+
47+/**
48+ * This class implements a content signing service.
49+ * It generates a timestamped signature for a given content according to
50+ * <a href="http://www.ietf.org/rfc/rfc3161.txt">RFC 3161</a>.
51+ * The signature along with a trusted timestamp and the signer's certificate
52+ * are all packaged into a standard PKCS #7 Signed Data message.
53+ *
54+ * @author Vincent Ryan
55+ */
56+
57+public final class TimestampedSigner extends ContentSigner {
58+
59+ /*
60+ * Random number generator for creating nonce values
61+ */
62+ private static final SecureRandom RANDOM;
63+ static {
64+ SecureRandom tmp = null;
65+ try {
66+ tmp = SecureRandom.getInstance("SHA1PRNG");
67+ } catch (NoSuchAlgorithmException e) {
68+ // should not happen
69+ }
70+ RANDOM = tmp;
71+ }
72+
73+ /*
74+ * Object identifier for the subject information access X.509 certificate
75+ * extension.
76+ */
77+ private static final String SUBJECT_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.11";
78+
79+ /*
80+ * Object identifier for the timestamping key purpose.
81+ */
82+ private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
83+
84+ /*
85+ * Object identifier for extendedKeyUsage extension
86+ */
87+ private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
88+
89+ /*
90+ * Object identifier for the timestamping access descriptors.
91+ */
92+ private static final ObjectIdentifier AD_TIMESTAMPING_Id;
93+ static {
94+ ObjectIdentifier tmp = null;
95+ try {
96+ tmp = new ObjectIdentifier("1.3.6.1.5.5.7.48.3");
97+ } catch (IOException e) {
98+ // ignore
99+ }
100+ AD_TIMESTAMPING_Id = tmp;
101+ }
102+
103+ /*
104+ * Location of the TSA.
105+ */
106+ private String tsaUrl = null;
107+
108+ /*
109+ * TSA's X.509 certificate.
110+ */
111+ private X509Certificate tsaCertificate = null;
112+
113+ /*
114+ * Generates an SHA-1 hash value for the data to be timestamped.
115+ */
116+ private MessageDigest messageDigest = null;
117+
118+ /*
119+ * Parameters for the timestamping protocol.
120+ */
121+ private boolean tsRequestCertificate = true;
122+
123+ /**
124+ * Instantiates a content signer that supports timestamped signatures.
125+ */
126+ public TimestampedSigner() {
127+ }
128+
129+ /**
130+ * Generates a PKCS #7 signed data message that includes a signature
131+ * timestamp.
132+ * This method is used when a signature has already been generated.
133+ * The signature, a signature timestamp, the signer's certificate chain,
134+ * and optionally the content that was signed, are packaged into a PKCS #7
135+ * signed data message.
136+ *
137+ * @param parameters The non-null input parameters.
138+ * @param omitContent true if the content should be omitted from the
139+ * signed data message. Otherwise the content is included.
140+ * @param applyTimestamp true if the signature should be timestamped.
141+ * Otherwise timestamping is not performed.
142+ * @return A PKCS #7 signed data message including a signature timestamp.
143+ * @throws NoSuchAlgorithmException The exception is thrown if the signature
144+ * algorithm is unrecognised.
145+ * @throws CertificateException The exception is thrown if an error occurs
146+ * while processing the signer's certificate or the TSA's
147+ * certificate.
148+ * @throws IOException The exception is thrown if an error occurs while
149+ * generating the signature timestamp or while generating the signed
150+ * data message.
151+ * @throws NullPointerException The exception is thrown if parameters is
152+ * null.
153+ */
154+ public byte[] generateSignedData(ContentSignerParameters parameters,
155+ boolean omitContent, boolean applyTimestamp)
156+ throws NoSuchAlgorithmException, CertificateException, IOException {
157+
158+ if (parameters == null) {
159+ throw new NullPointerException();
160+ }
161+
162+ // Parse the signature algorithm to extract the digest and key
163+ // algorithms. The expected format is:
164+ // "<digest>with<encryption>"
165+ // or "<digest>with<encryption>and<mgf>"
166+ String signatureAlgorithm = parameters.getSignatureAlgorithm();
167+ String keyAlgorithm =
168+ AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
169+ String digestAlgorithm =
170+ AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
171+ AlgorithmId digestAlgorithmId = AlgorithmId.get(digestAlgorithm);
172+
173+ // Examine signer's certificate
174+ X509Certificate[] signerCertificateChain =
175+ parameters.getSignerCertificateChain();
176+ Principal issuerName = signerCertificateChain[0].getIssuerDN();
177+ if (!(issuerName instanceof X500Name)) {
178+ // must extract the original encoded form of DN for subsequent
179+ // name comparison checks (converting to a String and back to
180+ // an encoded DN could cause the types of String attribute
181+ // values to be changed)
182+ X509CertInfo tbsCert = new
183+ X509CertInfo(signerCertificateChain[0].getTBSCertificate());
184+ issuerName = (Principal)
185+ tbsCert.get(CertificateIssuerName.NAME + "." +
186+ CertificateIssuerName.DN_NAME);
187+ }
188+ BigInteger serialNumber = signerCertificateChain[0].getSerialNumber();
189+
190+ // Include or exclude content
191+ byte[] content = parameters.getContent();
192+ ContentInfo contentInfo;
193+ if (omitContent) {
194+ contentInfo = new ContentInfo(ContentInfo.DATA_OID, null);
195+ } else {
196+ contentInfo = new ContentInfo(content);
197+ }
198+
199+ // Generate the timestamp token
200+ byte[] signature = parameters.getSignature();
201+ SignerInfo signerInfo = null;
202+ if (applyTimestamp) {
203+
204+ tsaCertificate = parameters.getTimestampingAuthorityCertificate();
205+ URI tsaUri = parameters.getTimestampingAuthority();
206+ if (tsaUri != null) {
207+ tsaUrl = tsaUri.toString();
208+ } else {
209+ // Examine TSA cert
210+ String certUrl = getTimestampingUrl(tsaCertificate);
211+ if (certUrl == null) {
212+ throw new CertificateException(
213+ "Subject Information Access extension not found");
214+ }
215+ tsaUrl = certUrl;
216+ }
217+
218+ // Timestamp the signature
219+ byte[] tsToken = generateTimestampToken(signature);
220+
221+ // Insert the timestamp token into the PKCS #7 signer info element
222+ // (as an unsigned attribute)
223+ PKCS9Attributes unsignedAttrs =
224+ new PKCS9Attributes(new PKCS9Attribute[]{
225+ new PKCS9Attribute(
226+ PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
227+ tsToken)});
228+ signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
229+ digestAlgorithmId, null, AlgorithmId.get(keyAlgorithm),
230+ signature, unsignedAttrs);
231+ } else {
232+ signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
233+ digestAlgorithmId, AlgorithmId.get(keyAlgorithm), signature);
234+ }
235+
236+ SignerInfo[] signerInfos = {signerInfo};
237+ AlgorithmId[] algorithms = {digestAlgorithmId};
238+
239+ // Create the PKCS #7 signed data message
240+ PKCS7 p7 = new PKCS7(algorithms, contentInfo, signerCertificateChain,
241+ null, signerInfos);
242+ ByteArrayOutputStream p7out = new ByteArrayOutputStream();
243+ p7.encodeSignedData(p7out);
244+
245+ return p7out.toByteArray();
246+ }
247+
248+ /**
249+ * Examine the certificate for a Subject Information Access extension
250+ * (<a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280</a>).
251+ * The extension's <tt>accessMethod</tt> field should contain the object
252+ * identifier defined for timestamping: 1.3.6.1.5.5.7.48.3 and its
253+ * <tt>accessLocation</tt> field should contain an HTTP or HTTPS URL.
254+ *
255+ * @param tsaCertificate An X.509 certificate for the TSA.
256+ * @return An HTTP or HTTPS URL or null if none was found.
257+ */
258+ public static String getTimestampingUrl(X509Certificate tsaCertificate) {
259+
260+ if (tsaCertificate == null) {
261+ return null;
262+ }
263+ // Parse the extensions
264+ try {
265+ byte[] extensionValue =
266+ tsaCertificate.getExtensionValue(SUBJECT_INFO_ACCESS_OID);
267+ if (extensionValue == null) {
268+ return null;
269+ }
270+ DerInputStream der = new DerInputStream(extensionValue);
271+ der = new DerInputStream(der.getOctetString());
272+ DerValue[] derValue = der.getSequence(5);
273+ AccessDescription description;
274+ GeneralName location;
275+ URIName uri;
276+ for (int i = 0; i < derValue.length; i++) {
277+ description = new AccessDescription(derValue[i]);
278+ if (description.getAccessMethod().equals(AD_TIMESTAMPING_Id)) {
279+ location = description.getAccessLocation();
280+ if (location.getType() == GeneralNameInterface.NAME_URI) {
281+ uri = (URIName) location.getName();
282+ if (uri.getScheme().equalsIgnoreCase("http") ||
283+ uri.getScheme().equalsIgnoreCase("https")) {
284+ return uri.getName();
285+ }
286+ }
287+ }
288+ }
289+ } catch (IOException ioe) {
290+ // ignore
291+ }
292+ return null;
293+ }
294+
295+ /*
296+ * Returns a timestamp token from a TSA for the given content.
297+ * Performs a basic check on the token to confirm that it has been signed
298+ * by a certificate that is permitted to sign timestamps.
299+ *
300+ * @param toBeTimestamped The data to be timestamped.
301+ * @throws IOException The exception is throw if an error occurs while
302+ * communicating with the TSA.
303+ * @throws CertificateException The exception is throw if the TSA's
304+ * certificate is not permitted for timestamping.
305+ */
306+ private byte[] generateTimestampToken(byte[] toBeTimestamped)
307+ throws CertificateException, IOException {
308+
309+ // Generate hash value for the data to be timestamped
310+ // SHA-1 is always used.
311+ if (messageDigest == null) {
312+ try {
313+ messageDigest = MessageDigest.getInstance("SHA-1");
314+ } catch (NoSuchAlgorithmException e) {
315+ // ignore
316+ }
317+ }
318+ byte[] digest = messageDigest.digest(toBeTimestamped);
319+
320+ // Generate a timestamp
321+ TSRequest tsQuery = new TSRequest(digest, "SHA-1");
322+ // Generate a nonce
323+ BigInteger nonce = null;
324+ if (RANDOM != null) {
325+ nonce = new BigInteger(64, RANDOM);
326+ tsQuery.setNonce(nonce);
327+ }
328+ tsQuery.requestCertificate(tsRequestCertificate);
329+
330+ Timestamper tsa = new HttpTimestamper(tsaUrl); // use supplied TSA
331+ TSResponse tsReply = tsa.generateTimestamp(tsQuery);
332+ int status = tsReply.getStatusCode();
333+ // Handle TSP error
334+ if (status != 0 && status != 1) {
335+ int failureCode = tsReply.getFailureCode();
336+ if (failureCode == -1) {
337+ throw new IOException("Error generating timestamp: " +
338+ tsReply.getStatusCodeAsText());
339+ } else {
340+ throw new IOException("Error generating timestamp: " +
341+ tsReply.getStatusCodeAsText() + " " +
342+ tsReply.getFailureCodeAsText());
343+ }
344+ }
345+ PKCS7 tsToken = tsReply.getToken();
346+
347+ TimestampToken tst = new TimestampToken(tsToken.getContentInfo().getData());
348+ if (!tst.getHashAlgorithm().equals(
349+ new AlgorithmId(new ObjectIdentifier("1.3.14.3.2.26")))) {
350+ throw new IOException("Digest algorithm not SHA-1 in timestamp token");
351+ }
352+ if (!Arrays.equals(tst.getHashedMessage(), digest)) {
353+ throw new IOException("Digest octets changed in timestamp token");
354+ };
355+
356+ BigInteger replyNonce = tst.getNonce();
357+ if (replyNonce == null && nonce != null) {
358+ throw new IOException("Nonce missing in timestamp token");
359+ }
360+ if (replyNonce != null && !replyNonce.equals(nonce)) {
361+ throw new IOException("Nonce changed in timestamp token");
362+ }
363+
364+ // Examine the TSA's certificate (if present)
365+ for (SignerInfo si: tsToken.getSignerInfos()) {
366+ X509Certificate cert = si.getCertificate(tsToken);
367+ if (cert == null) {
368+ // Error, we've already set tsRequestCertificate = true
369+ throw new CertificateException(
370+ "Certificate not included in timestamp token");
371+ } else {
372+ if (!cert.getCriticalExtensionOIDs().contains(
373+ EXTENDED_KEY_USAGE_OID)) {
374+ throw new CertificateException(
375+ "Certificate is not valid for timestamping");
376+ }
377+ List keyPurposes = cert.getExtendedKeyUsage();
378+ if (keyPurposes == null ||
379+ ! keyPurposes.contains(KP_TIMESTAMPING_OID)) {
380+ throw new CertificateException(
381+ "Certificate is not valid for timestamping");
382+ }
383+ }
384+ }
385+ return tsReply.getEncodedToken();
386+ }
387+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t