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