iaik.pkcs.pkcs7
Class DigestedDataStream

java.lang.Object
  |
  +--iaik.pkcs.pkcs7.DigestedDataStream
All Implemented Interfaces:
ContentStream, EncodeListener, EOFListener, EventListener
Direct Known Subclasses:
DigestedData

public class DigestedDataStream
extends Object
implements ContentStream, EncodeListener, EOFListener

This class represents the stream supporting implementation of the PKCS#7 DigestedData type.

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 DigestedData content type is defined as:

digestedData OBJECT IDENTIFIER ::= { pkcs-7 5 }

which corresponds to the OID string "1.2.840.1.113549.1.7.5".

The PKCS#7 Cryptographic Message Standard specifies the DigestedData content type for providing a syntax for building message digests. The digested-data content type consists of content of any type and a message digest of the content:

 DigestedData ::= SEQUENCE {
    version            Version,
    digestAlgorithm    DigestAlgorithmIdentifier,
    contentInfo        ContentInfo,
    digest             Digest }
 
Digest ::= OCTET STRING

The digestAlgorithm field specifies the digest algorithm to be used for computing the message digest of the content given in the contentInfo field. The result of the digest calculation is stored in the digest field. Verifying a received message digest is done by comparing it with an independently computed message digest.

For more information consult the RSA PKCS#7 specification (section 12.Digested-data content type).


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

 InputSrteam data_stream = ...; // the raw data supplying input stream
 AlgorithmID digestAlg = ...;
 DigestedDataStream digested_data = new DigestedDataStream(data_stream, digestAlg, DigestedDataStream.IMPLICIT);
 
respectively
 DigestedDataStream digested_data = new DigestedDataStream(data_stream, digestAlg, DigestedDataStream.EXPLICIT);
 
In contrast to the non-stream-variant of the PKCS#7 DigestedData type (implemented by the DigestedData class), where explicit and implicit mode can be handled in the same way when creating a DigestedData object, they require a different proceeding for the stream-supporting DigestedDataStream class. In this way, the steps for creating a DigestedDataStream object and preparing it for transmission can be summarized in the following way:
  1. Create a new DigestedDataStream object thereby supplying the raw data to be digested as input stream and specifying digest algorithm and transmission mode to be used (either DigestedDataStream.IMPLICIT or DigestedDataStream.EXPLICIT):
         InputStream data_stream = ...;
         int mode = ...;
         AlgorithmID digestAlg = ...;
         DigestedDataStream digested_data = new DigestedDataStream(data_stream, digestAlg, mode);
         
  2. If the chosen mode is DigestedDataStream.EXPLICIT, now get and read the data from the data carrying input stream. Since in explicit mode the raw data is not included in the DigestedData message, the stream has to be read before actually performing the encoding. When reading the stream, the data is piped a digest stream for updating the hash computation.
         if (mode == DigestedDataStream.EXPLICIT) {
           InputStream data_is = digested_data.getInputStream();
           byte[] buf = new byte[1024];
           int r;
           while ((r = data_is.read(buf)) > 0) {
             // do something useful
           }
         }
         
    When using the implicit mode, do not explicitly read data from the input stream at all! This will be done automatically during the last step when performing the encoding.

  3. Use a proper writeTo method for BER encoding the the DigestedDataStream object and writing it to an output stream. You optionally may specify a particular block size for splitting the data encoding:
         int blockSize = ...;
         digested_data.writeTo(output_stream, blockSize);
         
    respectively
         digested_data.writeTo(output_stream);
         
    It is recommended only to use the writeTo method where a particular block size can be specified, because it is the intended purpose of this stream-supporting DigestedData implementation to handle large amounts of data. When no block size is specified whole the raw data is encoded as one primitive definite octet string, which advantageously may be done when using the non-stream supporting DigestedData implementation. When a positve block size is specified for encoding the DigestedData to a stream, the raw data is BER encoded as indefinite constructed octet string being composed of a series of definite primitive encoded octet strings of blockSize length, 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.

Again, it has to be distinguished between IMPLICIT and EXPLICIT mode when using the DigestedDataStream implementation for parsing a received DigestedData message. When operating in IMPLICIT mode, the raw data is included in the received DigestedData object, and so the parsing immediately may be performed when creating a DigestedDataStream object from the DER encoded DigestedData object by calling the DigestedDataStream(InputStream is) constructor. On the other side, when the raw data has been transmitted outside the DigestedData message (EXPLICIT mode), the DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm) constructor has to be used for initializing the new DigestedDataStream object with raw data and hash algorithm to be used for digest computation; and the decoding has to be performed explicitly by calling the decode method. The initialization is necessary for preparing the digest computation on the raw data for the specified digest algorithm. Later, during digest verification the digest value computaion is finished and the result is compared against the hash value sent within the final digest field.
The individual steps necessary for parsing a received DigestedData message and verifying the digest may be summarized as follows:

  1. If the InputStream supplies the BER encoding of an implicit DigestedData object, use the DigestedDataStream(InputStream is) constructor for creating a DigestedDataStream object and implicitly performing the decoding:
         DigestedDataStream digestedData = new DigestedDataStream(encoded_stream);
         
    On the other hand, if the BER encoding represents an explicit DigestedData object, use the DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm) constructor for initializing a new DigestedDataStream object with raw data and digest algorithm for hash computation (assuming that the hash algorithm is SHA):
         AlgorithmID algID = AlgorithmID.sha1;
         DigestedDataStream digestedData = new DigestedDataStream(data_is, algID);
         
  2. Get the data carrying input stream and entirely read the data from the stream. This has to be performed for both implicit and explicit modes at exactly this state of the parsing procedure. In both cases the constructor has initialized the digest computation by wrapping digest streams around the raw data stream for the given digest algorithm, either implicitly parsed from the supplied DigestedData object, or explicitly supplied as via the constructor. When now reading the stream, the data is piped through the digest stream for updating the hash computation:
         InputStream dataIs = digestedDdata.getInputStream();
         byte[] buf = new byte[1024];
         int r;
         while ((r = dataIs.read(buf)) > 0) {
           // do something useful
         }
         
  3. When dealing with an explicit message now explicitly perform the decoding by calling the decode method:
         digestedData.decode(encoded_stream);
         
  4. Now itīs time to call the verify method for comparing the computed digest against the value sent in the digest field:
         if (digestedData.verify())
            System.out.println("Hash o.k!");
         else
            System.out.println("hash verification failed!");
         
Attention! This class only shall be used for inherent ContentInfos of type Data!

Version:
File Revision 31

Field Summary
protected  int block_size
          The block size for block oriented stream encoding.
protected  ObjectID content_type
          The content type.
static int EXPLICIT
          Denotes a mode where the data to be digested is not included.
static int IMPLICIT
          Denotes a mode where the data to be digested is included.
protected  InputStream input_stream
          An InputStream holding the data.
protected  int mode
          The mode specifying if the data shall be included (IMPLICIT), or if is not included (EXPLICIT).
protected  DerInputStream this_object
          An InputStream from which a DER encoded DigestedData object is read.
 
Constructor Summary
protected DigestedDataStream()
          Default constructor for dynamic object creation in ContentInfo.
  DigestedDataStream(InputStream is)
          Creates a PKCS#7 DigestedData from an InputStream.
  DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm)
          Creates a new DigestedDataStream from an InputStream holding the content that has been transmitted by other means, and the hash algorithm to be used for digesting.
  DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm, int mode)
          Creates a new DigestedDataStream object from given content and and digest algorithm.
  DigestedDataStream(ObjectID contentType, AlgorithmID digestAlgorithm, byte[] digest)
          Creates a new DigestedDataStream object without content.
 
Method Summary
 void decode(InputStream is)
          Reads and decodes the DigestedDataStream from a DerInputStream.
 void encodeCalled(ASN1Object o, int id)
          This method implements the EncodeListener interface.
 int getBlockSize()
          Gets the block size defining the length of each definite primitive encoded octet string component.
 ObjectID getContentType()
          Returns the content type this class implements.
 byte[] getDigest()
          Returns the message-digest computed on the content value.
 AlgorithmID getDigestAlgorithm()
          Returns the message-digest algorithm used for computing the digest.
 InputStream getInputStream()
          Returns an input stream with the raw data.
 int getVersion()
          Returns the version syntax number.
 void notifyEOF()
          This method implements the EOFListener interface for performing the final decoding.
 void setBlockSize(int blockSize)
          Sets the block size for defining the length of each definite primitive encoded octet string component.
 void setDigest(byte[] digest)
          Sets the message-digest value.
 ASN1Object toASN1Object()
          Returns this DigestedDataStream as ASN1Object.
protected  ASN1Object toASN1Object(int blockSize)
          Returns this DigestedData as ASN1Object where a constructed OCTET STRING is used for encoding the content.
 String toString()
          Returns a string giving some information about this DigestedData object.
 String toString(boolean detailed)
          Returns a string giving some - if requested - detailed information about this DigestedData object.
 boolean verify()
          Verifies the digest.
 void writeTo(OutputStream os)
          Writes this DigestedData DER encoded to the supplied output stream.
 void writeTo(OutputStream os, int blockSize)
          Writes this object to the supplied output stream where a constructed OCTET STRING is used for encoding the content.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

IMPLICIT

public static final int IMPLICIT
Denotes a mode where the data to be digested is included.

EXPLICIT

public static final int EXPLICIT
Denotes a mode where the data to be digested is not included.

content_type

protected ObjectID content_type
The content type.

this_object

protected DerInputStream this_object
An InputStream from which a DER encoded DigestedData object is read.

block_size

protected int block_size
The block size for block oriented stream encoding.

input_stream

protected InputStream input_stream
An InputStream holding the data.

mode

protected int mode
The mode specifying if the data shall be included (IMPLICIT), or if is not included (EXPLICIT).
Constructor Detail

DigestedDataStream

protected DigestedDataStream()
Default constructor for dynamic object creation in ContentInfo. The block size is set to 2048.

DigestedDataStream

public DigestedDataStream(InputStream data_is,
                          AlgorithmID digestAlgorithm,
                          int mode)
                   throws PKCSException
Creates a new DigestedDataStream object from given content and and digest algorithm. The data to be digested is supplied as input stream. The mode parameter specifies whether to include the data (mode = DigestedDataStream.IMPLICIT) or not include it (mode = DigestedDataStream.EXPLICIT). The content type of the inherent ContentInfo automatically is set to PKCS#7 Data.
Parameters:
data_is - the data to be digested supplied from an input stream
digestAlgorithm - the message-digest algorithm to be used for creating the digest
mode - either DigestedDataStream.IMPLICIT for including the data, or DigestedDataStream.EXPLICIT for not including it
Throws:
PKCSException - if the supplied paramteres to not satisfy the requirements ot there is no implementation for the requested hash algorithm

DigestedDataStream

public DigestedDataStream(ObjectID contentType,
                          AlgorithmID digestAlgorithm,
                          byte[] digest)
Creates a new DigestedDataStream object without content.

For instance:

 byte[] message = "Test data to be digested".getBytes();
 MessageDigest md = MessageDigest.getInstance("SHA");
 md.update(message);
 byte[] digest = md.digest();
 DigestedDataStream digested_data = new DigestedDataStream(ObjectID.pkcs7_data, AlgorithmID.sha, digest);
 

The content must be supplied by other means.

Parameters:
contentType - the content type of the digested data
digestAlgorithm - the message-digest algorithm (and any associated parameters) used for creating the digest
digest - the already calculated digest value

DigestedDataStream

public DigestedDataStream(InputStream is)
                   throws IOException,
                          PKCSParsingException
Creates a PKCS#7 DigestedData from an InputStream. The given input stream supplies the DER encoding of an already existing PKCS#7 DigestedData object. This constructor shall be used for parsing a implict DigestedData object where the content is included.
To initialize a DigestedDataStream object for parsing an explicit DigestedData message where the raw data is not included, use the DigestedDataStream(InputStream data_is, AlgorithmID hashAlgorithm) constructor, and perform the decoding explicitly by calling the decode method.

A sender shall use the DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm, int mode) constructor for supplying the content to be digested when creating a DigestedDataStream object.

Parameters:
is - the InputStream holding the DER encoded PKCS#7 DigestedData object
Throws:
IOException - if an error occurs when reasding from the stream
PKCSParsingException - if the object can not be parsed

DigestedDataStream

public DigestedDataStream(InputStream data_is,
                          AlgorithmID digestAlgorithm)
                   throws IOException
Creates a new DigestedDataStream from an InputStream holding the content that has been transmitted by other means, and the hash algorithm to be used for digesting.

Do not use this constructor for supplying the content value to be digested. This constructor may be used by the recipient for initializing the digest computation for an already existing explicit DigestedDataStream message where the raw data is not included. The initialization is done by wrapping a digest stream around the supplied raw data stream for the specified hash algorithm. Subsequently the hash value will be updated when reading the stream thereby piping the data through the digest stream. Later, during digest verification the digest computaion is finished and the result is compared with the hash values derived parsed from the encoding.
For an explicit message the actual decoding has to be performed by calling the decode method just after reading the data:

 // initialize for hash computation:
 DigestedDataStream digestedData = new DigestedDataStream(data_is, hashAlgorithm);
 //read the stream thereby updating the hash values:
 InputStream dataIs = digestedData.getInputStream();
 byte[] buf = new byte[1024];
 int r;
 while ((r = dataIs.read(buf)) > 0) {
   // do something useful
 }
 // explicitly perform the decoding
 digestedData.decode(encoded_stream);
 

A sender shall use the DigestedDataStream(InputStream data_is, AlgorithmID digestAlgorithm, int mode) constructor for supplying the content to be digested when creating a DigestedDataStream object.

For decoding an implicit DigestedDataStream message, use the DigestedDataStream(InputStream is) constructor.

Parameters:
data_is - the InputStream supplying the raw data which has been transmitted by other means
hashAlgorithm - the hash algorithm used by for digesting the content data
Throws:
IOException - if there is no implementation for the specified hash algorithm
Method Detail

decode

public void decode(InputStream is)
            throws IOException,
                   PKCSParsingException
Reads and decodes the DigestedDataStream from a DerInputStream. If the supplied InputStream actually is not an instance of DerInputStream, internally a DerInputStream is created before parsing the data.
Specified by:
decode in interface ContentStream
Parameters:
is - the InputStream holding a DER encoded PKCS#7 DigestedData object
Throws:
IOException - if an I/O error occurs during reading from the InputStream
PKCSParsingException - if an error occurs while parsing the object

getContentType

public ObjectID getContentType()
Returns the content type this class implements.
Specified by:
getContentType in interface ContentStream
Returns:
ObjectID.pkcs7_digestedData

getVersion

public int getVersion()
Returns the version syntax number. At this time version 0 is implemented.
Returns:
the version syntax number

getDigestAlgorithm

public AlgorithmID getDigestAlgorithm()
Returns the message-digest algorithm used for computing the digest.
Returns:
the ID of the message-digest algorithm

getDigest

public byte[] getDigest()
Returns the message-digest computed on the content value.
Returns:
the message-digest, as byte array

setDigest

public void setDigest(byte[] digest)
Sets the message-digest value. This method can be used to set a digest value that has been calculated outside this class.
Parameters:
digest - the digest value as byte array

setBlockSize

public void setBlockSize(int blockSize)
Sets the block size for defining the length of each definite primitive encoded octet string component. If the value of blockSize is smaller or equal to zero the whole data is encoded as definite primitive octet string. This method may be used for enforcing block encoding when wrapping the DigestedData into a ContentInfo.
Specified by:
setBlockSize in interface ContentStream
Parameters:
blockSize - for defining the encoding scheme and setting the octet string component length, if positive
See Also:
OCTET_STRING

getBlockSize

public int getBlockSize()
Gets the block size defining the length of each definite primitive encoded octet string component. If the value of blockSize is smaller or equal to zero the whole data is encoded as definite primitive octet string. This method may be used for enforcing block encoding when wrapping the EncryptedData into a ContentInfo.
Specified by:
getBlockSize in interface ContentStream
Returns:
blockSize defining the encoding scheme and setting the octet string component length, if positive
See Also:
OCTET_STRING

getInputStream

public InputStream getInputStream()
Returns an input stream with the raw data.
Returns:
an input stream with the raw data

toASN1Object

public ASN1Object toASN1Object()
                        throws PKCSException
Returns this DigestedDataStream as ASN1Object.
Specified by:
toASN1Object in interface ContentStream
Returns:
this DigestedDataStream as ASN1Object
Throws:
PKCSException - if the ASN1Object could not be created

toASN1Object

protected ASN1Object toASN1Object(int blockSize)
                           throws PKCSException
Returns this DigestedData as ASN1Object where a constructed OCTET STRING is used for encoding the content.
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

encodeCalled

public void encodeCalled(ASN1Object o,
                         int id)
                  throws CodingException
This method implements the EncodeListener interface. During the encoding process the DerCoder calls this method when it actually is time for computing and setting the message digest to be encoded. In particular for the stream-supporting implementation of this DigestedData content type, the message digest cannot be computed before the content stream entirely has been read. Since the content stream actually is read during the encoding procedure, this class has to be notified when the content stream has been read. This is done by means of the EncodeListener utility. The toASN1Object() method of this DigestedDataStream class instantiates an empty OCTET_STRING for the digest field, and registers itself as EncodeListener for this empty OCTET_STRING. Now, during the encoding process, when the content stream entirely has been read, this encodeCalled method is called for performing digest computation. The supplied ASN1Object is the empty OCTET_STRING to be "filled" with the result of the digest-computation-encryption.
Specified by:
encodeCalled in interface EncodeListener
Parameters:
o - an OCTET_STRING for being supplied with the message digest value
id - the id identifying the particular octet string to be processed
Throws:
CodingException - if an error occurs when computing/encrypting the message digest

notifyEOF

public void notifyEOF()
               throws IOException
This method implements the EOFListener interface for performing the final decoding. Since the digest field is located at the end of a DigestedData structure, it only can be accessed after reading the data stream. For that reason, when starting the parsing of an implicit DigestedData message only version, digestAlgorithms, and ContentInfo fields can be parsed before reading the data. Since the data is supplied from an input stream, a iaik.utils.NotifyEOFInputStream is wrapped around this raw data stream for indicating that the parsing procedure is to be notified when the stream actually has been read. At that point, the programm exceuting automatically jumps to the actual notifyEOF method for finishing the decoding by parsing the final digest field.
For any application it is strongly recommended not to explicitly call this method. This method only is qualified as public method since it implements the iaik.utils.EOFListener interface.
Specified by:
notifyEOF in interface EOFListener
Throws:
IOException - if an error occurs while parsing the stream
See Also:
EOFListener, NotifyEOFInputStream

verify

public boolean verify()
               throws PKCSException
Verifies the digest. This method finishes the hash computation on the data and compares it with the value received in the digest field.
Returns:
true if the hash verifies, false otherwise
Throws:
PKCSException - if an error occurs during verification process

writeTo

public void writeTo(OutputStream os)
             throws IOException
Writes this DigestedData DER encoded to the supplied output stream. If for the inherent content structure a positive blocksize has been specified, the content is encoded as indefinite constructed octet string.
Parameters:
os - the output stream to which this DigestedData shall be written
Throws:
IOException - if an IOException occurs while writing to the stream

writeTo

public void writeTo(OutputStream os,
                    int blockSize)
             throws IOException
Writes this object to the supplied output stream where a constructed OCTET STRING is used for encoding the content. If block size has a value > 0, in implicit mode the inherent raw data is encoded as constructed OCTET STRING. In this case the encoding is splitted according to the defined block size:
 0x24 0x80
           0x04 <blocksize> <data>
           0x04 <blocksize> <data>
           0x04 <blocksize> <data>
                ...
 0x00 0x00
 
If the block size is not positive, whole the inherent data is encoded as one single primitive definite octet string:
 0x04 <length> <data>
 
Parameters:
os - the output stream to which this DigestedData shall be written
blockSize - the block size defining the encoding scheme - and specifying the length of each primitive encoded octet string component, if positive
Throws:
IOException - if an error occurs during writing the object

toString

public String toString()
Returns a string giving some information about this DigestedData object.
Overrides:
toString in class Object
Returns:
the string representation

toString

public String toString(boolean detailed)
Returns a string giving some - if requested - detailed information about this DigestedData object.
Specified by:
toString in interface ContentStream
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