iaik.cms
Class SignedDataInOutStream

java.lang.Object
  extended by iaik.cms.SignedDataStream
      extended by iaik.cms.SignedDataInOutStream
All Implemented Interfaces:
EncodeListener, ContentStream, EOFListener, java.util.EventListener

public class SignedDataInOutStream
extends SignedDataStream

SignedDataStream implementation that encodes a SignedData object again during parsing is done. The SignedData object may be modified before finishing the encoding.

When creating a SignedDataInOutStream object an input stream has to specified from which to read the encoded SignedData object, and an output stream to which to write the (maybe modified) SignedData object again:

 // input stream from which to read the already existing encoded SignedData object:
 InputStream is = ...;
 // output stream to which to write the (maybe modified) SignedData object again:
 OutputStream os = ...;
 SignedDataInOutStream signedData = new SignedDataInOutStream(is, os);
 
Additionally an array of digest algorithm ids may be specified to be added to the list of digest algorithms of the already existing SignedData. Additional digest algorithms may be necessary when, for instance, adding a new SignerInfo that uses a digest algorithm that is not used by any of the already included SignerInfos, e.g.:
 // input stream from which to read the already existing encoded SignedData object:
 InputStream is = ...;
 // output stream to which to write the (maybe modified) SignedData object again:
 OutputStream os = ...;
 // we want add a SignerInfo that used SHA-256
 AlgorithmID[] additionalDigestAlgs = { AlgorithmID.sha256 };
 SignedDataInOutStream signedData = new SignedDataInOutStream(is, os, additionalDigestAlgs);
 
It is necessary to specify any additional digest algorithm already when creating the SignedDataInOutStream object because they have to be known for the initial SignedData digestAlgorithms field.

The further proceeding is similar to that of parent class SignedDataStream except for using method write() for finishing the SignedData encoding instead of using method writeTo(OutputStream). Since the output stream is already set when creating the SignedDataInOutStream object, it is not required as parameter for the final write() method, e.g.:

 // input stream from which to read the already existing encoded SignedData object:
 InputStream is = ...;
 // output stream to which to write the (maybe modified) SignedData object again:
 OutputStream os = ...;
 // we want add a SignerInfo that used SHA-256
 AlgorithmID sha256 = (AlgorithmID)AlgorithmID.sha256.clone();
 AlgorithmID[] additionalDigestAlgs = { sha256 };
 SignedDataInOutStream signedData = new SignedDataInOutStream(is, os, additionalDigestAlgs);
 
 // in explicit mode explicitly supply the content for hash computatio
 if (signedData.getMode() == SignedDataStream.EXPLICIT) {
   signed_data.setInputStream(new ByteArrayInputStream(message));
 }
 
 // get an InputStream for reading the signed content
 InputStream data = signed_data.getInputStream();
 OutputStream dataStream = ...;
 Util.copyStream(data, dataStream, new byte[4096]);
 
 // verify the signature(s) of the already included SignerInfo(s)
 SignerInfo[] signerInfos = signedData.getSignerInfos();
 int numberOfSigners = signerInfos.length;
 if (numberOfSigners == 0) {
   String warning = "Warning: Unsigned message (no SignerInfo included)!"
   System.err.println(warning);
   throw new CMSException(warning);  
 } else {
   for (int i=0; i < numberOfSigners; i++) {
     try {
       // verify the signed data using the SignerInfo at index i
       X509Certificate signerCert = signedData.verify(i);
       // if the signature is OK the certificate of the signer is returned
       System.out.println("Signature OK from signer: "+signerCert.getSubjectDN());
     } catch (SignatureException ex) {
       // if the signature is not OK a SignatureException is thrown
       System.err.println("Signature ERROR from signer: "+signedData.getCertificate((signerInfos[i].getSignerIdentifier())).getSubjectDN());
       throw new CMSException(ex.toString());
     }
   }   
 }
 
 // create and add a new SignerInfo using SHA-256 as digest algorithm
 X509Certificate signerCert = ...;
 PrivateKey signerKey = ...;
 SignerInfo signerInfo = new SignerInfo(new IssuerAndSerialNumber(signerCert),
                                        sha256,
                                        signerKey);
    
 // create some signed attributes (the message digest attribute is automatically added)
 Attribute[] attributes = new Attribute[2];
 // content type is data
 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
 attributes[0] = new Attribute(contentType);
 // signing time is now
 SigningTime signingTime = new SigningTime();
 attributes[1] = new Attribute(signingTime);
 // set the attributes
 signerInfo.setSignedAttributes(attributes);
 // add SignerInfo to SignedData 
 signedData.addSignerInfo(signer_info);
 // add the signer certificate
 signedData.addCertificates(new Certificate[] { signerCert });
 
 // finish the SignedData encoding
 SignedData.write(); 
 
Note that in difference to the writeTo method of class SignedDataStream method write already wraps the SignedData into a ContentInfo.

Note that adding/removing/modifying attributes in already included SignerInfos has to be done with care when wanting to encode the SignedData anew. The encoding of attributes / attribute values is kept during parsing to write them unchanged when encoding the SignedData anew. For that reason attribute modifications of already existing SignerInfos may not be included in the newly encoded SignedData. For that reason you should explicitly call any of the Attribute clearEncoded method before changing an Attribute (value).

However, not only the encoding of attributes is kept (and written anew), but also the following components: Certificate(Choices) elements, RevocationInfoChoice elements, and the SignerInfo fields version, signerIdentifier, signedAttrs, signatureAlgorithm and signature, and each unsigned attribute and unsigned attribute value. If you do not want to keep the encoding of these fields (to encode them anew in any case) just call static method keepComponentEncodings with false as parameter:

 SignedDataInOutStream.setKeepComponentEncodings(false);
 

SignedDataInOutStream should be used with an IAIK-JCE version >5.3, at least an IAIK-JCE version >3.16 is required.


Field Summary
 
Fields inherited from class iaik.cms.SignedDataStream
blockSize_, certSet_, contentType_, crls_, encapContentInfo_, EXPLICIT, IMPLICIT, inputStream_, mode_, securityProvider_, signerInfos_, thisObject_, version_
 
Constructor Summary
SignedDataInOutStream(java.io.InputStream dataIs, AlgorithmID[] digestAlgs, SecurityProvider securityProvider)
          Creates a new SignedDataInOutStream from an InputStream holding the content that has been transmitted by other means and an array specifying the hash algorithms to be used for digesting.
SignedDataInOutStream(java.io.InputStream is, java.io.OutputStream out)
          Creates a new SignedDataInOutStream.
SignedDataInOutStream(java.io.InputStream is, java.io.OutputStream out, AlgorithmID[] additionalDigestAlgs)
          Creates a new SignedDataInOutStream.
SignedDataInOutStream(java.io.InputStream is, java.io.OutputStream out, AlgorithmID[] additionalDigestAlgs, SecurityProvider securityProvider)
          Creates a new SignedDataInOutStream.
SignedDataInOutStream(java.io.InputStream is, java.io.OutputStream out, SecurityProvider securityProvider)
          Creates a new SignedDataInOutStream.
 
Method Summary
static void setKeepComponentEncodings(boolean keepEncodings)
          Decides whether to keep the encodings of some certain SignedData/SignerInfo components during parsing to write them unchanged when encoding the SignedData again.
 void setOutputStream(java.io.OutputStream out)
          Sets the output stream to which to encode the parsed SignedData again.
 void setSDSEncodeListener(SDSEncodeListener sdsEncodeListener)
          Sets an SDSEncodeListener for this SignedDataStream, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.
 ASN1Object toASN1Object()
          Returns this SignedData as ASN1Object, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.
protected  ASN1Object toASN1Object(int blockSize)
          Returns this SignedData as ASN1Object where a constructed OCTET STRING is used for encoding the content, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.
 void write()
          Finishes the encoding and writes the certificates, CRLs (if set) and the SignerInfo objects to the stream.
 void writeTo(java.io.OutputStream os)
          BER encodes and writes the SignedData to the supplied output stream, but throws a RuntimeException if an output stream has been already set when creating the SignedDataInOutStream object or by calling method setOutputStream.
 void writeTo(java.io.OutputStream os, int blockSize)
          BER encodes and writes the SignedData to the supplied output stream, but throws a RuntimeException if an output stream has been already set when creating the SignedDataInOutStream object or by calling method setOutputStream.
 
Methods inherited from class iaik.cms.SignedDataStream
addCertificates, addCRLs, addDigestAlgorithm, addSDSEncodeListener, addSignerInfo, clearSignatures, decode, encodeCalled, getAttributeCertificates, getBlockSize, getCertificate, getCertificates, getCertificates, getCertificateSet, getContentType, getCRLs, getDigestAlgorithms, getEncapsulatedContentType, getInputStream, getMessageDigest, getMode, getRevocationInfoChoices, getSecurityProvider, getSignedDigest, getSignerInfo, getSignerInfo, getSignerInfoIndex, getSignerInfos, getVersion, getX509Certificates, notifyEOF, removeMessageDigest, removeSignerInfo, setBlockSize, setCertificates, setCertificateSet, setCRLs, setInputStream, setMessageDigest, setRevocationInfoChoices, setSecurityProvider, setSignerInfos, toString, toString, verify, verify, verify
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

SignedDataInOutStream

public SignedDataInOutStream(java.io.InputStream is,
                             java.io.OutputStream out)
                      throws CMSParsingException,
                             java.io.IOException
Creates a new SignedDataInOutStream.

Parameters:
is - the input stream from which to parse the SignedData
out - the output stream to which to write the encoded SignedData again
Throws:
CMSParsingException - if an error occurs during SignedData parsing
java.io.IOException - if an I/O error occurs during reading/writing

SignedDataInOutStream

public SignedDataInOutStream(java.io.InputStream is,
                             java.io.OutputStream out,
                             AlgorithmID[] additionalDigestAlgs)
                      throws CMSParsingException,
                             java.io.IOException
Creates a new SignedDataInOutStream.

Parameters:
is - the input stream from which to parse the SignedData
out - the output stream to which to write the encoded SignedData again
additionalDigestAlgs - any additional digest algorithm ids to be added to the digestAlgorithms field
Throws:
CMSParsingException - if an error occurs during SignedData parsing
java.io.IOException - if an I/O error occurs during reading/writing

SignedDataInOutStream

public SignedDataInOutStream(java.io.InputStream is,
                             java.io.OutputStream out,
                             SecurityProvider securityProvider)
                      throws CMSParsingException,
                             java.io.IOException
Creates a new SignedDataInOutStream.

Parameters:
is - the input stream from which to parse the SignedData
out - the output stream to which to write the encoded SignedData again
securityProvider - the CMS SecurityProvider to be used for cryptographic operations
Throws:
CMSParsingException - if an error occurs during SignedData parsing
java.io.IOException - if an I/O error occurs during reading/writing

SignedDataInOutStream

public SignedDataInOutStream(java.io.InputStream is,
                             java.io.OutputStream out,
                             AlgorithmID[] additionalDigestAlgs,
                             SecurityProvider securityProvider)
                      throws CMSParsingException,
                             java.io.IOException
Creates a new SignedDataInOutStream.

Parameters:
is - the input stream from which to parse the SignedData
out - the output stream to which to write the encoded SignedData again
additionalDigestAlgs - any additional digest algorithm ids to be added to the digestAlgorithms field
securityProvider - the CMS SecurityProvider to be used for cryptographic operations
Throws:
CMSParsingException - if an error occurs during SignedData parsing
java.io.IOException - if an I/O error occurs during reading/writing

SignedDataInOutStream

public SignedDataInOutStream(java.io.InputStream dataIs,
                             AlgorithmID[] digestAlgs,
                             SecurityProvider securityProvider)
                      throws CMSParsingException,
                             java.io.IOException
Creates a new SignedDataInOutStream from an InputStream 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 SignedDataStream message where the content data is not included (EXPLICIT mode). The actual decoding has to be performed by calling the decode method just after reading the data.
Since the main use case of this SignedDataInOutStream class is to encode the SignedData again when parsing it method setOutputStream maybe used to specify the output stream to which to encode the SignedData again. It is important to call method setOutputStream before calling method decode:

 // the content data provided by other means:
 InputStream dataIs = ...;
 // the digest algorithms
 AlgorithmID[] digestAlgorithms = ...;
 // initialize for digest computation:
 SignedDataInOutStream signedData = new SignedInOutDataStream(dataIs, digestAlgorithms, null);
 //read the stream thereby updating the hash values:
 dataIs = signedData.getInputStream();
 byte[] buf = new byte[4096];
 int r;
 while ((r = dataIs.read(buf)) > 0) {
   // do something useful 
 }
 
 // set the output stream to which to encode the parsed SignedData again:
 OutputStream os = ...;
 signedData.setOutputStream(os);
 
 // the stream from which to read the encoded SignedData
 InputStream signedDataIs = ...;
 // explicitly perform the decoding
 signedData.decode(signedDataIs); 
 
Note that this constructor is only included to provide the functionality of the same constructor of the parent SignedDataStream class. It requires that the digest algorithm(s) for an explicit SignedData message have to be known in advance before actually parsing it. The digestAlgs field may also contain any additional digest algorithm(s) that may be required when, e.g., adding a new SignerInfo.

When not knowing the digest algorithms of the to-be-parsed SignedData message in advance it will be more appropriate to create the SignedDataInOutStream in the accustomed way and subsequently call method setInputStream to provide the content data received by other means:

 // the stream from which to read the encoded SignedData
 InputStream signedDataIs = ...;
 // the output stream to which to encode the parsed SignedData again:
 OutputStream os = ...;
 SignedDataInOutStream signedData = new SignedInOutDataStream(signedDataIs, os);
 
 // in explicit mode explicitly supply the content for hash computation
 if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
   // the content data provided by other means:
   InputStream dataIs = ...;  
   signedData.setInputStream(dataIs);
 }
 //read the stream thereby updating the hash values:
 InputStream dataIs = signedData.getInputStream();
 byte[] buf = new byte[4096];
 int r;
 while ((r = dataIs.read(buf)) > 0) {
   // do something useful 
 }
 .... 
 
 

Parameters:
dataIs - the InputStream supplying the content data which has been transmitted by other means
digestAlgs - the digest algorithms used by the participated signers for digesting the content data and any additional digest algorithms that may be required
securityProvider - the SecurityProvider to be used for this SignedData object; if null the default system-wide installed SecurityProvider is used
Throws:
java.io.IOException - if there is no implementation for the specified hash algorithm
CMSParsingException
Method Detail

setKeepComponentEncodings

public static void setKeepComponentEncodings(boolean keepEncodings)
Decides whether to keep the encodings of some certain SignedData/SignerInfo components during parsing to write them unchanged when encoding the SignedData again.

During parsing the encodings of the following components are kept: Certificate(Choices) elements, RevocationInfoChoice elements, and the SignerInfo fields version, signerIdentifier, signedAttrs, signatureAlgorithm and signature, and each unsigned attribute and unsigned attribute value. If you do not want to keep the encoding of these fields (to encode them anew in any case) just call this method with false as parameter:

 SignedDataInOutStream.setKeepComponentEncodings(false);
 

Parameters:
keepEncodings - whether to keep the encodings of certain components during parsing (default: true)

setOutputStream

public void setOutputStream(java.io.OutputStream out)
Sets the output stream to which to encode the parsed SignedData again.
Since the output stream has to be known before starting the SignedData parsing this method only may be used when having created an SignedDataInOutStream object in explicit mode with the SignedDataInOutStream(InputStream dataIs, AlgorithmID[] digestAlgs, SecurityProvider securityProvider) constructor from the content data and later call method decode to parse the SignedData:
 // the content data provided by other means:
 InputStream dataIs = ...;
 // the digest algorithms
 AlgorithmID[] digestAlgorithms = ...;
 // initialize for digest computation:
 SignedDataInOutStream signedData = new SignedInOutDataStream(dataIs, digestAlgorithms, null);
 //read the stream thereby updating the hash values:
 dataIs = signedData.getInputStream();
 byte[] buf = new byte[4096];
 int r;
 while ((r = dataIs.read(buf)) > 0) {
   // do something useful 
 }
 
 // set the output stream to which to encode the parsed SignedData again:
 OutputStream os = ...;
 signedData.setOutputStream(os);
 
 // the stream from which to read the encoded SignedData
 InputStream signedDataIs = ...;
 // explicitly perform the decoding
 signedData.decode(signedDataIs); 
 
When using this method in any other case an IllegalStateException will be thrown.

Parameters:
out - the output stream to which to write the newly encoded SignedData
Throws:
java.lang.IllegalStateException - if this method is called when SignedData parsing already has occurred

write

public void write()
           throws java.io.IOException
Finishes the encoding and writes the certificates, CRLs (if set) and the SignerInfo objects to the stream.
Since the output stream is already set when creating the SignedDataInOutStream object, it is not required as parameter for the final write() method.

Note that in difference to the writeTo method of class SignedDataStream method write already wraps the SignedData into a ContentInfo.

Throws:
java.io.IOException - if an I/O error occurs while writing to the stream

writeTo

public void writeTo(java.io.OutputStream os)
             throws java.io.IOException
BER encodes and writes the SignedData to the supplied output stream, but throws a RuntimeException if an output stream has been already set when creating the SignedDataInOutStream object or by calling method setOutputStream. In this case method write has to be used for encoding the SignedData (again).

Overrides:
writeTo in class SignedDataStream
Parameters:
os - the output stream to which this SignedDataStream shall be encoded
Throws:
java.io.IOException - if an error occurs when writing to the stream

writeTo

public void writeTo(java.io.OutputStream os,
                    int blockSize)
             throws java.io.IOException
BER encodes and writes the SignedData to the supplied output stream, but throws a RuntimeException if an output stream has been already set when creating the SignedDataInOutStream object or by calling method setOutputStream. In this case method write has to be used for encoding the SignedData (again).

Overrides:
writeTo in class SignedDataStream
Parameters:
os - the output stream to which this SignedData 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:
java.io.IOException - if an error occurs during writing the object

setSDSEncodeListener

public void setSDSEncodeListener(SDSEncodeListener sdsEncodeListener)
                          throws java.security.NoSuchAlgorithmException
Sets an SDSEncodeListener for this SignedDataStream, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.

Overrides:
setSDSEncodeListener in class SignedDataStream
Parameters:
sdsEncodeListener - the SDSEncodeListener to be set
Throws:
if - the SDSEncodeListener announces any not supported digest algorithms to be used
java.security.NoSuchAlgorithmException
See Also:
SDSEncodeListener

toASN1Object

public ASN1Object toASN1Object()
                        throws CMSException
Returns this SignedData as ASN1Object, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.

Specified by:
toASN1Object in interface ContentStream
Overrides:
toASN1Object in class SignedDataStream
Returns:
this SignedData as ASN1Object
Throws:
CMSException - if the ASN1Object could not be created

toASN1Object

protected ASN1Object toASN1Object(int blockSize)
                           throws CMSException
Returns this SignedData as ASN1Object where a constructed OCTET STRING is used for encoding the content, but throws a RuntimeException if an output stream has been set when creating the SignedDataInOutStream object or by calling method setOutputStream.

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:
CMSException - if the ASN1Object could not be created

This Javadoc may contain text parts from text parts from IETF Internet Standard specifications (see copyright note).

IAIK-CMS 6.0, (c) 2002 IAIK, (c) 2003, 2023 SIC