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