001: /*
002: * @(#)SignerInfo.java 1.48 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.pkcs;
029:
030: import java.io.OutputStream;
031: import java.io.IOException;
032: import java.math.BigInteger;
033: import java.security.cert.X509Certificate;
034: import java.security.*;
035: import java.util.ArrayList;
036:
037: import sun.security.util.*;
038: import sun.security.x509.AlgorithmId;
039: import sun.security.x509.X500Name;
040: import sun.security.x509.KeyUsageExtension;
041: import sun.security.x509.PKIXExtensions;
042: import sun.misc.HexDumpEncoder;
043:
044: /**
045: * A SignerInfo, as defined in PKCS#7's signedData type.
046: *
047: * @author Benjamin Renaud
048: * @version 1.40 02/02/00
049: */
050: public class SignerInfo implements DerEncoder {
051:
052: BigInteger version;
053: X500Name issuerName;
054: BigInteger certificateSerialNumber;
055: AlgorithmId digestAlgorithmId;
056: AlgorithmId digestEncryptionAlgorithmId;
057: byte[] encryptedDigest;
058:
059: PKCS9Attributes authenticatedAttributes;
060: PKCS9Attributes unauthenticatedAttributes;
061:
062: public SignerInfo(X500Name issuerName, BigInteger serial,
063: AlgorithmId digestAlgorithmId,
064: AlgorithmId digestEncryptionAlgorithmId,
065: byte[] encryptedDigest) {
066: this .version = BigInteger.ONE;
067: this .issuerName = issuerName;
068: this .certificateSerialNumber = serial;
069: this .digestAlgorithmId = digestAlgorithmId;
070: this .digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
071: this .encryptedDigest = encryptedDigest;
072: }
073:
074: public SignerInfo(X500Name issuerName, BigInteger serial,
075: AlgorithmId digestAlgorithmId,
076: PKCS9Attributes authenticatedAttributes,
077: AlgorithmId digestEncryptionAlgorithmId,
078: byte[] encryptedDigest,
079: PKCS9Attributes unauthenticatedAttributes) {
080: this .version = BigInteger.ONE;
081: this .issuerName = issuerName;
082: this .certificateSerialNumber = serial;
083: this .digestAlgorithmId = digestAlgorithmId;
084: this .authenticatedAttributes = authenticatedAttributes;
085: this .digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
086: this .encryptedDigest = encryptedDigest;
087: this .unauthenticatedAttributes = unauthenticatedAttributes;
088: }
089:
090: /**
091: * Parses a PKCS#7 signer info.
092: */
093: public SignerInfo(DerInputStream derin) throws IOException,
094: ParsingException {
095: this (derin, false);
096: }
097:
098: /**
099: * 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: // version
111: version = derin.getBigInteger();
112:
113: // issuerAndSerialNumber
114: DerValue[] issuerAndSerialNumber = derin.getSequence(2);
115: byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();
116: issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,
117: issuerBytes));
118: certificateSerialNumber = issuerAndSerialNumber[1]
119: .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 = new PKCS9Attributes(derin);
159: }
160: }
161:
162: // all done
163: if (derin.available() != 0) {
164: throw new ParsingException("extra data at the end");
165: }
166: }
167:
168: public void encode(DerOutputStream out) throws IOException {
169:
170: derEncode(out);
171: }
172:
173: /**
174: * DER encode this object onto an output stream.
175: * Implements the <code>DerEncoder</code> interface.
176: *
177: * @param out
178: * the output stream on which to write the DER encoding.
179: *
180: * @exception IOException on encoding error.
181: */
182: public void derEncode(OutputStream out) throws IOException {
183: DerOutputStream seq = new DerOutputStream();
184: seq.putInteger(version);
185: DerOutputStream issuerAndSerialNumber = new DerOutputStream();
186: issuerName.encode(issuerAndSerialNumber);
187: issuerAndSerialNumber.putInteger(certificateSerialNumber);
188: seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);
189:
190: digestAlgorithmId.encode(seq);
191:
192: // encode authenticated attributes if there are any
193: if (authenticatedAttributes != null)
194: authenticatedAttributes.encode((byte) 0xA0, seq);
195:
196: digestEncryptionAlgorithmId.encode(seq);
197:
198: seq.putOctetString(encryptedDigest);
199:
200: // encode unauthenticated attributes if there are any
201: if (unauthenticatedAttributes != null)
202: unauthenticatedAttributes.encode((byte) 0xA1, seq);
203:
204: DerOutputStream tmp = new DerOutputStream();
205: tmp.write(DerValue.tag_Sequence, seq);
206:
207: out.write(tmp.toByteArray());
208: }
209:
210: /*
211: * Returns the (user) certificate pertaining to this SignerInfo.
212: */
213: public X509Certificate getCertificate(PKCS7 block)
214: throws IOException {
215: return block
216: .getCertificate(certificateSerialNumber, issuerName);
217: }
218:
219: /*
220: * Returns the certificate chain pertaining to this SignerInfo.
221: */
222: public ArrayList getCertificateChain(PKCS7 block)
223: throws IOException {
224: X509Certificate userCert;
225: userCert = block.getCertificate(certificateSerialNumber,
226: issuerName);
227: if (userCert == null)
228: return null;
229:
230: ArrayList certList = new ArrayList();
231: certList.add(userCert);
232:
233: X509Certificate[] pkcsCerts = block.getCertificates();
234: if (pkcsCerts == null
235: || userCert.getSubjectDN().equals(
236: userCert.getIssuerDN())) {
237: return certList;
238: }
239:
240: Principal issuer = userCert.getIssuerDN();
241: int start = 0;
242: while (true) {
243: boolean match = false;
244: int i = start;
245: while (i < pkcsCerts.length) {
246: if (issuer.equals(pkcsCerts[i].getSubjectDN())) {
247: // next cert in chain found
248: certList.add(pkcsCerts[i]);
249: // if selected cert is self-signed, we're done
250: // constructing the chain
251: if (pkcsCerts[i].getSubjectDN().equals(
252: pkcsCerts[i].getIssuerDN())) {
253: start = pkcsCerts.length;
254: } else {
255: issuer = pkcsCerts[i].getIssuerDN();
256: X509Certificate tmpCert = pkcsCerts[start];
257: pkcsCerts[start] = pkcsCerts[i];
258: pkcsCerts[i] = tmpCert;
259: start++;
260: }
261: match = true;
262: break;
263: } else {
264: i++;
265: }
266: }
267: if (!match)
268: break;
269: }
270:
271: return certList;
272: }
273:
274: /* Returns null if verify fails, this signerInfo if
275: verify succeeds. */
276: SignerInfo verify(PKCS7 block, byte[] data)
277: throws NoSuchAlgorithmException, SignatureException {
278:
279: try {
280:
281: ContentInfo content = block.getContentInfo();
282: if (data == null) {
283: data = content.getContentBytes();
284: }
285:
286: String digestAlgname = getDigestAlgorithmId().getName();
287: if (digestAlgname.equalsIgnoreCase("SHA"))
288: digestAlgname = "SHA1";
289:
290: byte[] dataSigned;
291:
292: // if there are authenticate attributes, get the message
293: // digest and compare it with the digest of data
294: if (authenticatedAttributes == null) {
295: dataSigned = data;
296: } else {
297:
298: // first, check content type
299: ObjectIdentifier contentType = (ObjectIdentifier) authenticatedAttributes
300: .getAttributeValue(PKCS9Attribute.CONTENT_TYPE_OID);
301: if (contentType == null
302: || !contentType.equals(content.contentType))
303: return null; // contentType does not match, bad SignerInfo
304:
305: // now, check message digest
306: byte[] messageDigest = (byte[]) authenticatedAttributes
307: .getAttributeValue(PKCS9Attribute.MESSAGE_DIGEST_OID);
308:
309: if (messageDigest == null) // fail if there is no message digest
310: return null;
311:
312: MessageDigest md = MessageDigest
313: .getInstance(digestAlgname);
314: byte[] computedMessageDigest = md.digest(data);
315:
316: if (messageDigest.length != computedMessageDigest.length)
317: return null;
318: for (int i = 0; i < messageDigest.length; i++) {
319: if (messageDigest[i] != computedMessageDigest[i])
320: return null;
321: }
322:
323: // message digest attribute matched
324: // digest of original data
325:
326: // the data actually signed is the DER encoding of
327: // the authenticated attributes (tagged with
328: // the "SET OF" tag, not 0xA0).
329: dataSigned = authenticatedAttributes.getDerEncoding();
330: }
331:
332: // put together digest algorithm and encryption algorithm
333: // to form signing algorithm
334: String encryptionAlgname = getDigestEncryptionAlgorithmId()
335: .getName();
336:
337: if (encryptionAlgname.equalsIgnoreCase("SHA1withDSA"))
338: encryptionAlgname = "DSA";
339: String algname = digestAlgname + "with" + encryptionAlgname;
340:
341: Signature sig = Signature.getInstance(algname);
342: X509Certificate cert = getCertificate(block);
343:
344: if (cert == null) {
345: return null;
346: }
347: if (cert.hasUnsupportedCriticalExtension()) {
348: throw new SignatureException(
349: "Certificate has unsupported "
350: + "critical extension(s)");
351: }
352:
353: // Make sure that if the usage of the key in the certificate is
354: // restricted, it can be used for digital signatures.
355: // TODO: We may want to check for additional extensions in the
356: // future.
357: byte[] keyUsageBytes = cert
358: .getExtensionValue(PKIXExtensions.KeyUsage_Id
359: .toString());
360: if (keyUsageBytes != 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(new Boolean(true),
368: keyUsageBytes);
369: } catch (IOException ioe) {
370: throw new SignatureException(
371: "Failed to parse keyUsage " + "extension");
372: }
373:
374: boolean digSigAllowed = ((Boolean) keyUsage
375: .get(KeyUsageExtension.DIGITAL_SIGNATURE))
376: .booleanValue();
377: if (!digSigAllowed) {
378: throw new SignatureException(
379: "Key usage restricted: "
380: + "cannot be used for "
381: + "digital signatures");
382: }
383: }
384:
385: PublicKey key = cert.getPublicKey();
386: sig.initVerify(key);
387:
388: sig.update(dataSigned);
389:
390: if (sig.verify(encryptedDigest)) {
391: return this ;
392: }
393:
394: } catch (IOException e) {
395: throw new SignatureException(
396: "IO error verifying signature:\n" + e.getMessage());
397:
398: } catch (InvalidKeyException e) {
399: throw new SignatureException("InvalidKey: "
400: + e.getMessage());
401:
402: }
403: return null;
404: }
405:
406: /* Verify the content of the pkcs7 block. */
407: SignerInfo verify(PKCS7 block) throws NoSuchAlgorithmException,
408: SignatureException {
409: return verify(block, null);
410: }
411:
412: public BigInteger getVersion() {
413: return version;
414: }
415:
416: public X500Name getIssuerName() {
417: return issuerName;
418: }
419:
420: public BigInteger getCertificateSerialNumber() {
421: return certificateSerialNumber;
422: }
423:
424: public AlgorithmId getDigestAlgorithmId() {
425: return digestAlgorithmId;
426: }
427:
428: public PKCS9Attributes getAuthenticatedAttributes() {
429: return authenticatedAttributes;
430: }
431:
432: public AlgorithmId getDigestEncryptionAlgorithmId() {
433: return digestEncryptionAlgorithmId;
434: }
435:
436: public byte[] getEncryptedDigest() {
437: return encryptedDigest;
438: }
439:
440: public PKCS9Attributes getUnauthenticatedAttributes() {
441: return unauthenticatedAttributes;
442: }
443:
444: public String toString() {
445: HexDumpEncoder hexDump = new HexDumpEncoder();
446:
447: String out = "";
448:
449: out += "Signer Info for (issuer): " + issuerName + "\n";
450: out += "\tversion: " + Debug.toHexString(version) + "\n";
451: out += "\tcertificateSerialNumber: "
452: + Debug.toHexString(certificateSerialNumber) + "\n";
453: out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";
454: if (authenticatedAttributes != null) {
455: out += "\tauthenticatedAttributes: "
456: + authenticatedAttributes + "\n";
457: }
458: out += "\tdigestEncryptionAlgorithmId: "
459: + digestEncryptionAlgorithmId + "\n";
460:
461: out += "\tencryptedDigest: " + "\n"
462: + hexDump.encodeBuffer(encryptedDigest) + "\n";
463: if (unauthenticatedAttributes != null) {
464: out += "\tunauthenticatedAttributes: "
465: + unauthenticatedAttributes + "\n";
466: }
467: return out;
468: }
469:
470: }
|