iaik.pkcs.pkcs7
Class SignedData

java.lang.Object
  |
  +--iaik.pkcs.pkcs7.SignedDataStream
        |
        +--iaik.pkcs.pkcs7.SignedData
All Implemented Interfaces:
Content, ContentStream, iaik.pkcs.pkcs7.DigestProvider, EOFListener, EventListener
Direct Known Subclasses:
SignedAndEnvelopedData

public class SignedData
extends SignedDataStream
implements Content

This class represents the non-stream implementation of the PKCS#7 content type SignedData.

Each PKCS#7 content type is associated with a specific object identifier, derived from:

 pkcs-7 OBJECT IDENTIFIER ::=
   { iso(1) member-body(2) US(840) rsadsi(113549)
       pkcs(1) 7 }
 

The object identifier for the SignedData content type is defined as:

signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }

which corresponds to the OID string "1.2.840.1.113549.1.7.2".

The PKCS#7 Cryptographic Message Standard specifies the SignedData content type for providing a syntax for building digital signatures. Content of any type may be signed by any number of signers in parallel. For each signer, a message digest is computed on the content (and any additional authenticating information) with a signer-specific message-digest algorithm. Subsequently, again for each signer, the corresponding message digest from the previous step is encrypted with the particular signer´s private key and - together with some signer-specific information - collected into a SignerInfo value. Finally all created SignerInfo values are collected together with the content for forming a SignedData structure.

This class implements the SignedData structure resulting from the last step described above. The SignedData type is defined as ASN.1 SEQUENCE type containing the following components (see PKCS#7 specification, Version 1.5; notice that the ASN.1 module printed below not fully agrees with the SignedData definition of Version 1.5, since the certificates field is interpreted as SET OF pure X.509 certificates, whereas Version 1.5 also allows PKCS#6 extended certificates!):

 SignedData ::= SEQUENCE {
   version           Version,
   digestAlgorithms  DigestAlgorithmIdentifiers,
   contentInfo       ContentInfo,
   certificates      [0] IMPLICIT Certificates OPTIONAL,
   crls              [1] IMPLICIT CertificateRevocationLists OPTIONAL,
   signerInfos       SignerInfos }
 
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
Certificates ::= SET OF Certificate -- X.509
CertificateRevocationLists ::= SET OF CertificateRevocationList
SignerInfos ::= SET OF SignerInfo

The digestAlgorithms field contains the object identifiers of the message digest algorithms used by the several signers for digesting the content that is supplied in the contentInfo field. The optional certificates field shall contain certificate chains for all the signers of the signerInfos field. The optional crls field may supply information about the revocation status of the certificates specified in the certificates field. And finally, the signerInfos field collects per-signer information for all parciticipated signers including the encrypted digests of the content (i.e. the signer-specific digital signatures of the content).

If there are no signers on the content, the signed-data content type may be used for disseminating certificates and certificate-revocation lists.

Verifying some received signature(s) is done by decrytping the encrypted message digests for each signer with the signer´s public key and comparing the results with idependently computed message digests on the original content.

For more information consult the RSA PKCS#7 specification.

The SignedData version implemented by this class complies with the SignedData structure as used by the S/MIME protocol. In this way, the digest is calculated on only the content octets of the DER encoding of the content field of the inherent ContentInfo structure. At this point it is important to note that currently IAIK-JCE only supports SignedData structures to be used for signing content values of the PKCS#7 Data content type!


When creating a SignedData object for the content to be signed by using the SignedData(byte[] content, int mode) constructor, additionally the transimission mode has to be specified. If the mode is set to SignedData.IMPLICIT the raw data will be included in the SignedData message to be transmitted, but it will be not included if the mode is set to SignedData.EXPLICIT. However, in both cases the raw data has to be supplied when creating the SignedData object, because it is needed for the digest computation:

 byte[] data = ...;
 SignedData signed_data = new SignedData(data, SignedData.IMPLICIT);
 
respectively
 SignedData signed_data = new SignedData(data, SignedData.EXPLICIT);
 
In contrast to the stream-variant of the PKCS#7 SignedData type (implemented by the SignedDataStream class), where explicit and implicit mode require a different proceeding, both cases may be handled in the same way, when using the non-stream variant. In this way, the steps of creating a SignedData object and preparing it for transmission can be summarized in the following way (to simplify matters, we will assume not to include certificate revocation lists):
  1. Create a new SignedData object thereby supplying the raw data to be signed and specifying the transmission mode to be used (either SignedData.IMPLICIT or SignedData.EXPLICIT):
         byte[] data = ...;
         int mode = ...;
         SignedData signed_data = new SignedData(data, mode);
         
  2. Set the certificates of all the signers by calling the setCertificates method. The certificates are supplied as array of X509Certificates an shall contain chains from a known top-level CA to all the signers in the SignerInfo field:
         signed_data.setCertificates(certificates);
         
  3. For each participated signer, create a SignerInfo object, optionally supply it with attributes, and add it to the SignedData structure by calling the addSignerInfo method:
         SignerInfo signer1 = ...;
         signed_data.addSignerInfo(signer1);
         SignerInfo signer2 = ...;
         signed_data.addSignerInfo(signer2);
          ...
         
    You alternatively may collectively add all signers by utilizing the setSignerInfos method.
    When actually adding a SignerInfo the digest computation is performed on the content for the signer´s particular digest algorithm.

  4. Prepare the SignedData object for transmission by transforming it into an ASN1Object or immediately DER encoding it. The former is done by calling the toASN1Object method, the latter by using the getEncoded method:
         ASN1Object obj = signed_data.toASN1Object();
         
    respectively
         byte[] encoding = signed_data.getEncoded();
         
    You alternatively may use a proper writeTo method of the parent SignedDataStream class for immediately encoding this SignedData object to an output stream. When using writeTo in implicit mode, you additionally have the possibility of specifying a particular blockSize for forcing an indefinite constructed encoding of the inherent raw data bytes, instead of of the default definite primitive encoding, e.g:
         0x24 0x80
                   0x04 0x02 0x01 0xAB
                   0x04 0x02 0x23 0x7F
                   0x04 0x01 0xCA
         0x00 0x00
         
    instead of:
         0x04 0x05 0x01 0xAB 0x23 0x7F 0xCA
         
    for encoding the five data bytes 0x01 0xAB 0x23 0x7F 0xCA. The indefinte constrcuted encoding scheme may be preferable when intending to be compatible to the encoding practice of some particular application (for instance some versions of Netscape Navigator).

Unlike the procedure of newly creating a SignedData object to be transmitted, even when using this non-stream implementation of the SignedData content type, it has to be distinguished between IMPLICIT and EXPLICIT mode when parsing a received SignedData message. When operating in IMPLICIT mode, the raw data is included in the received SignedData object, and so the parsing immediately may be performed when creating a SignedData object from the - already decoded - SignedData ASN1Object by calling the SignedData(ASN1Object obj) constructor. On the other side, when the raw data has been transmitted outside the SignedData message (EXPLICIT mode), the SignedData(byte[] content, AlgorithmID[] hashAlgorithms) constructor has be to used for initializing the new SignedData object with raw data and hash algorithms to be used for digest computation; and the decoding has to be performed explicitly by calling the decode method. The initialization is necessary for computing the digests on the raw data for the digest algorithms of all participated signers. Later, during signature verification these digest values have to be compared against the hash values resulting from decrypting the encrypted digests with the signer´s public keys.
All further steps are the same for implicit and explicit mode, and so the proceeding necessary for parsing a received SignedData message and verifying the signatures may be summarized as follows:

  1. If nessecary, first decode the received DER encoded mesage to obtain an ASN1Object:
         ASN1Object obj = DerCoder.decode(received_message);
         
  2. If the ASN1Object resulting from step 1 above represents an implicit SignedData object, use the SignedData(ASN1Object obj) constructor for creating a SignedData object and implicitly parsing the the supplied ASN.1 structure:
         SignedData signedData = new SignedData(obj);
         
    On the other hand, if the ASN1Object resulting from step 1 above represents an explicit SignedData object, use the SignedData(byte[] content, AlgorithmID[] hashAlgorithms) constructor for initializing a new SignedData object with raw data and digest algorithms for hash computation, and subsequently explicitly perform the decoding by means of the decode method (assuming that two hash algorithms are used: SHA and MD5):
         AlgorithmID[] algIDs = { AlgorithmID.sha1, AlgorithmID.md5 };
         SignedData signedData = new SignedData(raw_data, algIDs);
         signedData.decode(obj);
         
  3. Get the SignerInfos from the SignedData object and perform the signature verification:
         // get the signer infos
         SignerInfo[] signer_infos = signed_data.getSignerInfos();
         // verify the signatures
         for (int i=0; i < signer_infos.length; i++) {
           try {
              // verify the signature for SignerInfo at index i
              X509Certificate signer_cert = signed_data.verify(i);
              // if the signature is OK the certificate of the signer is returned
              System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
           } catch (SignatureException ex) {
              // if the signature is not OK a SignatureException is thrown
              System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN());
           }
         }
         
  4. In implicit mode, get the raw data from the SignedData object:
         byte[] data = signedData.getContent();
         
For handling large amounts of data, the parent SignedDataStream class should be used.

PKCS#7v1.6 Support:
===================

This class also may be used for creating/parsing SignedData objects of version 1.6 as used by the SET protocol. Main differences between v1.5 and v1.6 are the hash calculation (v1.6 calcualtes the hash over the whole content encoding and therefore may be unsuitable for handling large amounts of data) and the encoding of SET OF structures which is replaced by SEQUENCE OF in PKCS#7v1.6. For creating a v1.6 SignedData object you only have to call method setVersion immediately after creating a (implicit or explicit) SignedData object:

 int mode = ...;
 byte[] content = ...;
 SignedData signedData = new SignedData(content, mode);
 // version number 2 indicates PKCS#7v1.6
 signedData.setVersion(2);
 ...
 
On the receiving end you only have to take care about the version number when initializing this class for an explicit signed message (in implicit mode you do not have to take care about the version number at all). If version 1.6 is used you have to specify the version number to indicate to calculate the hash over the content encoding:
 // raw data received by other means:
 byte[] content = ...;
 // hash algorithm(s) used
 AlgorithmID[] hashAlgIDs = ...;
 // version 2 indicates PKCS#7v1.6
 int version = 2;
 SignedData signedData = new SignedData(content, hashAlgIDs, version);
 ... 
 
Since PKCS#7v1.6 is unsuitable for stream handling, the stream based class SignedDataStream does not support PKCS#7v1.6.

Version:
File Revision 51
See Also:
Content, ContentInfo, SignerInfo, SignedDataStream

Fields inherited from class iaik.pkcs.pkcs7.SignedDataStream
block_size, certificates, content_info, content_type, crls, EXPLICIT, IMPLICIT, input_stream, mode, signer_infos, this_object, version
 
Constructor Summary
protected SignedData()
          Default constructor for dynamic object creation in ContentInfo.
  SignedData(ASN1Object obj)
          Creates a PKCS#7 SignedData from an ASN1Object.
  SignedData(byte[] content, AlgorithmID[] hashAlgorithms)
          Creates a new SignedData from a byte array holding the content that has been transmitted by other means, and an array specifying the hash algorithms to be used for digesting.
  SignedData(byte[] content, AlgorithmID[] hashAlgorithms, int version)
          Creates a new SignedData from a byte array holding the content that has been transmitted by other means, and an array specifying the hash algorithms to be used for digesting.
  SignedData(byte[] content, int mode)
          Creates a SignedData object from a byte array containing the raw data to be signed.
  SignedData(InputStream is)
          Creates a new SignedData where the DER encoded data is read from the given InputStream.
  SignedData(ObjectID contentType)
          Creates a new SignedData object without any content.
 
Method Summary
 void addSignerInfo(SignerInfo signerInfo)
          Adds a SignerInfo object to this SignedData.
 void decode(ASN1Object obj)
          Decodes the SignedData supplied as ASN1Object.
 void decode(InputStream is)
          Reads and decodes the SignedData from a DerInputStream.
 byte[] getContent()
          Returns the content.
 byte[] getEncoded()
          Returns the DER encoding of this SignedData object as byte array.
 InputStream getInputStream()
          Returns an InputStream from which the contents of this object can be read.
 void setInputStream(InputStream is)
          Sets the InputStream which holds the content to sign.
protected  void setupMessageDigests()
          Sets up the message digests for hash computation.
 void setVersion(int version)
          Sets the version of this SignedData.
protected  ASN1Object toASN1Object(int blockSize)
          Returns this SignedData as ASN1Object.
 String toString(boolean detailed)
          Returns a string giving some - if requested - detailed information about this SignedData object.
 
Methods inherited from class iaik.pkcs.pkcs7.SignedDataStream
getBlockSize, getCertificate, getCertificates, getContentType, getCRLs, getDigestAlgorithms, getMessageDigest, getMode, getSignedDigest, getSignerInfos, getVersion, notifyEOF, setBlockSize, setCertificates, setCRLs, setMessageDigest, setSignerInfos, toASN1Object, toString, verify, verify, verify, writeTo, writeTo
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 
Methods inherited from interface iaik.pkcs.pkcs7.ContentStream
getBlockSize, getContentType, setBlockSize, toASN1Object
 

Constructor Detail

SignedData

protected SignedData()
Default constructor for dynamic object creation in ContentInfo. The block size is set to -1 meaning that the data will be encoded a one simple octet string by deafult.

SignedData

public SignedData(ObjectID contentType)
Creates a new SignedData object without any content. The content value to be signed has to be of the given content type and has to be supplied by other means.
Parameters:
contentType - the contentType of the data

SignedData

public SignedData(InputStream is)
           throws PKCSParsingException,
                  IOException
Creates a new SignedData where the DER encoded data is read from the given InputStream.
Parameters:
is - the InputStream holding a DER encoded PKCS#7 SignedData object
Throws:
IOException - if an I/O error occurs during reading from the InputStream
PKCSParsingException - if an error occurs while parsing the object

SignedData

public SignedData(byte[] content,
                  int mode)
Creates a SignedData object from a byte array containing the raw data to be signed. The given mode specifies the way the raw data is transmitted. If the mode is SignedData.IMPLICIT, the raw data is included in the SignedData message to be transmitted. If the mode is SignedData.EXPLICIT, the raw data is not included in the SignedData object.
Parameters:
content - the raw data to be signed
IMPLICIT - if the message shall be included in the DER encoding, EXPLICIT otherwise

SignedData

public SignedData(ASN1Object obj)
           throws PKCSParsingException
Creates a PKCS#7 SignedData from an ASN1Object.

Do not use this constructor for supplying the content value to be signed. This constructor may be used by the recipient for parsing an already exisiting SignedData object, supplied as ASN1Object that may have been created by calling toASN1Object.

Use the SignedData(byte[] content, int mode) constructor for supplying the content value to be signed when creating a SignedData object.

This constructor only shall be used for parsing an ASN1Object that represents a SignedData object with included raw data bytes (implicit mode).
To initialize a SignedData object for parsing an explicit SignedData message where the raw data is not included, use the SignedData(byte[] content, AlgorithmID[] hashAlgorithms) constructor, and perform the decoding explicitly by calling the decode method.

Parameters:
obj - the PKCS#7 SignedData as ASN1Object representing an implicit SignedData object
Throws:
PKCSParsingException - if a parsing error occurs

SignedData

public SignedData(byte[] content,
                  AlgorithmID[] hashAlgorithms)
           throws NoSuchAlgorithmException
Creates a new SignedData from a byte array holding the content that has been transmitted by other means, and an array specifying the hash algorithms to be used for digesting.

Do not use this constructor for supplying the content value to be signed. This constructor may be used by the recipient for initializing the digest computation for an already existing explicit SignedData message where the raw data is not included. In contrast to the equivalent constructor of the parent SignedDataStream class, this constructor not only initializes the digest computation, but also already computes the digests for all supplied digest algorithms. Later, during signature verification these digest values have to be compared against the hash values resulting from decrypting the encrypted digests with the signer´s public keys.
For subsequently performing the decoding of the received explicit SignedData object, use the decode method:

 // the received explicit SignedData structure as ASN1Object:
 ASN1Object = DerCoder.decode(received_message);
 //initialize - and perform - digest computaion:
 SignedData signedData = new SignedData(raw_data, hashAlgorithms);
 //parse the received SignedData ASN1Object
 signedData.decode(obj);
 

A sender shall use the SignedData(byte[] content, int mode) constructor for supplying the content value to be signed when creating a SignedData object.

For parsing an implicit SignedData message, use the SignedData(ASN1Object obj) constructor.

Parameters:
content - the content transmitted by other means
hashAlgorithms - the hash algorithms used by the participated signers for digesting the content data
Throws:
NoSuchAlgorithmException - if any of the supplied hash algorithms is not supported

SignedData

public SignedData(byte[] content,
                  AlgorithmID[] hashAlgorithms,
                  int version)
           throws NoSuchAlgorithmException
Creates a new SignedData from a byte array holding the content that has been transmitted by other means, and an array specifying the hash algorithms to be used for digesting.

Do not use this constructor for supplying the content value to be signed. This constructor may be used by the recipient for initializing the digest computation for an already existing explicit SignedData message where the raw data is not included. In contrast to the equivalent constructor of the parent SignedDataStream class, this constructor not only initializes the digest computation, but also already computes the digests for all supplied digest algorithms. Later, during signature verification these digest values have to be compared against the hash values resulting from decrypting the encrypted digests with the signer´s public keys.
For subsequently performing the decoding of the received explicit SignedData object, use the decode method:

 // the received explicit SignedData structure as ASN1Object:
 ASN1Object = DerCoder.decode(received_message);
 //initialize - and perform - digest computaion:
 SignedData signedData = new SignedData(raw_data, hashAlgorithms);
 //parse the received SignedData ASN1Object
 signedData.decode(obj);
 

A sender shall use the SignedData(byte[] content, int mode) constructor for supplying the content value to be signed when creating a SignedData object.

For parsing an implicit SignedData message, use the SignedData(ASN1Object obj) constructor.

This constructor may be used for initializing an explicit signed v1.6 SignedData message, where the version number (2) has to be supplied for calculating the hash value(s) over the whole content encoding.

Parameters:
content - the content transmitted by other means
hashAlgorithms - the hash algorithms used by the participated signers for digesting the content data
version - the version; either 1 or 2
Throws:
NoSuchAlgorithmException - if any of the supplied hash algorithms is not supported
Method Detail

addSignerInfo

public void addSignerInfo(SignerInfo signerInfo)
                   throws NoSuchAlgorithmException
Adds a SignerInfo object to this SignedData.

Unlike the same-name method in the SignedDataStream super class this method already performs the digest computation for the given SignerInfo.

Overrides:
addSignerInfo in class SignedDataStream
Parameters:
signerInfo - the SignerInfo to add
Throws:
NoSuchAlgorithmException - if there is no implementation for the message digest algorithm specified in the signerInfo
See Also:
SignerInfo

setupMessageDigests

protected void setupMessageDigests()
                            throws NoSuchAlgorithmException
Sets up the message digests for hash computation. This method is used for message digest setup when parsing an already existing SignedData object. Unlike the same-name method in the SignedDataStream super class this method also - already - performs the digest computation for all participated digest algorithms.
Throws:
NoSuchAlgorithmException - if any of the participated digest algorithms is not supported

decode

public void decode(ASN1Object obj)
            throws PKCSParsingException
Decodes the SignedData supplied as ASN1Object. This method implicitly is called from inside the corresponding constructor for decoding an received implicit SignedData object where the raw data is included. This method has to be explicitly called for decoding a received explicit SignedData object where the raw data is not included. Before calling this method for an explicit SignedData ASN1Object object, a new SignedData object has to be created by means of the SignedData(byte[] content, AlgorithmID[] hashAlgorithms) constructor for initializing it for hash computation:
 // the received explicit SignedData structure as ASN1Object:
 ASN1Object = DerCoder.decode(received_message);
 //initialize - and perform - digest computaion:
 SignedData signedData = new SignedData(raw_data, hashAlgorithms);
 //parse the received SignedData ASN1Object
 signedData.decode(obj);
 
Specified by:
decode in interface Content
Parameters:
obj - the PKCS#7 SignedData as ASN1Object
Throws:
PKCSParsingException - if an error occurs while parsing the object

decode

public void decode(InputStream is)
            throws IOException,
                   PKCSParsingException
Reads and decodes the SignedData from a DerInputStream. If the supplied InputStream actually is not an instance of DerInputStream, internally a DerInputStream is created before parsing the data.

This method provides an alternative to the decoding method where the SignedData to be decoded is given as ASN1Object.

Specified by:
decode in interface ContentStream
Overrides:
decode in class SignedDataStream
Parameters:
is - the InputStream holding a DER encoded PKCS#7 SignedData object
Throws:
IOException - if an I/O error occurs during reading from the InputStream
PKCSParsingException - if an error occurs while parsing the object

setInputStream

public void setInputStream(InputStream is)
Sets the InputStream which holds the content to sign. This method only overrides the same named method of the SignedDataStream parent class.
Overrides:
setInputStream in class SignedDataStream
Parameters:
is - InputSteam holding the content to sign

setVersion

public void setVersion(int version)
Sets the version of this SignedData. The version either may be 1 (indicating a PKCS#7v1.5 SignedData) or 2 (indicating a PKCS#71.6 SignedData); default is 1.
Parameters:
version - the version; either 1 or 2

getInputStream

public InputStream getInputStream()
Returns an InputStream from which the contents of this object can be read.

This method only overrides the corresponding getInputStream method of the parent SignedDataStream class for returning the content of this SignedData object. There should be no real necessity for using this method since the raw data bytes immediately can be obtained by the getContent method. However, in contrast to the equivalent getInputStream method of the parent SignedDataStream class, this method may be called arbitrarly often; it only returns a ByteArrayInputStream that is initialized with the raw content bytes.

Overrides:
getInputStream in class SignedDataStream
Returns:
an InputStream with the contents of this SignedData object

getContent

public byte[] getContent()
Returns the content.
Returns:
the content as byte array

toASN1Object

protected ASN1Object toASN1Object(int blockSize)
                           throws PKCSException
Returns this SignedData as ASN1Object.

If blockSize is set to a positive value the ASN1Object returned by this method is prepared for forcing an indefinite constructed encoding of the inherent raw data bytes, instead of the default definite primitive encoding, e.g:

 0x24 0x80
           0x04 0x02 0x01 0xAB
           0x04 0x02 0x23 0x7F
           0x04 0x01 0xCA
 0x00 0x00
 
instead of:
 0x04 0x05 0x01 0xAB 0x23 0x7F 0xCA
 
for encoding the five data bytes 0x01 0xAB 0x23 0x7F 0xCA. The indefinte constrcuted encoding scheme may be preferable when intending to be compatible to the encoding practice of some particular application (for instance some versions of Netscape Navigator). The inherent data is encoded by means of the primitive definite method when the blockSize value is not positive.
Overrides:
toASN1Object in class SignedDataStream
Parameters:
blockSize - the block size defining the encoding scheme - and specifying the length of each primitive encoded octet string component, if positive
Throws:
PKCSException - if the ASN1Object could not be created

getEncoded

public byte[] getEncoded()
                  throws PKCSException
Returns the DER encoding of this SignedData object as byte array. If no positive blocksize has been yet set, any inherent raw data bytes (implicit mode) will be encoded as primitive definite octet string. Otherwise the encoding of the inherent data will be indefinte constructed (may be forced by using the writeTo(OutputStream os, int blockSize) method of the parent SignedDataStream class.
Returns:
the DER encoding of this SignedData
Throws:
PKCSException - if an encoding error occurs

toString

public String toString(boolean detailed)
Returns a string giving some - if requested - detailed information about this SignedData object.
Specified by:
toString in interface ContentStream
Overrides:
toString in class SignedDataStream
Parameters:
detailed - - whether or not to give detailed information
Returns:
the string representation

This Javadoc may contain text parts from Internet Standard specifications (RFC 2459, 3280, 3039, 2560, 1521, 821, 822, 2253, 1319, 1321, ,2630, 2631, 2268, 3058, 2984, 2104, 2144, 2040, 2311, 2279, see copyright note) and RSA Data Security Public-Key Cryptography Standards (PKCS#1,3,5,7,8,9,10,12, see copyright note).

IAIK-JCE 3.1 with IAIK-JCE CC Core 3.1, (c) 1997-2004 IAIK