1 /*
   2  * Copyright (c) 1996, 2006, 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.pkcs;
  27 
  28 import java.io.*;
  29 import java.math.BigInteger;
  30 import java.util.*;
  31 import java.security.cert.Certificate;
  32 import java.security.cert.X509Certificate;
  33 import java.security.cert.CertificateException;
  34 import java.security.cert.X509CRL;
  35 import java.security.cert.CRLException;
  36 import java.security.cert.CertificateFactory;
  37 import java.security.*;
  38 
  39 import sun.security.util.*;
  40 import sun.security.x509.AlgorithmId;
  41 import sun.security.x509.CertificateIssuerName;
  42 import sun.security.x509.X509CertImpl;
  43 import sun.security.x509.X509CertInfo;
  44 import sun.security.x509.X509CRLImpl;
  45 import sun.security.x509.X500Name;
  46 
  47 /**
  48  * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
  49  * Supports only <tt>SignedData</tt> ContentInfo
  50  * type, where to the type of data signed is plain Data.
  51  * For signedData, <tt>crls</tt>, <tt>attributes</tt> and
  52  * PKCS#6 Extended Certificates are not supported.
  53  *
  54  * @author Benjamin Renaud
  55  */
  56 public class PKCS7 {
  57 
  58     private ObjectIdentifier contentType;
  59 
  60     // the ASN.1 members for a signedData (and other) contentTypes
  61     private BigInteger version = null;
  62     private AlgorithmId[] digestAlgorithmIds = null;
  63     private ContentInfo contentInfo = null;
  64     private X509Certificate[] certificates = null;
  65     private X509CRL[] crls = null;
  66     private SignerInfo[] signerInfos = null;
  67 
  68     private boolean oldStyle = false; // Is this JDK1.1.x-style?
  69 
  70     private Principal[] certIssuerNames;
  71 
  72     /**
  73      * Unmarshals a PKCS7 block from its encoded form, parsing the
  74      * encoded bytes from the InputStream.
  75      *
  76      * @param in an input stream holding at least one PKCS7 block.
  77      * @exception ParsingException on parsing errors.
  78      * @exception IOException on other errors.
  79      */
  80     public PKCS7(InputStream in) throws ParsingException, IOException {
  81         DataInputStream dis = new DataInputStream(in);
  82         byte[] data = new byte[dis.available()];
  83         dis.readFully(data);
  84 
  85         parse(new DerInputStream(data));
  86     }
  87 
  88     /**
  89      * Unmarshals a PKCS7 block from its encoded form, parsing the
  90      * encoded bytes from the DerInputStream.
  91      *
  92      * @param derin a DerInputStream holding at least one PKCS7 block.
  93      * @exception ParsingException on parsing errors.
  94      */
  95     public PKCS7(DerInputStream derin) throws ParsingException {
  96         parse(derin);
  97     }
  98 
  99     /**
 100      * Unmarshals a PKCS7 block from its encoded form, parsing the
 101      * encoded bytes.
 102      *
 103      * @param bytes the encoded bytes.
 104      * @exception ParsingException on parsing errors.
 105      */
 106     public PKCS7(byte[] bytes) throws ParsingException {
 107         try {
 108             DerInputStream derin = new DerInputStream(bytes);
 109             parse(derin);
 110         } catch (IOException ioe1) {
 111             ParsingException pe = new ParsingException(
 112                 "Unable to parse the encoded bytes");
 113             pe.initCause(ioe1);
 114             throw pe;
 115         }
 116     }
 117 
 118     /*
 119      * Parses a PKCS#7 block.
 120      */
 121     private void parse(DerInputStream derin)
 122         throws ParsingException
 123     {
 124         try {
 125             derin.mark(derin.available());
 126             // try new (i.e., JDK1.2) style
 127             parse(derin, false);
 128         } catch (IOException ioe) {
 129             try {
 130                 derin.reset();
 131                 // try old (i.e., JDK1.1.x) style
 132                 parse(derin, true);
 133                 oldStyle = true;
 134             } catch (IOException ioe1) {
 135                 ParsingException pe = new ParsingException(
 136                     ioe1.getMessage());
 137                 pe.initCause(ioe1);
 138                 throw pe;
 139             }
 140         }
 141     }
 142 
 143     /**
 144      * Parses a PKCS#7 block.
 145      *
 146      * @param derin the ASN.1 encoding of the PKCS#7 block.
 147      * @param oldStyle flag indicating whether or not the given PKCS#7 block
 148      * is encoded according to JDK1.1.x.
 149      */
 150     private void parse(DerInputStream derin, boolean oldStyle)
 151         throws IOException
 152     {
 153         contentInfo = new ContentInfo(derin, oldStyle);
 154         contentType = contentInfo.contentType;
 155         DerValue content = contentInfo.getContent();
 156 
 157         if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) {
 158             parseSignedData(content);
 159         } else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) {
 160             // This is for backwards compatibility with JDK 1.1.x
 161             parseOldSignedData(content);
 162         } else if (contentType.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){
 163             parseNetscapeCertChain(content);
 164         } else {
 165             throw new ParsingException("content type " + contentType +
 166                                        " not supported.");
 167         }
 168     }
 169 
 170     /**
 171      * Construct an initialized PKCS7 block.
 172      *
 173      * @param digestAlgorithmIds the message digest algorithm identifiers.
 174      * @param contentInfo the content information.
 175      * @param certificates an array of X.509 certificates.
 176      * @param signerInfos an array of signer information.
 177      */
 178     public PKCS7(AlgorithmId[] digestAlgorithmIds,
 179                  ContentInfo contentInfo,
 180                  X509Certificate[] certificates,
 181                  SignerInfo[] signerInfos) {
 182 
 183         version = BigInteger.ONE;
 184         this.digestAlgorithmIds = digestAlgorithmIds;
 185         this.contentInfo = contentInfo;
 186         this.certificates = certificates;
 187         this.signerInfos = signerInfos;
 188     }
 189 
 190     private void parseNetscapeCertChain(DerValue val)
 191     throws ParsingException, IOException {
 192         DerInputStream dis = new DerInputStream(val.toByteArray());
 193         DerValue[] contents = dis.getSequence(2);
 194         certificates = new X509Certificate[contents.length];
 195 
 196         CertificateFactory certfac = null;
 197         try {
 198             certfac = CertificateFactory.getInstance("X.509");
 199         } catch (CertificateException ce) {
 200             // do nothing
 201         }
 202 
 203         for (int i=0; i < contents.length; i++) {
 204             ByteArrayInputStream bais = null;
 205             try {
 206                 if (certfac == null)
 207                     certificates[i] = new X509CertImpl(contents[i]);
 208                 else {
 209                     byte[] encoded = contents[i].toByteArray();
 210                     bais = new ByteArrayInputStream(encoded);
 211                     certificates[i] =
 212                         (X509Certificate)certfac.generateCertificate(bais);
 213                     bais.close();
 214                     bais = null;
 215                 }
 216             } catch (CertificateException ce) {
 217                 ParsingException pe = new ParsingException(ce.getMessage());
 218                 pe.initCause(ce);
 219                 throw pe;
 220             } catch (IOException ioe) {
 221                 ParsingException pe = new ParsingException(ioe.getMessage());
 222                 pe.initCause(ioe);
 223                 throw pe;
 224             } finally {
 225                 if (bais != null)
 226                     bais.close();
 227             }
 228         }
 229     }
 230 
 231     private void parseSignedData(DerValue val)
 232         throws ParsingException, IOException {
 233 
 234         DerInputStream dis = val.toDerInputStream();
 235 
 236         // Version
 237         version = dis.getBigInteger();
 238 
 239         // digestAlgorithmIds
 240         DerValue[] digestAlgorithmIdVals = dis.getSet(1);
 241         int len = digestAlgorithmIdVals.length;
 242         digestAlgorithmIds = new AlgorithmId[len];
 243         try {
 244             for (int i = 0; i < len; i++) {
 245                 DerValue oid = digestAlgorithmIdVals[i];
 246                 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
 247             }
 248 
 249         } catch (IOException e) {
 250             ParsingException pe =
 251                 new ParsingException("Error parsing digest AlgorithmId IDs: " +
 252                                      e.getMessage());
 253             pe.initCause(e);
 254             throw pe;
 255         }
 256         // contentInfo
 257         contentInfo = new ContentInfo(dis);
 258 
 259         CertificateFactory certfac = null;
 260         try {
 261             certfac = CertificateFactory.getInstance("X.509");
 262         } catch (CertificateException ce) {
 263             // do nothing
 264         }
 265 
 266         /*
 267          * check if certificates (implicit tag) are provided
 268          * (certificates are OPTIONAL)
 269          */
 270         if ((byte)(dis.peekByte()) == (byte)0xA0) {
 271             DerValue[] certVals = dis.getSet(2, true);
 272 
 273             len = certVals.length;
 274             certificates = new X509Certificate[len];
 275 
 276             for (int i = 0; i < len; i++) {
 277                 ByteArrayInputStream bais = null;
 278                 try {
 279                     if (certfac == null)
 280                         certificates[i] = new X509CertImpl(certVals[i]);
 281                     else {
 282                         byte[] encoded = certVals[i].toByteArray();
 283                         bais = new ByteArrayInputStream(encoded);
 284                         certificates[i] =
 285                             (X509Certificate)certfac.generateCertificate(bais);
 286                         bais.close();
 287                         bais = null;
 288                     }
 289                 } catch (CertificateException ce) {
 290                     ParsingException pe = new ParsingException(ce.getMessage());
 291                     pe.initCause(ce);
 292                     throw pe;
 293                 } catch (IOException ioe) {
 294                     ParsingException pe = new ParsingException(ioe.getMessage());
 295                     pe.initCause(ioe);
 296                     throw pe;
 297                 } finally {
 298                     if (bais != null)
 299                         bais.close();
 300                 }
 301             }
 302         }
 303 
 304         // check if crls (implicit tag) are provided (crls are OPTIONAL)
 305         if ((byte)(dis.peekByte()) == (byte)0xA1) {
 306             DerValue[] crlVals = dis.getSet(1, true);
 307 
 308             len = crlVals.length;
 309             crls = new X509CRL[len];
 310 
 311             for (int i = 0; i < len; i++) {
 312                 ByteArrayInputStream bais = null;
 313                 try {
 314                     if (certfac == null)
 315                         crls[i] = (X509CRL) new X509CRLImpl(crlVals[i]);
 316                     else {
 317                         byte[] encoded = crlVals[i].toByteArray();
 318                         bais = new ByteArrayInputStream(encoded);
 319                         crls[i] = (X509CRL) certfac.generateCRL(bais);
 320                         bais.close();
 321                         bais = null;
 322                     }
 323                 } catch (CRLException e) {
 324                     ParsingException pe =
 325                         new ParsingException(e.getMessage());
 326                     pe.initCause(e);
 327                     throw pe;
 328                 } finally {
 329                     if (bais != null)
 330                         bais.close();
 331                 }
 332             }
 333         }
 334 
 335         // signerInfos
 336         DerValue[] signerInfoVals = dis.getSet(1);
 337 
 338         len = signerInfoVals.length;
 339         signerInfos = new SignerInfo[len];
 340 
 341         for (int i = 0; i < len; i++) {
 342             DerInputStream in = signerInfoVals[i].toDerInputStream();
 343             signerInfos[i] = new SignerInfo(in);
 344         }
 345     }
 346 
 347     /*
 348      * Parses an old-style SignedData encoding (for backwards
 349      * compatibility with JDK1.1.x).
 350      */
 351     private void parseOldSignedData(DerValue val)
 352         throws ParsingException, IOException
 353     {
 354         DerInputStream dis = val.toDerInputStream();
 355 
 356         // Version
 357         version = dis.getBigInteger();
 358 
 359         // digestAlgorithmIds
 360         DerValue[] digestAlgorithmIdVals = dis.getSet(1);
 361         int len = digestAlgorithmIdVals.length;
 362 
 363         digestAlgorithmIds = new AlgorithmId[len];
 364         try {
 365             for (int i = 0; i < len; i++) {
 366                 DerValue oid = digestAlgorithmIdVals[i];
 367                 digestAlgorithmIds[i] = AlgorithmId.parse(oid);
 368             }
 369         } catch (IOException e) {
 370             throw new ParsingException("Error parsing digest AlgorithmId IDs");
 371         }
 372 
 373         // contentInfo
 374         contentInfo = new ContentInfo(dis, true);
 375 
 376         // certificates
 377         CertificateFactory certfac = null;
 378         try {
 379             certfac = CertificateFactory.getInstance("X.509");
 380         } catch (CertificateException ce) {
 381             // do nothing
 382         }
 383         DerValue[] certVals = dis.getSet(2);
 384         len = certVals.length;
 385         certificates = new X509Certificate[len];
 386 
 387         for (int i = 0; i < len; i++) {
 388             ByteArrayInputStream bais = null;
 389             try {
 390                 if (certfac == null)
 391                     certificates[i] = new X509CertImpl(certVals[i]);
 392                 else {
 393                     byte[] encoded = certVals[i].toByteArray();
 394                     bais = new ByteArrayInputStream(encoded);
 395                     certificates[i] =
 396                         (X509Certificate)certfac.generateCertificate(bais);
 397                     bais.close();
 398                     bais = null;
 399                 }
 400             } catch (CertificateException ce) {
 401                 ParsingException pe = new ParsingException(ce.getMessage());
 402                 pe.initCause(ce);
 403                 throw pe;
 404             } catch (IOException ioe) {
 405                 ParsingException pe = new ParsingException(ioe.getMessage());
 406                 pe.initCause(ioe);
 407                 throw pe;
 408             } finally {
 409                 if (bais != null)
 410                     bais.close();
 411             }
 412         }
 413 
 414         // crls are ignored.
 415         dis.getSet(0);
 416 
 417         // signerInfos
 418         DerValue[] signerInfoVals = dis.getSet(1);
 419         len = signerInfoVals.length;
 420         signerInfos = new SignerInfo[len];
 421         for (int i = 0; i < len; i++) {
 422             DerInputStream in = signerInfoVals[i].toDerInputStream();
 423             signerInfos[i] = new SignerInfo(in, true);
 424         }
 425     }
 426 
 427     /**
 428      * Encodes the signed data to an output stream.
 429      *
 430      * @param out the output stream to write the encoded data to.
 431      * @exception IOException on encoding errors.
 432      */
 433     public void encodeSignedData(OutputStream out) throws IOException {
 434         DerOutputStream derout = new DerOutputStream();
 435         encodeSignedData(derout);
 436         out.write(derout.toByteArray());
 437     }
 438 
 439     /**
 440      * Encodes the signed data to a DerOutputStream.
 441      *
 442      * @param out the DerOutputStream to write the encoded data to.
 443      * @exception IOException on encoding errors.
 444      */
 445     public void encodeSignedData(DerOutputStream out)
 446         throws IOException
 447     {
 448         DerOutputStream signedData = new DerOutputStream();
 449 
 450         // version
 451         signedData.putInteger(version);
 452 
 453         // digestAlgorithmIds
 454         signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
 455 
 456         // contentInfo
 457         contentInfo.encode(signedData);
 458 
 459         // certificates (optional)
 460         if (certificates != null && certificates.length != 0) {
 461             // cast to X509CertImpl[] since X509CertImpl implements DerEncoder
 462             X509CertImpl implCerts[] = new X509CertImpl[certificates.length];
 463             for (int i = 0; i < certificates.length; i++) {
 464                 if (certificates[i] instanceof X509CertImpl)
 465                     implCerts[i] = (X509CertImpl) certificates[i];
 466                 else {
 467                     try {
 468                         byte[] encoded = certificates[i].getEncoded();
 469                         implCerts[i] = new X509CertImpl(encoded);
 470                     } catch (CertificateException ce) {
 471                         IOException ie = new IOException(ce.getMessage());
 472                         ie.initCause(ce);
 473                         throw ie;
 474                     }
 475                 }
 476             }
 477 
 478             // Add the certificate set (tagged with [0] IMPLICIT)
 479             // to the signed data
 480             signedData.putOrderedSetOf((byte)0xA0, implCerts);
 481         }
 482 
 483         // no crls (OPTIONAL field)
 484 
 485         // signerInfos
 486         signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
 487 
 488         // making it a signed data block
 489         DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
 490                                               signedData.toByteArray());
 491 
 492         // making it a content info sequence
 493         ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,
 494                                             signedDataSeq);
 495 
 496         // writing out the contentInfo sequence
 497         block.encode(out);
 498     }
 499 
 500     /**
 501      * This verifies a given SignerInfo.
 502      *
 503      * @param info the signer information.
 504      * @param bytes the DER encoded content information.
 505      *
 506      * @exception NoSuchAlgorithmException on unrecognized algorithms.
 507      * @exception SignatureException on signature handling errors.
 508      */
 509     public SignerInfo verify(SignerInfo info, byte[] bytes)
 510     throws NoSuchAlgorithmException, SignatureException {
 511         return info.verify(this, bytes);
 512     }
 513 
 514     /**
 515      * Returns all signerInfos which self-verify.
 516      *
 517      * @param bytes the DER encoded content information.
 518      *
 519      * @exception NoSuchAlgorithmException on unrecognized algorithms.
 520      * @exception SignatureException on signature handling errors.
 521      */
 522     public SignerInfo[] verify(byte[] bytes)
 523     throws NoSuchAlgorithmException, SignatureException {
 524 
 525         Vector<SignerInfo> intResult = new Vector<SignerInfo>();
 526         for (int i = 0; i < signerInfos.length; i++) {
 527 
 528             SignerInfo signerInfo = verify(signerInfos[i], bytes);
 529             if (signerInfo != null) {
 530                 intResult.addElement(signerInfo);
 531             }
 532         }
 533         if (intResult.size() != 0) {
 534 
 535             SignerInfo[] result = new SignerInfo[intResult.size()];
 536             intResult.copyInto(result);
 537             return result;
 538         }
 539         return null;
 540     }
 541 
 542     /**
 543      * Returns all signerInfos which self-verify.
 544      *
 545      * @exception NoSuchAlgorithmException on unrecognized algorithms.
 546      * @exception SignatureException on signature handling errors.
 547      */
 548     public SignerInfo[] verify()
 549     throws NoSuchAlgorithmException, SignatureException {
 550         return verify(null);
 551     }
 552 
 553     /**
 554      * Returns the version number of this PKCS7 block.
 555      * @return the version or null if version is not specified
 556      *         for the content type.
 557      */
 558     public  BigInteger getVersion() {
 559         return version;
 560     }
 561 
 562     /**
 563      * Returns the message digest algorithms specified in this PKCS7 block.
 564      * @return the array of Digest Algorithms or null if none are specified
 565      *         for the content type.
 566      */
 567     public AlgorithmId[] getDigestAlgorithmIds() {
 568         return  digestAlgorithmIds;
 569     }
 570 
 571     /**
 572      * Returns the content information specified in this PKCS7 block.
 573      */
 574     public ContentInfo getContentInfo() {
 575         return contentInfo;
 576     }
 577 
 578     /**
 579      * Returns the X.509 certificates listed in this PKCS7 block.
 580      * @return a clone of the array of X.509 certificates or null if
 581      *         none are specified for the content type.
 582      */
 583     public X509Certificate[] getCertificates() {
 584         if (certificates != null)
 585             return certificates.clone();
 586         else
 587             return null;
 588     }
 589 
 590     /**
 591      * Returns the X.509 crls listed in this PKCS7 block.
 592      * @return a clone of the array of X.509 crls or null if none
 593      *         are specified for the content type.
 594      */
 595     public X509CRL[] getCRLs() {
 596         if (crls != null)
 597             return crls.clone();
 598         else
 599             return null;
 600     }
 601 
 602     /**
 603      * Returns the signer's information specified in this PKCS7 block.
 604      * @return the array of Signer Infos or null if none are specified
 605      *         for the content type.
 606      */
 607     public SignerInfo[] getSignerInfos() {
 608         return signerInfos;
 609     }
 610 
 611     /**
 612      * Returns the X.509 certificate listed in this PKCS7 block
 613      * which has a matching serial number and Issuer name, or
 614      * null if one is not found.
 615      *
 616      * @param serial the serial number of the certificate to retrieve.
 617      * @param issuerName the Distinguished Name of the Issuer.
 618      */
 619     public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) {
 620         if (certificates != null) {
 621             if (certIssuerNames == null)
 622                 populateCertIssuerNames();
 623             for (int i = 0; i < certificates.length; i++) {
 624                 X509Certificate cert = certificates[i];
 625                 BigInteger thisSerial = cert.getSerialNumber();
 626                 if (serial.equals(thisSerial)
 627                     && issuerName.equals(certIssuerNames[i]))
 628                 {
 629                     return cert;
 630                 }
 631             }
 632         }
 633         return null;
 634     }
 635 
 636     /**
 637      * Populate array of Issuer DNs from certificates and convert
 638      * each Principal to type X500Name if necessary.
 639      */
 640     private void populateCertIssuerNames() {
 641         if (certificates == null)
 642             return;
 643 
 644         certIssuerNames = new Principal[certificates.length];
 645         for (int i = 0; i < certificates.length; i++) {
 646             X509Certificate cert = certificates[i];
 647             Principal certIssuerName = cert.getIssuerDN();
 648             if (!(certIssuerName instanceof X500Name)) {
 649                 // must extract the original encoded form of DN for
 650                 // subsequent name comparison checks (converting to a
 651                 // String and back to an encoded DN could cause the
 652                 // types of String attribute values to be changed)
 653                 try {
 654                     X509CertInfo tbsCert =
 655                         new X509CertInfo(cert.getTBSCertificate());
 656                     certIssuerName = (Principal)
 657                         tbsCert.get(CertificateIssuerName.NAME + "." +
 658                                     CertificateIssuerName.DN_NAME);
 659                 } catch (Exception e) {
 660                     // error generating X500Name object from the cert's
 661                     // issuer DN, leave name as is.
 662                 }
 663             }
 664             certIssuerNames[i] = certIssuerName;
 665         }
 666     }
 667 
 668     /**
 669      * Returns the PKCS7 block in a printable string form.
 670      */
 671     public String toString() {
 672         String out = "";
 673 
 674         out += contentInfo + "\n";
 675         if (version != null)
 676             out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n";
 677         if (digestAlgorithmIds != null) {
 678             out += "PKCS7 :: digest AlgorithmIds: \n";
 679             for (int i = 0; i < digestAlgorithmIds.length; i++)
 680                 out += "\t" + digestAlgorithmIds[i] + "\n";
 681         }
 682         if (certificates != null) {
 683             out += "PKCS7 :: certificates: \n";
 684             for (int i = 0; i < certificates.length; i++)
 685                 out += "\t" + i + ".   " + certificates[i] + "\n";
 686         }
 687         if (crls != null) {
 688             out += "PKCS7 :: crls: \n";
 689             for (int i = 0; i < crls.length; i++)
 690                 out += "\t" + i + ".   " + crls[i] + "\n";
 691         }
 692         if (signerInfos != null) {
 693             out += "PKCS7 :: signer infos: \n";
 694             for (int i = 0; i < signerInfos.length; i++)
 695                 out += ("\t" + i + ".  " + signerInfos[i] + "\n");
 696         }
 697         return out;
 698     }
 699 
 700     /**
 701      * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false
 702      * otherwise.
 703      */
 704     public boolean isOldStyle() {
 705         return this.oldStyle;
 706     }
 707 }