iaik.smime
Class SMimeEncrypted

java.lang.Object
  extended by iaik.cms.EnvelopedDataStream
      extended by iaik.smime.SMimeEncrypted
All Implemented Interfaces:
ContentStream, EOFListener, java.util.EventListener

public class SMimeEncrypted
extends EnvelopedDataStream

This class represents the "CMS EnvelopedData object carrying" part of a S/MIME message.

This class extends the EnvelopedDataStream class of the iaik.cms package and may be used for creating and parsing CMS (RFC 5652) EnvelopedData objects that are (to be) wrapped into a ContentInfo structure.

The steps for creating a SMimeEncrypted object and writing it to a stream may be summarized as follows (note that in this example we use AES as content encryption algorithm and RSA key transport for encrypting the content-encryption key; however, an application may wish to use any other content encryption algorithm supported by a cryptographic provider installed, and any other key management technique allowed (e.g. ESDH key agreement):

  1. Create a new SMimeEncrypted by specifying the data to be enveloped, the content encryption algorithm, and - if required - the key lengh, e.g.:
          InputStream data_stream = ...;
          AlgorithmID contentEA = (AlgorithmID)AlgorithmID.aes256_CBC.clone();
          int keyLength = 256;
          SMimeEncrypted sme = new SMimeEncryoted(data_stream, contentEA, keyLength);
          
    This step also will create the symmetric content encryption key.
  2. Add any intended recipient by calling the addRecipient method thereby specifying the particular recipient certificate and the key encryption algorithm to be used for encrypting the symmetric content encryption key with the recipient public key obtained from the supplied certificate, e.g.:
          // the certificate of the recipient
          X509Certificate recipientCert = ...;
          // the sender wants to be able to decrypt the message, too
          X509Certificate senderCert = ...;
          sme.addRecipient(recipientCert, AlgorithmID.rsaEncryption);
          sme.addRecipient(senderCert, AlgorithmID.rsaEncryption);
          
  3. Finally call the writeTo method for wrapping the EnvelopedData object into a ContentInfo and writing it BER encoded to an output stream thereby actually performing the content encryption by piping the data through a cipher stream:
          OutputStrem os = ...;
          sme.writeTo(os);
          
On the receiving side, the recipient first creates a new SMimeEncrypted object from an input stream supplying the BER encoded message, subsequently decrypts the encrypted symmetric content encryption key with her/his (private) key, and finally gets and reads the data holding input stream which is decrypted during the read operation, e.g.:
 InputStream encryptedStream = ...;
 SMimeEncrypted sme = new SMimeEncrypted(encryptedStream);
 //recipient has index 0
 sme.decryptSymmetricKey(privateKey, 0);
 //get and read the data
 InputStream data_stream = sme.getInputStream();
 byte[] buf = new byte[2048];
 int r;
 while ((r = data_is.read(buf)) > 0) {
   // do something useful
 }
 
The decryptSymmetricKey method used in the sample above requires that the recipient knows the index of the RecipientInfo that belongs to her/his key. Alternatively you may use the recipient certificate or her/his KeyIdentifier to find the right RecipientInfo and decrypt the symmetric content encryption key.

See Also:
EnvelopedDataStream, RecipientInfo, EncryptedContent

Field Summary
 
Fields inherited from class iaik.cms.EnvelopedDataStream
blockSize_, encryptedContentInfo_, EXPLICIT, IMPLICIT, keyChanged_, originatorInfo_, recipientInfos_, securityProvider_, symmetricKey_, unprotectedAttrs_, version_
 
Constructor Summary
SMimeEncrypted(java.io.InputStream is)
          Creates a new SMimeEncrypted object where the BER encoded message is read from the given input stream.
SMimeEncrypted(java.io.InputStream is, AlgorithmID contentEA, int keyLength)
          Creates a new SMimeEncrypted object where the data to be enveloped is read from the given InputStream.
 
Method Summary
 java.security.KeyPair addRecipient(X509Certificate[] originatorCertificates, java.security.PrivateKey originatorPrivateKey, X509Certificate recipientCertificate, AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength)
          Adds one recipient to this S/MIME EnvelopedData object.
 void addRecipient(X509Certificate recipientCertificate, AlgorithmID keyEA)
          Adds one recipient to this S/MIME EnvelopedData object.
 java.security.KeyPair addRecipient(X509Certificate recipientCertificate, AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength)
          Adds one recipient to this S/MIME EnvelopedData object.
 javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey, int recipientInfoIndex)
          Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by its recipientInfoIndex.
 javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey, KeyIdentifier recipientIdentifier)
          Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by the given recipient identifier.
 javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey, X509Certificate recipientCertificate)
          Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by the given recipient certificate.
 AlgorithmID getEncryptionAlgorithm()
          Returns the content-encryption algorithm (including any associated parameters) of this SMimeEncrypted object.
 int getRecipientInfoIndex(X509Certificate recipientCertificate)
          Returns the recipient info index matching to the supplied recipient certificate.
 ASN1Object toASN1Object(int blockSize)
          Returns the CMS EnvelopedData object as an ASN1Object wrapped into a ContentInfo.
 void writeTo(java.io.OutputStream os)
          Writes this SMimeEncrypted object to the supplied output stream.
 void writeTo(java.io.OutputStream os, int blockSize)
          Writes this SMimeEncrypted object to the supplied output stream.
 
Methods inherited from class iaik.cms.EnvelopedDataStream
addRecipientInfo, decode, getBlockSize, getContentType, getEncryptedContentInfo, getInputStream, getMode, getOriginatorInfo, getRecipientInfo, getRecipientInfo, getRecipientInfos, getRecipientInfos, getSecurityProvider, getUnprotectedAttribute, getUnprotectedAttributes, getVersion, notifyEOF, setBlockSize, setInputStream, setMode, setOriginatorInfo, setRecipientInfos, setSecurityProvider, setUnprotectedAttributes, setupCipher, setupCipher, setupCipher, setupCipher, toASN1Object, toString, toString
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

SMimeEncrypted

public SMimeEncrypted(java.io.InputStream is,
                      AlgorithmID contentEA,
                      int keyLength)
               throws java.security.NoSuchAlgorithmException
Creates a new SMimeEncrypted object where the data to be enveloped is read from the given InputStream.

This constructor automatically will create the symmetric content encryption key for the specified algorithm.

Parameters:
is - an InputStream holding the data to be enveloped
contentEA - the content encryption algorithm to be used
keyLength - the length of the key; if -1 the default key length for the specified algorithm will be used
Throws:
java.security.NoSuchAlgorithmException - if there is no implementation for the specified algorithm

SMimeEncrypted

public SMimeEncrypted(java.io.InputStream is)
               throws java.io.IOException
Creates a new SMimeEncrypted object where the BER encoded message is read from the given input stream.

Do not use this constructor for supplying the content data to be enveloped. This constructor may be used by the recipient for parsing an already existing enveloped message, supplied as BER encoding from an input stream, that may have been created by means of the writeTo method.

Use the SMimeEncrypted(InputStream is, AlgorithmID contentEA, int keyLength) constructor for supplying the content data to be enveloped when creating a SMimeEncrypted object.

Parameters:
is - the input stream where the BER encoded message shall be read from
Throws:
java.io.IOException - if an I/O error occurs
Method Detail

getRecipientInfoIndex

public int getRecipientInfoIndex(X509Certificate recipientCertificate)
Returns the recipient info index matching to the supplied recipient certificate. This method may be used by a recipient for quering for the recipient info that holds the content encryption key encrypted with the public key of the given certificate.

Parameters:
recipientCertificate - the certificate of the recipient
Returns:
the recipient info index matching to the supplied recipient certificate, or -1 if no recipient info belonging to the given certificate can be found

decryptSymmetricKey

public javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey,
                                                  int recipientInfoIndex)
                                           throws SMimeException,
                                                  java.security.InvalidKeyException
Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by its recipientInfoIndex.

This method first uses the given key for decrypting the encrypted temporary symmetric key obtained from the corresponding RecipientInfo structure, and subsequently uses this key to initialize a CipherInputStream for decrypting the inherent encrypted content.

Note that the cipher will be only initialized for decrypting in this class. The encrypted-content decryption actually is done during reading the data obtained by calling the getInputStream method. So do not call getInputStream before decrypting the encrypted content-encryption key!

Note that you have to know the right index into the recipientInfos field when using this method for decrypting the encrypted content-encryption key and setting up the cipher for decryption. You may search for the index by using the getRecipientInfoIndex method.
However, when having some recipient using a key agreement protocol the corresponding RecipientInfo is of type KeyAgreeRecipientInfo which may hold encrypted content-encryption keys for more than only one recipients using the same key agreement algorithm with same domain parameters. Since this decryptSymmetricKey method only can get the KeyAgreeRecipientInfo with the given index (but not search for the right recipient in the KeyAgreeRecipientInfo), it will step through any recipient included in the KeyAgreeRecipientInfo trying to decrypt the encrypted content-encryption key with the supplied key. This may give some overhead so it might be appropriate to use another decryptSymmetricKey method allowing to immediately identify the particular recipient in mind by its keyIdentifier or certificate.

Parameters:
recipientKey - the key of the recipient to be used for decrypting the encrypted content-encryption key.
recipientInfoIndex - the index into the recipientInfos field
Returns:
the (decrypted) secret content encryption key
Throws:
SMIMEException - if there occurs an error while decrypting the content-encryption key or setting up the cipher for decrypting the content
java.security.InvalidKeyException - if the specified recipient key is not valid
SMimeException

decryptSymmetricKey

public javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey,
                                                  KeyIdentifier recipientIdentifier)
                                           throws SMimeException,
                                                  java.security.InvalidKeyException
Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by the given recipient identifier.

This method first uses the given key for decrypting the encrypted temporary symmetric key obtained from the corresponding RecipientInfo structure, and subsequently uses this key to initialize a CipherInputStream for decrypting the inherent encrypted content.

Note that the cipher will be only initialized for decrypting in this class. The encrypted-content decryption actually is done during reading the data obtained by calling the getInputStream method. So do not call getInputStream before decrypting the encrypted content-encryption key!

This decryptSymmetricKey method can be used to decrypt the encrypted content-encryption key and setup the cipher for decryption for any type of RecipientInfo. The supplied recipient identifier will be used for searching for the right RecipientInfo in the recipientInfos field.

Parameters:
recipientKey - the key of the recipient to be used for decrypting the encrypted content-encryption key.
recipientIdentifier - specifies which RecipientInfo the given key belongs to
Returns:
the (decrypted) secret content encryption key
Throws:
SMIMEException - if there occurs an error while decrypting the content-encryption key or setting up the cipher for decrypting the content, or no RecipientInfo for the requested recipient is included
java.security.InvalidKeyException - if the specified recipient key is not valid
SMimeException

decryptSymmetricKey

public javax.crypto.SecretKey decryptSymmetricKey(java.security.Key recipientKey,
                                                  X509Certificate recipientCertificate)
                                           throws SMimeException,
                                                  java.security.InvalidKeyException
Uses the specified key for decrypting the content-encryption key to setup the cipher for decrypting the encrypted content of this SMimeEncrypted object for the requesting recipient, specified by the given recipient certificate.

This method first uses the given key for decrypting the encrypted temporary symmetric key obtained from the corresponding RecipientInfo structure, and subsequently uses this key to initialize a CipherInputStream for decrypting the inherent encrypted content.

Note that the cipher will be only initialized for decrypting in this class. The encrypted-content decryption actually is done during reading the data obtained by calling the getInputStream method. So do not call getInputStream before decrypting the encrypted content-encryption key!

Note that this method only can be used for decrypting the encrypted content encyrption key and setting up the cipher for content decryption if the recipient in mind has a RecipientInfo of type KeyTransRecipientInfo or KeyAgreeRecipientInfo using a public key from a certificate for its key management protocol. However, this should be no problem since S/MIME generally only uses certificate based RecipientInfos.

Parameters:
recipientKey - the key of the recipient to be used for decrypting the encrypted content-encryption key.
recipientCertificate - the certificate of the recipient specifying which RecipientInfo the recipient private key belongs to
Returns:
the (decrypted) secret content encryption key
Throws:
SMimeException - if there occurs an error while decrypting the content-encryption key or setting up the cipher for decrypting the content, or no RecipientInfo for the requested recipient is included
java.security.InvalidKeyException - if the specified recipient key is not valid

addRecipient

public void addRecipient(X509Certificate recipientCertificate,
                         AlgorithmID keyEA)
Adds one recipient to this S/MIME EnvelopedData object.

When using this method for adding a Recipient, the corresponding RecipientInfo will be the KeyTransRecipientInfo choice and the recipient certificate will be identified by IssuerAndSerialNumber. So use this method with rsaEncyrption as key transport algorithm to be compatible to S/MIMEv2.

Parameters:
recipientCertificate - the certificate of the recipient
keyEA - the algorithm to use for encrypting the symmetric key (e.g. AlgorithmID.rsaEncryption)

addRecipient

public java.security.KeyPair addRecipient(X509Certificate recipientCertificate,
                                          AlgorithmID keyEA,
                                          AlgorithmID keyWrapAlg,
                                          int kekLength)
                                   throws SMimeException
Adds one recipient to this S/MIME EnvelopedData object.

When using this method for adding a Recipient, the corresponding RecipientInfo will be the KeyAgreeRecipientInfo choice and the recipient certificate will be identified by IssuerAndSerialNumber. The KeyAgreeRecipientInfo originator field will be the OriginatorPublicKey choice for using an ephemeral originator key.

This method may be called repeatedly for adding information for each recipient using key agreement as key management protocol. When calling this method the first time (for adding the first "KeyAgree" recipient) and the originator key yet has not been set, this method itself creates a OriginatorPublicKey with domain parameters matching to those of the supplied recipient key. Any further call of this method might add a recipient to an already existing KeyAgreeRecipientInfo (if the recipient to be added has a public key with domain parameters matching to those of an already existing KeyAgreeRecipientInfo originator key) or create a new KeyAgreeRecipientInfo for this recipient.

Parameters:
recipientCertificate - the certificate of the recipient
keyEA - the (key agreement) algorithm to use for creating a shared secret key encryption key for encrypting the symmetric key (e.g. AlgorithmID.esdhKeyAgreement)
keyWrapAlg - the key wrap algorithm to be used for encrypting (wrapping) the content-encryption key with the shared key-encryption created according to the requested key agreement protocol
kekLength - the length of the shared key encryption key to be generated
Returns:
the originator key pair matching to the domain parameters of the recipient key
Throws:
SMimeException - if it was not possible to create a RecipientInfo or encrypt the symmetric key for this recipient

addRecipient

public java.security.KeyPair addRecipient(X509Certificate[] originatorCertificates,
                                          java.security.PrivateKey originatorPrivateKey,
                                          X509Certificate recipientCertificate,
                                          AlgorithmID keyEA,
                                          AlgorithmID keyWrapAlg,
                                          int kekLength)
                                   throws SMimeException
Adds one recipient to this S/MIME EnvelopedData object.

When using this method for adding a Recipient, the corresponding RecipientInfo will be the KeyAgreeRecipientInfo choice and the recipient certificate will be identified by IssuerAndSerialNumber. The KeyAgreeRecipientInfo originator field will be the OriginatorPublicKey choice for using an ephemeral originator key.

This method may be called repeatedly for adding information for each recipient using key agreement as key management protocol. When calling this method the first time (for adding the first "KeyAgree" recipient) and the originator key yet has not been set, this method itself creates a OriginatorPublicKey with domain parameters matching to those of the supplied recipient key. Any further call of this method might add a recipient to an already existing KeyAgreeRecipientInfo (if the recipient to be added has a public key with domain parameters matching to those of an already existing KeyAgreeRecipientInfo originator key) or create a new KeyAgreeRecipientInfo for this recipient.

Parameters:
recipientCertificate - the certificate of the recipient
keyEA - the (key agreement) algorithm to use for creating a shared secret key encryption key for encrypting the symmetric key (e.g. AlgorithmID.esdhKeyAgreement)
keyWrapAlg - the key wrap algorithm to be used for encrypting (wrapping) the content-encryption key with the shared key-encryption created according to the requested key agreement protocol
kekLength - the length of the shared key encryption key to be generated
Returns:
the originator key pair matching to the domain parameters of the recipient key
Throws:
SMimeException - if it was not possible to create a RecipientInfo or encrypt the symmetric key for this recipient

getEncryptionAlgorithm

public AlgorithmID getEncryptionAlgorithm()
Returns the content-encryption algorithm (including any associated parameters) of this SMimeEncrypted object.

Returns:
the content-encryption AlgorithmID

toASN1Object

public ASN1Object toASN1Object(int blockSize)
                        throws CMSException
Returns the CMS EnvelopedData object as an ASN1Object wrapped into a ContentInfo. Automatically enforces block encoding with a block size of 2048 if a negative blockSize value is supplied.

Overrides:
toASN1Object in class EnvelopedDataStream
Parameters:
blockSize - the block size for using block encoding
Returns:
the S/MIME enveloped message as an ASN1Object
Throws:
CMSException - if an error occurs while building the ASN.1 object

writeTo

public void writeTo(java.io.OutputStream os)
             throws java.io.IOException
Writes this SMimeEncrypted object to the supplied output stream. Automatically enforces block encoding with a block size of 2048.

Overrides:
writeTo in class EnvelopedDataStream
Parameters:
os - the output stream to which this SMimeEncrypted shall be written
Throws:
java.io.IOException - if an I/O error occurs while writing to the stream

writeTo

public void writeTo(java.io.OutputStream os,
                    int blockSize)
             throws java.io.IOException
Writes this SMimeEncrypted object to the supplied output stream. The blockSize parameter indicates the block size to be used for performing block encoding.

Overrides:
writeTo in class EnvelopedDataStream
Parameters:
os - the output stream to which this SMimeEncrypted shall be written
blockSize - the block size for using block encoding
Throws:
java.io.IOException - if an I/O error occurs while writing to the stream

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