iaik.pkcs.pkcs7
Class SignedAndEnvelopedData

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

public class SignedAndEnvelopedData
extends SignedData
implements Content, iaik.pkcs.pkcs7.DigestProvider

This class represents the non-stream supporting implementation of the PKCS#7 SignedAndEnvelopedData 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 SignedAndEnvelopedData content type is defined as:

signedAndEnvelopedData OBJECT IDENTIFIER ::= { pkcs-7 4 }

which corresponds to the OID string "1.2.840.1.113549.1.7.4".

The PKCS#7 Cryptographic Message Standard specifies the SignedAndEnvelopedData content type for providing a syntax for building digital envelopes whose authenticity and integrity is ensured by digatal signatures. Content of any type may be enveloped for any number of recipients and signed by any number of signers in parallel.

For each recipient, a commonly at random generated content-encryption key is encrypted with the particular recipientīs public key and - together with recipient-specific information - collected into a RecipientInfo value. The content is encrypted with the content-encryption key giving a EncryptedContent value, which - in combination with a recipient-specific encrypted content-encryption key - forms the digital envelope for each particular recipient.

For each signer, a message digest is computed on the content 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 the result is encrypted with the content-encryption key. This double encrypted message digest and some signer-specific information are collected into a SignerInfo value. The digital signature for each signer is the recovered single encrypted message digest.

Finally all created SignerInfo and RecipientInfo values are collected together with the encrypted content for forming a SignedAndEnvelopedData structure.

This class implements the SignedAndEnvelopedData structure resulting from the last step described above. The SignedAndEnvelopedData type is defined as ASN.1 SEQUENCE type containing the following components (see PKCS#7 specification, Version 1.5):

 SignedAndEnvelopedData ::= SEQUENCE {
    version                 Version,
    recipientInfos          RecipientInfos,
    digestAlgorithms        DigestAlgorithmIdentifiers,
    encryptedContentInfo    EncryptedContentInfo,
    certificates 	          [0] IMPLICIT Certificates OPTIONAL,
    crls                    [1] IMPLICIT CertificateRevocationLists OPTIONAL,
    signerInfos             SignerInfos }
 

The digestAlgorithms field contains the object identifiers of the message digest algorithms used by the several signers for digesting the content. 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. The encryptedContentInfo field specifies the content type, the content-encryption algorithm, and holds the encrypted content. The non-empty SignerInfos and RecipientInfos collect per-signer (respectively per-recipient) information for all parciticipated signers (recipients). Each SignerInfo includes the double encrypted message digest in its encrypted_digest field, and each RecipientInfo includes the content-encryption key, encrypted with the particular recipientīs public key.

A recipient, when receiving the SignedAndEnvelopedData message, decrypts the encrypted content-encryption key from the corresponding RecipientInfo with his/her private key for subsequently decrypting the encrypted content using the content-encryption key just recovered. Subsequently the recovered content-encryption key is used for decrypting the double encrypted message digest for each signer, and the result is decrypted with the signerīs public key giving the recovered message digest, which can be compared with an independently computed message digest for verifying the signature.

For more information consult the RSA PKCS#7 specification. Note, that PKCS#7 Vesrsion 1.6 removes support for extended certificates, which already is considered by this implementation.

It is recommendet to use a sequential combination of the signed-data and the enveloped-data content types instead of using the signed-and-enveloped-data content type, since the signed-and-enveloped-data content type does not have authenticated or unauthenticated attributes, and does not provide enveloping of signer inforamtion other than the signature.


Assuming that signer_certs and recipient_certs represent the certifcate chains of some single signer and some intended recipient, a typical proceeding may perform the following steps for building a SignedAndEnvelopedData object to be sent to the recipient:

 //the data to be signed and enveloped supplied as byte array:
 byte[] data = ...;
 //create a SignedAndEnvelopedData object thereby supplying the data
 //and the content-encryption algorithm to use:
 SignedAndEnvelopedData saed = new new SignedAndEnvelopedData(data, AlgorithmID.des_EDE3_CBC);
 //supply the signerīs certificate chain (generally, there may be more than only
 //one signer):
 saed.setCertificates(signer_certs);
 //create a SignerInfo structure for the signer entity thereby
 //specifying the signerīs certificate by an IssuerAndSerialNumber
 //structure, the signerīs digest algorithm, and the signerīs private key;
 //the IssuerAndSerialNumber can be created from signer_certs[0], which constitutes
 //the signerīs certificate:
 IssuerAndSerialNumber issuer_and_serialNr = new IssuerAndSerialNumber(signer_certs[0]);
 SignerInfo signer_info = new SignerInfo(issuer_and_serialNr, AlgorithmID.sha, privateKey);
 //add the SignerInfo just created to the SignedAndEnvelopedData object:
 saed.addSignerInfo(signer_info);
 //create a RecipientInfo for the intended recipient thereby specifiying
 //its certificate (for obtaining the IssuerAndSerialNumber) and the key
 //encryption algorithm to be used (at this time only the rsaEncryption method
 //is supported):
 RecipientInfo recipient = new RecipientInfo(recipient_certs[0], AlgorithmID.rsaEncryption);
 //add the recipient to the SignedAndEnvelopedData object:
 saed.addRecipientInfo(recipient);
 //prepare the SignedAndEnvelopedData structure for transmission by transforming it to an
 //ASN1Object or immediately DER encoding it:
 ASN1Object obj = saed.toASN1Object();
 //respectively byte[] encoding = saed.getEncoded();
 
When encoding the SignedAndEnvelopedData you optionally may force block-encoding by using the corresponding writeTo method of the SignedData(Stream) super class with a positive block size value. In this case, the encrypted 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 <blocksize> <first encrypted content block>
           0x04 <blocksize> <second encrypted content block>
           0x04 <blocksize> <third encrypted content block>
              ...
 0x00 0x00
 
instead of:
 0x04 <length> <encrypted content>
 
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).

Each recipient decrypts the content-encryption key with his/her private key and subsequently decrypts the encrypted content and the double-encrypted message digest for each signer with the recovered content-encryption key. The signature is verifiied by decrypting the (now single-) encrypted message digest with the signerīs public key for comparing it with a independently computet message digest, e.g.:

 //if the SignedAndEnvelopedData is supplied as encoding, first decode it to an
 //ASN1Object:
 ASN1Object obj = DerCoder.decode(encoding);
 //create a SignedAndEnvelopedData from the received ASN1 object:
 SignedAndEnvelopedData saed =
                          new SignedAndEnvelopedData(obj);
 //setup the cipher for decryption with the recipientīs private key; perform the
 //encrypted-content decryption
 int recipientInfoIndex = 0;
 saed.setupCipher(privateKey, recipientInfoIndex);
 //get the recovered data:
 byte[] content = saed.getContent();
 //verify the signatures of all participated signers:
 SignerInfo[] signer_infos = saed.getSignerInfos();
 for (int i=0; i<signer_infos.length; i++) {
     try {
       // verify the signed data using the SignerInfo at index i
       X509Certificate signer_cert = saed.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_and_enveloped_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN());
     }
 }
 
PKCS#7v1.6 Support:
===================

This class also may be used for creating/parsing SignedAndEnvelopedData objects of version 1.6. Main differences between v1.5 and v1.6 are the hash calculation and content encryption (v1.6 calcualtes the has resoectively encrypts 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 SignedAndEnvelopedData object you only have to set the version number (2 for indicating PKCS#7v1.6) when creating a SignedAndEnvelopedData object:
 byte[] content = ...;
 AlgorithmID contentEA = ...;
 // version number 2 indicates PKCS#7v1.6
 int version = 2;
 SignedAndEnvelopedData sae = new SignedAndEnvelopedData(content, contentEA, version);
 ...
 
On the receiving end you can use this class in the same way as described above for parsing and decrypting the content and verifying the signature(s) of a PKCS#7v1.5 or v1.6 SignedAndEnvelopedData object. Since PKCS#7v1.6 is unsuitable for stream handling, the stream based class SignedAndEnvelopedDataStream does not support PKCS#7v1.6.

Version:
File Revision 30
See Also:
SignerInfo, RecipientInfo, IssuerAndSerialNumber, SignedData, EnvelopedData

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 SignedAndEnvelopedData()
          Default constructor for dynamic object creation in ContentInfo.
  SignedAndEnvelopedData(ASN1Object obj)
          Creates a PKCS#7 SignedAndEnvelopedData from an ASN1Object.
  SignedAndEnvelopedData(byte[] content, AlgorithmID contentEA)
          Creates a new PKCS#7 SignedAndEnvelopedData object where the content is supplied as byte array.
  SignedAndEnvelopedData(byte[] content, AlgorithmID contentEA, int version)
          Creates a new PKCS#7 SignedAndEnvelopedData object where the content is supplied as byte array.
  SignedAndEnvelopedData(InputStream is)
          Creates a new SignedAndEnvelopedData where the DER encoded data is read from the given InputStream.
 
Method Summary
 void addRecipientInfo(RecipientInfo recipient)
          Adds one recipient to the list of recipient infos.
 void decode(ASN1Object obj)
          Decodes the SignedAndEnvelopedData supplied as ASN1Object.
 void decode(InputStream is)
          Reads and decodes the SignedAndEnvelopedData from a DerInputStream.
 byte[] getContent()
          Returns the content as byte array.
 ObjectID getContentType()
          Returns the content type this class implements.
 EncryptedContentInfo getEncryptedContentInfo()
          Returns the encrypted content info included in this SignedAndEnvelopedData object.
 InputStream getInputStream()
          Returns an InputStream for reading the content.
 byte[] getMessageDigest(AlgorithmID digestAlgorithm)
          Returns the message digest calculated for a specific algorithm.
 RecipientInfo[] getRecipientInfos()
          Returns all the recipient infos included in this SignedAndEnvelopedData object.
 void setRecipientInfos(RecipientInfo[] recipients)
          Sets the recipient infos.
 void setupCipher(PrivateKey recipientPrivateKey, int recipientInfoIndex)
          Uses the specified private key to setup the Cipher for decrypting the content-encryption key and subsequently using it to decrypt the encrypted content of this EnvelopedData object for the requesting recipient, specified by its recipientInfoIndex.
protected  ASN1Object toASN1Object(int blockSize)
          Returns this SignedAndEnvelopedData as ASN1Object where a constructed OCTET STRING is used for encoding the content.
 String toString()
          Returns a string giving some information about this SignedAndEnvelopedData object.
 String toString(boolean detailed)
          Returns a string giving some - if requested - detailed information about this SignedAndEnvelopedData object.
 void verify(PublicKey publicKey, int signerInfoIndex)
          Uses the provided public key for verifying the signature that has been created by the signerInfoIndexīth signer.
 
Methods inherited from class iaik.pkcs.pkcs7.SignedData
addSignerInfo, getEncoded, setInputStream, setupMessageDigests, setVersion
 
Methods inherited from class iaik.pkcs.pkcs7.SignedDataStream
getBlockSize, getCertificate, getCertificates, getCRLs, getDigestAlgorithms, getMode, getSignedDigest, getSignerInfos, getVersion, notifyEOF, setBlockSize, setCertificates, setCRLs, setMessageDigest, setSignerInfos, toASN1Object, 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, setBlockSize, toASN1Object
 

Constructor Detail

SignedAndEnvelopedData

protected SignedAndEnvelopedData()
Default constructor for dynamic object creation in ContentInfo. An application shall not use this constructor for creating a SignedAndEnvelopedData object. The block size is set to -1 meaning that the data will be encoded a one simple octet string by deafult.

SignedAndEnvelopedData

public SignedAndEnvelopedData(byte[] content,
                              AlgorithmID contentEA)
                       throws NoSuchAlgorithmException
Creates a new PKCS#7 SignedAndEnvelopedData object where the content is supplied as byte array. The content type is set to PKCS#7 Data.
Parameters:
content - the raw data to sign and envelope
contentEA - the content encryption algorithm for encrypting the content
Throws:
NoSuchAlgorithmException - if there is no implementation for the specified algorithm

SignedAndEnvelopedData

public SignedAndEnvelopedData(byte[] content,
                              AlgorithmID contentEA,
                              int version)
                       throws NoSuchAlgorithmException
Creates a new PKCS#7 SignedAndEnvelopedData object where the content is supplied as byte array. The content type is set to PKCS#7 Data.

This constructor may be used for creating an v1.6 SignedAndEnvelopedData message, where the version number (2) has to be supplied for indicating to calculate the hash over and encrypt the (content) encoding.

Parameters:
content - the raw data to sign and envelope
contentEA - the content encryption algorithm for encrypting the content
version - either 1 (indicating a PKCS#7v1.5 SignedAndEnvelopedData) or 2 (indicating a PKCS#71.6 SignedAndEnvelopedData); default is 1.
Throws:
NoSuchAlgorithmException - if there is no implementation for the specified algorithm

SignedAndEnvelopedData

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

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

Parameters:
obj - the PKCS#7 SignedAndEnvelopedData as ASN1Object
Throws:
PKCSParsingException - if the object can not be parsed

SignedAndEnvelopedData

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

decode

public void decode(ASN1Object obj)
            throws PKCSParsingException
Decodes the SignedAndEnvelopedData supplied as ASN1Object.
Specified by:
decode in interface Content
Overrides:
decode in class SignedData
Parameters:
obj - the PKCS#7 SignedAndEnvelopedData 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 SignedAndEnvelopedData 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
Overrides:
decode in class SignedData
Parameters:
is - the InputStream holding a DER encoded PKCS#7 SignedAndEnvelopedData object
Throws:
IOException - if an I/O error occurs during reading from the InputStream
PKCSParsingException - if an error occurs while parsing the object

setupCipher

public void setupCipher(PrivateKey recipientPrivateKey,
                        int recipientInfoIndex)
                 throws PKCSException,
                        NoSuchAlgorithmException,
                        InvalidKeyException
Uses the specified private key to setup the Cipher for decrypting the content-encryption key and subsequently using it to decrypt the encrypted content of this EnvelopedData object for the requesting recipient, specified by its recipientInfoIndex.

This method first uses the given private key for decrypting the encrypted temporary symmetric key obtained from the corresponding RecipientInfo structure, and subsequently uses this key for decrypting the encrypted content.

Unlike the stream supporting SignedAndEnvelopedDataStream class where the setupCipher method only initializes the cipher for decryption, whole the encrypted-content decryption already is performed inside the setupCipher method of this class.

Parameters:
recipientPrivateKey - the private key of one recipient specified in a RecipientInfo object
recipientInfoIndex - specifies which RecipientInfo the private key belongs to
Throws:
PKCSException - if there occurs an error while decrypting the data
NoSuchAlgorithmException - if there is no implementation of the content-encryption algorithm
InvalidKeyException - if the specified private key is not valid

getContentType

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

setRecipientInfos

public void setRecipientInfos(RecipientInfo[] recipients)
Sets the recipient infos.

Each single RecipientInfo specifies the particular recipientīs certificate by IssuerAndSerialNumber, and the key encryption algorithm to be used; currently only PKCS#1 rsaEncryption is supported.

Example:

 RecipientInfo[] recipients = new RecipientInfo[2];
 recipients[0] = new RecipientInfo(cert1, AlgorithmID.rsaEncryption);
 recipients[1] = new RecipientInfo(cert2, AlgorithmID.rsaEncryption);
 enveloped_data.setRecipientInfos(recipients);
 
Parameters:
recipients - a collection of per-recipient information
See Also:
RecipientInfo

addRecipientInfo

public void addRecipientInfo(RecipientInfo recipient)
Adds one recipient to the list of recipient infos.

The RecipientInfo specifies the particular recipientīs certificate by IssuerAndSerialNumber, and the key encryption algorithm to be used; currently only PKCS#1 rsaEncryption is supported.

Example:

 RecipientInfo recipient = new RecipientInfo(cert1, AlgorithmID.rsaEncryption);
 enveloped_data.addRecipientInfo(recipient);
 
Parameters:
recipient - the RecipientInfo to be added

verify

public void verify(PublicKey publicKey,
                   int signerInfoIndex)
            throws SignatureException
Uses the provided public key for verifying the signature that has been created by the signerInfoIndexīth signer.
Overrides:
verify in class SignedDataStream
Parameters:
publicKey - the public key of the signer to verify the message
signerInfoIndex - the index into the SignerInfos array for identifying the SignerInfo belonging to the signer whose signature has to be verified
Throws:
SignatureException - if the signature turns out to be incorrect

getRecipientInfos

public RecipientInfo[] getRecipientInfos()
Returns all the recipient infos included in this SignedAndEnvelopedData object.
Returns:
an array containing all the RecipientInfo objects included in this SignedAndEnvelopedData
See Also:
setRecipientInfos(iaik.pkcs.pkcs7.RecipientInfo[])

getInputStream

public InputStream getInputStream()
Returns an InputStream for reading the content.

The returned content depends on whether creating a new SignedAndEnvelopedData or parsing an existing one:

Overrides:
getInputStream in class SignedData
Returns:
an InputStream holding the content

getContent

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

The returned content depends on whether creating a new SignedEnvelopedData or parsing an existing one:

Overrides:
getContent in class SignedData
Returns:
a byte array holding the content

getEncryptedContentInfo

public EncryptedContentInfo getEncryptedContentInfo()
Returns the encrypted content info included in this SignedAndEnvelopedData object.
Returns:
the encrypted content info

toASN1Object

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

toString

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

toString

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

getMessageDigest

public byte[] getMessageDigest(AlgorithmID digestAlgorithm)
                        throws NoSuchAlgorithmException
Returns the message digest calculated for a specific algorithm.
Returns:
the message digest calculated while writing the content
Throws:
NoSuchAlgorithmException - if there is no message digest for the specified algorithm

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