blob: 75dd0c26bdb3cb95dc476fcd5bfdf7d5883c1cd5 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.pkcs;
27
28import java.io.OutputStream;
29import java.io.IOException;
30import java.math.BigInteger;
31import java.security.cert.X509Certificate;
32import java.security.*;
33import java.util.ArrayList;
34
35import sun.security.util.*;
36import sun.security.x509.AlgorithmId;
37import sun.security.x509.X500Name;
38import sun.security.x509.KeyUsageExtension;
39import sun.security.x509.PKIXExtensions;
40import sun.misc.HexDumpEncoder;
41
42/**
43 * A SignerInfo, as defined in PKCS#7's signedData type.
44 *
45 * @author Benjamin Renaud
46 */
47public class SignerInfo implements DerEncoder {
48
49 BigInteger version;
50 X500Name issuerName;
51 BigInteger certificateSerialNumber;
52 AlgorithmId digestAlgorithmId;
53 AlgorithmId digestEncryptionAlgorithmId;
54 byte[] encryptedDigest;
55
56 PKCS9Attributes authenticatedAttributes;
57 PKCS9Attributes unauthenticatedAttributes;
58
59 public SignerInfo(X500Name issuerName,
60 BigInteger serial,
61 AlgorithmId digestAlgorithmId,
62 AlgorithmId digestEncryptionAlgorithmId,
63 byte[] encryptedDigest) {
64 this.version = BigInteger.ONE;
65 this.issuerName = issuerName;
66 this.certificateSerialNumber = serial;
67 this.digestAlgorithmId = digestAlgorithmId;
68 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
69 this.encryptedDigest = encryptedDigest;
70 }
71
72 public SignerInfo(X500Name issuerName,
73 BigInteger serial,
74 AlgorithmId digestAlgorithmId,
75 PKCS9Attributes authenticatedAttributes,
76 AlgorithmId digestEncryptionAlgorithmId,
77 byte[] encryptedDigest,
78 PKCS9Attributes unauthenticatedAttributes) {
79 this.version = BigInteger.ONE;
80 this.issuerName = issuerName;
81 this.certificateSerialNumber = serial;
82 this.digestAlgorithmId = digestAlgorithmId;
83 this.authenticatedAttributes = authenticatedAttributes;
84 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
85 this.encryptedDigest = encryptedDigest;
86 this.unauthenticatedAttributes = unauthenticatedAttributes;
87 }
88
89 /**
90 * Parses a PKCS#7 signer info.
91 */
92 public SignerInfo(DerInputStream derin)
93 throws IOException, ParsingException
94 {
95 this(derin, false);
96 }
97
98 /**
99 * Parses a PKCS#7 signer info.
100 *
101 * <p>This constructor is used only for backwards compatibility with
102 * PKCS#7 blocks that were generated using JDK1.1.x.
103 *
104 * @param derin the ASN.1 encoding of the signer info.
105 * @param oldStyle flag indicating whether or not the given signer info
106 * is encoded according to JDK1.1.x.
107 */
108 public SignerInfo(DerInputStream derin, boolean oldStyle)
109 throws IOException, ParsingException
110 {
111 // version
112 version = derin.getBigInteger();
113
114 // issuerAndSerialNumber
115 DerValue[] issuerAndSerialNumber = derin.getSequence(2);
116 byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();
117 issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,
118 issuerBytes));
119 certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger();
120
121 // digestAlgorithmId
122 DerValue tmp = derin.getDerValue();
123
124 digestAlgorithmId = AlgorithmId.parse(tmp);
125
126 // authenticatedAttributes
127 if (oldStyle) {
128 // In JDK1.1.x, the authenticatedAttributes are always present,
129 // encoded as an empty Set (Set of length zero)
130 derin.getSet(0);
131 } else {
132 // check if set of auth attributes (implicit tag) is provided
133 // (auth attributes are OPTIONAL)
134 if ((byte)(derin.peekByte()) == (byte)0xA0) {
135 authenticatedAttributes = new PKCS9Attributes(derin);
136 }
137 }
138
139 // digestEncryptionAlgorithmId - little RSA naming scheme -
140 // signature == encryption...
141 tmp = derin.getDerValue();
142
143 digestEncryptionAlgorithmId = AlgorithmId.parse(tmp);
144
145 // encryptedDigest
146 encryptedDigest = derin.getOctetString();
147
148 // unauthenticatedAttributes
149 if (oldStyle) {
150 // In JDK1.1.x, the unauthenticatedAttributes are always present,
151 // encoded as an empty Set (Set of length zero)
152 derin.getSet(0);
153 } else {
154 // check if set of unauth attributes (implicit tag) is provided
155 // (unauth attributes are OPTIONAL)
156 if (derin.available() != 0
157 && (byte)(derin.peekByte()) == (byte)0xA1) {
158 unauthenticatedAttributes =
159 new PKCS9Attributes(derin, true);// ignore unsupported attrs
160 }
161 }
162
163 // all done
164 if (derin.available() != 0) {
165 throw new ParsingException("extra data at the end");
166 }
167 }
168
169 public void encode(DerOutputStream out) throws IOException {
170
171 derEncode(out);
172 }
173
174 /**
175 * DER encode this object onto an output stream.
176 * Implements the <code>DerEncoder</code> interface.
177 *
178 * @param out
179 * the output stream on which to write the DER encoding.
180 *
181 * @exception IOException on encoding error.
182 */
183 public void derEncode(OutputStream out) throws IOException {
184 DerOutputStream seq = new DerOutputStream();
185 seq.putInteger(version);
186 DerOutputStream issuerAndSerialNumber = new DerOutputStream();
187 issuerName.encode(issuerAndSerialNumber);
188 issuerAndSerialNumber.putInteger(certificateSerialNumber);
189 seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);
190
191 digestAlgorithmId.encode(seq);
192
193 // encode authenticated attributes if there are any
194 if (authenticatedAttributes != null)
195 authenticatedAttributes.encode((byte)0xA0, seq);
196
197 digestEncryptionAlgorithmId.encode(seq);
198
199 seq.putOctetString(encryptedDigest);
200
201 // encode unauthenticated attributes if there are any
202 if (unauthenticatedAttributes != null)
203 unauthenticatedAttributes.encode((byte)0xA1, seq);
204
205 DerOutputStream tmp = new DerOutputStream();
206 tmp.write(DerValue.tag_Sequence, seq);
207
208 out.write(tmp.toByteArray());
209 }
210
211
212
213 /*
214 * Returns the (user) certificate pertaining to this SignerInfo.
215 */
216 public X509Certificate getCertificate(PKCS7 block)
217 throws IOException
218 {
219 return block.getCertificate(certificateSerialNumber, issuerName);
220 }
221
222 /*
223 * Returns the certificate chain pertaining to this SignerInfo.
224 */
225 public ArrayList<X509Certificate> getCertificateChain(PKCS7 block)
226 throws IOException
227 {
228 X509Certificate userCert;
229 userCert = block.getCertificate(certificateSerialNumber, issuerName);
230 if (userCert == null)
231 return null;
232
233 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
234 certList.add(userCert);
235
236 X509Certificate[] pkcsCerts = block.getCertificates();
237 if (pkcsCerts == null
238 || userCert.getSubjectDN().equals(userCert.getIssuerDN())) {
239 return certList;
240 }
241
242 Principal issuer = userCert.getIssuerDN();
243 int start = 0;
244 while (true) {
245 boolean match = false;
246 int i = start;
247 while (i < pkcsCerts.length) {
248 if (issuer.equals(pkcsCerts[i].getSubjectDN())) {
249 // next cert in chain found
250 certList.add(pkcsCerts[i]);
251 // if selected cert is self-signed, we're done
252 // constructing the chain
253 if (pkcsCerts[i].getSubjectDN().equals(
254 pkcsCerts[i].getIssuerDN())) {
255 start = pkcsCerts.length;
256 } else {
257 issuer = pkcsCerts[i].getIssuerDN();
258 X509Certificate tmpCert = pkcsCerts[start];
259 pkcsCerts[start] = pkcsCerts[i];
260 pkcsCerts[i] = tmpCert;
261 start++;
262 }
263 match = true;
264 break;
265 } else {
266 i++;
267 }
268 }
269 if (!match)
270 break;
271 }
272
273 return certList;
274 }
275
276 /* Returns null if verify fails, this signerInfo if
277 verify succeeds. */
278 SignerInfo verify(PKCS7 block, byte[] data)
279 throws NoSuchAlgorithmException, SignatureException {
280
281 try {
282
283 ContentInfo content = block.getContentInfo();
284 if (data == null) {
285 data = content.getContentBytes();
286 }
287
288 String digestAlgname = getDigestAlgorithmId().getName();
289 if (digestAlgname.equalsIgnoreCase("SHA"))
290 digestAlgname = "SHA1";
291
292 byte[] dataSigned;
293
294 // if there are authenticate attributes, get the message
295 // digest and compare it with the digest of data
296 if (authenticatedAttributes == null) {
297 dataSigned = data;
298 } else {
299
300 // first, check content type
301 ObjectIdentifier contentType = (ObjectIdentifier)
302 authenticatedAttributes.getAttributeValue(
303 PKCS9Attribute.CONTENT_TYPE_OID);
304 if (contentType == null ||
305 !contentType.equals(content.contentType))
306 return null; // contentType does not match, bad SignerInfo
307
308 // now, check message digest
309 byte[] messageDigest = (byte[])
310 authenticatedAttributes.getAttributeValue(
311 PKCS9Attribute.MESSAGE_DIGEST_OID);
312
313 if (messageDigest == null) // fail if there is no message digest
314 return null;
315
316 MessageDigest md = MessageDigest.getInstance(digestAlgname);
317 byte[] computedMessageDigest = md.digest(data);
318
319 if (messageDigest.length != computedMessageDigest.length)
320 return null;
321 for (int i = 0; i < messageDigest.length; i++) {
322 if (messageDigest[i] != computedMessageDigest[i])
323 return null;
324 }
325
326 // message digest attribute matched
327 // digest of original data
328
329 // the data actually signed is the DER encoding of
330 // the authenticated attributes (tagged with
331 // the "SET OF" tag, not 0xA0).
332 dataSigned = authenticatedAttributes.getDerEncoding();
333 }
334
335 // put together digest algorithm and encryption algorithm
336 // to form signing algorithm
337 String encryptionAlgname =
338 getDigestEncryptionAlgorithmId().getName();
339
340 if (encryptionAlgname.equalsIgnoreCase("SHA1withDSA"))
341 encryptionAlgname = "DSA";
342 String algname = digestAlgname + "with" + encryptionAlgname;
343
344 Signature sig = Signature.getInstance(algname);
345 X509Certificate cert = getCertificate(block);
346
347 if (cert == null) {
348 return null;
349 }
350 if (cert.hasUnsupportedCriticalExtension()) {
351 throw new SignatureException("Certificate has unsupported "
352 + "critical extension(s)");
353 }
354
355 // Make sure that if the usage of the key in the certificate is
356 // restricted, it can be used for digital signatures.
357 // XXX We may want to check for additional extensions in the
358 // future.
359 boolean[] keyUsageBits = cert.getKeyUsage();
360 if (keyUsageBits != null) {
361 KeyUsageExtension keyUsage;
362 try {
363 // We don't care whether or not this extension was marked
364 // critical in the certificate.
365 // We're interested only in its value (i.e., the bits set)
366 // and treat the extension as critical.
367 keyUsage = new KeyUsageExtension(keyUsageBits);
368 } catch (IOException ioe) {
369 throw new SignatureException("Failed to parse keyUsage "
370 + "extension");
371 }
372
373 boolean digSigAllowed = ((Boolean)keyUsage.get(
374 KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue();
375
376 boolean nonRepuAllowed = ((Boolean)keyUsage.get(
377 KeyUsageExtension.NON_REPUDIATION)).booleanValue();
378
379 if (!digSigAllowed && !nonRepuAllowed) {
380 throw new SignatureException("Key usage restricted: "
381 + "cannot be used for "
382 + "digital signatures");
383 }
384 }
385
386 PublicKey key = cert.getPublicKey();
387 sig.initVerify(key);
388
389 sig.update(dataSigned);
390
391 if (sig.verify(encryptedDigest)) {
392 return this;
393 }
394
395 } catch (IOException e) {
396 throw new SignatureException("IO error verifying signature:\n" +
397 e.getMessage());
398
399 } catch (InvalidKeyException e) {
400 throw new SignatureException("InvalidKey: " + e.getMessage());
401
402 }
403 return null;
404 }
405
406 /* Verify the content of the pkcs7 block. */
407 SignerInfo verify(PKCS7 block)
408 throws NoSuchAlgorithmException, SignatureException {
409 return verify(block, null);
410 }
411
412
413 public BigInteger getVersion() {
414 return version;
415 }
416
417 public X500Name getIssuerName() {
418 return issuerName;
419 }
420
421 public BigInteger getCertificateSerialNumber() {
422 return certificateSerialNumber;
423 }
424
425 public AlgorithmId getDigestAlgorithmId() {
426 return digestAlgorithmId;
427 }
428
429 public PKCS9Attributes getAuthenticatedAttributes() {
430 return authenticatedAttributes;
431 }
432
433 public AlgorithmId getDigestEncryptionAlgorithmId() {
434 return digestEncryptionAlgorithmId;
435 }
436
437 public byte[] getEncryptedDigest() {
438 return encryptedDigest;
439 }
440
441 public PKCS9Attributes getUnauthenticatedAttributes() {
442 return unauthenticatedAttributes;
443 }
444
445 public String toString() {
446 HexDumpEncoder hexDump = new HexDumpEncoder();
447
448 String out = "";
449
450 out += "Signer Info for (issuer): " + issuerName + "\n";
451 out += "\tversion: " + Debug.toHexString(version) + "\n";
452 out += "\tcertificateSerialNumber: " +
453 Debug.toHexString(certificateSerialNumber) + "\n";
454 out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";
455 if (authenticatedAttributes != null) {
456 out += "\tauthenticatedAttributes: " + authenticatedAttributes +
457 "\n";
458 }
459 out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId +
460 "\n";
461
462 out += "\tencryptedDigest: " + "\n" +
463 hexDump.encodeBuffer(encryptedDigest) + "\n";
464 if (unauthenticatedAttributes != null) {
465 out += "\tunauthenticatedAttributes: " +
466 unauthenticatedAttributes + "\n";
467 }
468 return out;
469 }
470
471}