public class EnvelopedDataStream extends java.lang.Object implements ContentStream
EnvelopedData.
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 EnvelopedData content type is
defined as:
envelopedData OBJECT IDENTIFIER ::= { pkcs-7 3 }
which corresponds to the OID string "1.2.840.1.113549.1.7.3".
The PKCS #7: Cryptographic Message
Syntax Version 1.5 (RFC 2315) specifies the EnvelopedData
content type for providing a syntax for building digital envelopes. Content
of any type may be enveloped for any number of recipients 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. All RecipientInfo
values are collected together with the encrypted content into an
EnvelopedData value to be sent to each intended recipient.
This class implements the EnvelopedData structure resulting from
the last step described above. The EnvelopedData type is defined
as ASN.1 SEQUENCE type containing the following components (see PKCS #7: Cryptographic Message Syntax Version 1.5
(RFC 2315)):
EnvelopedData ::= SEQUENCE {
version Version,
recipientInfos RecipientInfos,
encryptedContentInfo EncryptedContentInfo }
RecipientInfos ::= SET OF RecipientInfo
EncryptedContentInfo ::= SEQUENCE {
contentType ContentType,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
EncryptedContent ::= OCTET STRING
The recipientInfos field is a non-empty collection of
per-recipient information. The encryptedContentInfo field
specifies the type of the content being enveloped, the content-encryption
algorithm (the same for all recipients) used for encrypting the content,
and the result of the content encryption. If the encrypted content value
is not present in the encryptedContent field, it has to be
supplied by other means.
A recipient, when receiving the EnvelopedData message,
decrypts the corresponding encrypted content-encryption key with his/her
private key for subsequently decrypting the encrypted content using the
content-encryption key just recovered. The recipient's private key is
referenced by an issuer distinguished name and an issuer-specific serial
number that uniquely identify the certificate for the corresponding public
key.
For more information consult the RSA PKCS #7: Cryptographic Message Syntax Version 1.5 (RFC 2315) specification.
When creating a new EnvelopedDataStream object for the data to be enveloped
the symmetric algorithm has to be specified to be used for content-encryption. After setting
the recipients, the EnvelopedDataStream object may be encoded and written to an output stream
by using a proper writeTo method, e.g.:
EnvelopedDataStream(InputStream is, AlgorithmID contentEA) constructor:
//the data to be enveloped supplied from an input stream:
InputStream dataStream = ...;
//use TripleDES in CBC mode for encrypting the content
EnvelopedDataStream enveloped_data = new EnvelopedDataStream(dataStream, AlgorithmID.des_EDE3_CBC);
RecipientInfo object, and add all RecipientInfos to the EnvelopedDataStream
structure by calling the setRecipientInfos method, e.g.
(assuming to add two recipients with corresponding certificates cert1 and
cert2; currently only the PKCS#1 rsaEncryption is supported as key-
encryption algorithm):
RecipientInfo[] recipients = new RecipientInfo[2];
recipients[0] = new RecipientInfo(cert1, AlgorithmID.rsaEncryption);
recipients[1] = new RecipientInfo(cert2, AlgorithmID.rsaEncryption);
enveloped_data.setRecipientInfos(recipients);
writeTo method for BER encoding the
EnvelopedData object and writing it to an output stream. You optionally may specify
a particular block size for splitting the encoding of encrypted content. This step
also will perform the encryption of the symmetric content-encryption key for each
participated recipient.
int blockSize = ...;
OutputStream encoded_stream = ...;
enveloped_data.writeTo(encoded_stream, blockSize);
respectively
enveloped_data.writeTo(encoded_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
EnvelopedData implementation to handle large amounts of data. When no block size is
specified whole the encrypted content is encoded as primitive definite octet string, which
advantageously may be done when using the non-stream supporting
EnvelopedData implementation.
When a positve block size is specified for encoding the EnvelopedData to a stream,
the encrypted content is BER encoded as indefinite constructed octet string being composed
of a series of definite primitive encoded octet strings of blockSize length:
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 also may be preferable when intending to be
compatible to the encoding practice of some particular application (for instance some
versions of Netscape Navigator).
EnvelopedDataStream(InputStream is)
constructor for parsing the internal structure. Before reading the recovered content by means of the
getInputStream method, the cipher has to be
initialized for decryption with the particular recipient´s private key by calling the
setupCipher method:
EnvelopedDataStream enveloped_data = new EnvelopedDataStream(encoded_stream);
EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
System.out.println("Content type: "+eci.getContentType().getName());
System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
System.out.pritnln("Included RecipientInfos:");
for (int i=0; i < recipients.length; i++) {
System.out.print("Recipient "+(i+1)+":");
System.out.println(recipients[i].getIssuerAndSerialNumber());
}
//setup cipher for recipient 1:
int recipientInfoIndex = 0;
enveloped_data.setupCipher(privateKey, recipientInfoIndex);
Unlike the non-stream supporting EnvelopedData class where the encrypted-content decryption
already is performed inside the setupCipher method, the cipher
will be only initialized for decryption in this class. The encrypted-content
decryption actually is done during reading the data obtained by calling the
getInputStream method. So don´t call
getInputStream before setting up the cipher!
InputStream data_is = enveloped_data.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = data_is.read(buf)) > 0) {
// do something useful
}
RecipientInfo,
EncryptedContentInfoStream| Modifier and Type | Field and Description |
|---|---|
protected int |
block_size
The block size for block oriented stream encoding.
|
protected EncryptedContentInfoStream |
encrypted_content_info
The EncryptedContentInfo for the encrypted content.
|
protected java.util.Vector |
recipient_infos
Repository for the RecipientInfos.
|
protected javax.crypto.SecretKey |
symmetric_key
The secret content encryption key.
|
protected int |
version
The version number; currently 0.
|
| Modifier | Constructor and Description |
|---|---|
protected |
EnvelopedDataStream()
Default constructor for dynamic object creation in ContentInfo.
|
|
EnvelopedDataStream(java.io.InputStream is)
Creates a new EnvelopedDataStream where the DER encoded data
is read from the given InputStream.
|
|
EnvelopedDataStream(java.io.InputStream is,
AlgorithmID contentEA)
Creates a new PKCS#7 EnvelopedDataStream object where the content to be enveloped
is read from the supplied InputStream.
|
|
EnvelopedDataStream(java.io.InputStream is,
AlgorithmID contentEA,
int keyLength)
Creates a new PKCS#7 EnvelopedDataStream object where the content to be enveloped
is read from the supplied InputStream.
|
|
EnvelopedDataStream(RecipientInfo[] recipients,
EncryptedContentInfoStream encryptedCI)
Constructs an EnvelopedDataStream object with an already
created EncryptedContentInfoStream.
|
| Modifier and Type | Method and Description |
|---|---|
void |
addRecipientInfo(RecipientInfo recipient)
Adds one recipient to the list of recipient infos.
|
void |
decode(java.io.InputStream is)
Reads and decodes the EnvelopedData from a DerInputStream.
|
ObjectID |
getContentType()
Returns the content type this class implements.
|
java.lang.Object |
getEncryptedContentInfo()
Returns the encrypted content info included in this
EnvelopedDataStream object. |
java.io.InputStream |
getInputStream()
Returns an InputStream from where the decrypted data can be read.
|
RecipientInfo |
getRecipientInfo(java.security.cert.X509Certificate recipientCertificate)
Returns the recipient info matching to the supplied recipient certificate.
|
RecipientInfo[] |
getRecipientInfos()
Returns all the recipient infos included in this
EnvelopedData object. |
int |
getVersion()
Returns the syntax version number.
|
void |
setBlockSize(int blockSize)
Sets the block size for defining the length of each definite primitive
encoded octet string component.
|
void |
setRecipientInfos(RecipientInfo[] recipients)
Sets the recipient infos.
|
void |
setupCipher(java.security.Key key)
Uses the given symmetric key to setup the cipher for decrypting the content.
|
void |
setupCipher(java.security.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
EnvelopedDataStream object for the requesting recipient, specified by its
recipientInfoIndex. |
ASN1Object |
toASN1Object()
Returns this EnvelopedDataStream as ASN1Object.
|
protected ASN1Object |
toASN1Object(int blockSize)
Returns this EnvelopedData as ASN1Object.
|
java.lang.String |
toString()
Returns a string giving some information about this
EnvelopedData object. |
java.lang.String |
toString(boolean detailed)
Returns a string giving some - if requested - detailed information
about this
EnvelopedData object. |
void |
writeTo(java.io.OutputStream os)
DER encoded this EnvelopedData and writes the encoding to the supplied output stream.
|
void |
writeTo(java.io.OutputStream os,
int blockSize)
Writes the BER encoding of this EnvelopedData to the supplied output stream
where a constructed OCTET STRING is used for encoding the content.
|
protected int version
protected java.util.Vector recipient_infos
protected EncryptedContentInfoStream encrypted_content_info
protected javax.crypto.SecretKey symmetric_key
protected int block_size
protected EnvelopedDataStream()
public EnvelopedDataStream(java.io.InputStream is,
AlgorithmID contentEA)
throws java.security.NoSuchAlgorithmException
When using this constructor, automatically a symmetric key for content
encryption is generated. For that reason this constructor shall not be used
in situations where the desired content encryption algorithm requires a
specific key/parameter handling. In such cases the EnvelopedDataStream(RecipientInfo[], EncryptedContentInfoStream)
constructor shall be used to be supplied with precomputed RecipientInfos
and EncryptedContentInfo. Consult the EncryptedContentInfoStream class documentation for more information about
special key/parameter handling.
is - the InputStream containing the data to envelopecontentEA - the content encryption algorithm for encrypting the contentjava.security.NoSuchAlgorithmException - if there is no implementation for the specified algorithmpublic EnvelopedDataStream(java.io.InputStream is,
AlgorithmID contentEA,
int keyLength)
throws java.security.NoSuchAlgorithmException
When using this constructor, automatically a symmetric key for content
encryption is generated.
If the specified content encryption algorithm supports variable key lengths, a
particular key length may be set by means of the keyLength parameter.
If no length is specified, the defined default key length will be used. If the
algorithm only works with keys of fixed-size length, the keyLength parameter
may be set to -1 or the EnvelopedDataStream(InputStream is, AlgorithmID contentEA) constructor may be used.
This constructor shall not be used in situations where the desired content encryption
algorithm requires a specific parameter handling. In such cases the EnvelopedDataStream(RecipientInfo[], EncryptedContentInfoStream)
constructor shall be used to be supplied with precomputed RcipientInfos
and EncryptedContentInfo. Consult the EncryptedContentInfoStream class documentation for more information about
special key/parameter handling.
is - the InputStream containing the data to envelopecontentEA - the content encryption algorithm for encrypting the contentkeyLength - the key length that may be set when using a content
encryption algorithm that supports variable key lengthsjava.security.NoSuchAlgorithmException - if there is no implementation for the specified algorithmpublic EnvelopedDataStream(RecipientInfo[] recipients, EncryptedContentInfoStream encryptedCI)
RecipientInfo specifies a collection of
per-recipient information, and the given EncryptedContentInfoStream
supplies the already encrypted content.recipients - information about the recipientsencryptedCI - the encrypted content infopublic EnvelopedDataStream(java.io.InputStream is)
throws PKCSParsingException,
java.io.IOException
is - the InputStream holding a DER encoded PKCS#7 EnvelopedData objectjava.io.IOException - if an I/O error occurs during reading from the InputStreamPKCSParsingException - if an error occurs while parsing the objectpublic void decode(java.io.InputStream is)
throws java.io.IOException,
PKCSParsingException
DerInputStream,
internally a DerInputStream is created before parsing the data.decode in interface ContentStreamis - the InputStream holding a DER encoded PKCS#7 EnvelopedData objectjava.io.IOException - if an I/O error occurs during reading from the InputStreamPKCSParsingException - if an error occurs while parsing the objectpublic ObjectID getContentType()
getContentType in interface ContentStreamObjectID.pkcs7_envelopedDatapublic void setRecipientInfos(RecipientInfo[] recipients)
RecipientInfo
specifies the particular recipient´s certificate by IssuerAndAserialNumber, 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);
recipients - a collection of per-recipient informationRecipientInfopublic void addRecipientInfo(RecipientInfo recipient)
The RecipientInfo specifies the particular recipient´s
certificate by IssuerAndAserialNumber, and the
key encryption algorithm to be used; currently only PKCS#1 rsaEncryption is supported.
Example:
RecipientInfo recipient = new RecipientInfo(cert, AlgorithmID.rsaEncryption); enveloped_data.addRecipientInfo(recipient);
recipient - the RecipientInfo to be addedpublic void setBlockSize(int blockSize)
blockSize is smaller or equal to zero the
whole encrypted data is encoded as definite primitive octet string.
This method may be used for enforcing block encoding when wrapping the
EnvelopedData into a ContentInfo.setBlockSize in interface ContentStreamblockSize - for defining the encoding scheme and setting the octet
string component length, if positiveOCTET_STRINGpublic void setupCipher(java.security.PrivateKey recipientPrivateKey,
int recipientInfoIndex)
throws PKCSException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
EnvelopedDataStream 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 to initialize a CipherInputStream for
the inherent encrypted content.
Unlike the non-stream supporting EnvelopedData class where the encrypted-content decryption
already is performed inside the setupCipher method, 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 don´t call
getInputStream before setting up the cipher!
Attention! This method only can be used when the content
encryption AlgorithmID contains IV parameters encoded as an OCTET_STRING. When
the algorithmID contains parameters of other type (e.g. S/MIME RC2-CBC parameters)
they have to be decoded separatly, and the setupCipher(Key key, AlgorithmParameterSpec) method of the EncryptedContentInfoStream
class has to be used to setup the cipher for content decryption, e.g.:
//get the ECI from the enveloped data:
EncryptedContentInfoStream eci =
(EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
// get the recipient infos
RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
// use the specific recipient´s private key for decrypting the required
// symmetric content encryption key, e.g.:
SecretKey secretKey =
recipient_infos[0].decryptKey(recipientPrivateKey)
//get the content encryption algorithm:
AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
// get the parameters as SEQUENCE
SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
// the iv is the second component
OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(1);
// create an IvParameterSpec:
IvParameterSpec ivSpec = new IvParameterSpec(oct.getValue());
//now setup the cipher with previously decrypted recipient key amd params
eci.setupCipher(secretKey, ivSpec);
//get and read the data thereby actually performing the decryption
InputStream data_is = signed_data.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = data_is.read(buf)) > 0) {
// do something useful
}
recipientPrivateKey - the private key of the recipient to be used for decrypting
the encrypted content-encryption key.recipientInfoIndex - specifies which RecipientInfo the private key belongs toPKCSException - if there occurs an error while decrypting the datajava.security.NoSuchAlgorithmException - if there is no implementation of the content-encryption algorithmjava.security.InvalidKeyException - if the specified private key is not validpublic void setupCipher(java.security.Key key)
throws PKCSException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
Unlike the non-stream supporting EnvelopedData class where the encrypted-content decryption
already is performed inside the setupCipher method, 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 don´t call
getInputStream before setting up the cipher!
Attention! This method only can be used when the content
encryption AlgorithmID contains IV parameters encoded as an OCTET_STRING. When
the algorithmID contains parameters of other type (e.g. S/MIME RC2-CBC parameters)
they have to be decoded separatly, and the setupCipher(Key key, AlgorithmParameterSpec) method of the EncryptedContentInfoStream
class has to be used to setup the cipher for content decryption, e.g.:
//get the ECI from the enveloped data:
EncryptedContentInfoStream eci =
(EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
//get the content encryption algorithm:
AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
// get the parameters as SEQUENCE
SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
// the iv is the second component
OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(1);
// create an IvParameterSpec:
IvParameterSpec ivSpec = new IvParameterSpec(oct.getValue());
//now setup the cipher with previously decrypted recipient key amd params
eci.setupCipher(secretKey, ivSpec);
//get and read the data thereby actually performing the decryption
InputStream data_is = signed_data.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = data_is.read(buf)) > 0) {
// do something useful
}
key - the temporary symmetric key that has been used to encrypt the content,
and now is used for decrypting it againPKCSException - if there occurs an error while decrypting the datajava.security.NoSuchAlgorithmException - if there is no implementation for the content-encryption algorithmjava.security.InvalidKeyException - if the specified private key is not validpublic java.io.InputStream getInputStream()
When having created a new EnvelopedDataStream object to
be encoded to a stream, this method should not be utilized at all, since the stream
automatically will be read during performing the encoding (which is done
when calling a writeTo method).
When having decoded and parsed a received EnvelopedDataStream object
comimg from some stream, this method may be used for obtaining the raw (decrypted) data
after having setup the cipher.
public int getVersion()
public RecipientInfo[] getRecipientInfos()
EnvelopedData object.RecipientInfo objects
included into this EnvelopedDataEnvelopedData#setRecipientInfospublic RecipientInfo getRecipientInfo(java.security.cert.X509Certificate recipientCertificate)
null if no recipient info belonging to the given
certificate can be foundEnvelopedData#setRecipientInfospublic java.lang.Object getEncryptedContentInfo()
EnvelopedDataStream object.
When calling this method for obtaining the inherent EncryptedContentInfoStream
an explicit cast to EncryptedContentInfoStream has to be made:
EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
public ASN1Object toASN1Object() throws PKCSException
toASN1Object in interface ContentStreamEnvelopedDataStream as ASN1Object.PKCSException - if the ASN1Object could not be createdprotected ASN1Object toASN1Object(int blockSize) throws PKCSException
If
blockSize - the block size defining the encoding scheme - and specifying the
length of each primitive encoded octet string component, if positiveEnvelopedData as ASN1ObjectPKCSException - if the ASN1Object could not be createdpublic void writeTo(java.io.OutputStream os)
throws java.io.IOException
os - the output stream to which this EnvelopedData shall be writtenjava.io.IOExceptionpublic void writeTo(java.io.OutputStream os,
int blockSize)
throws java.io.IOException
When encoding the content data to the given stream it is piped through a cipher stream thereby performing the content encryption.
If the a positive blockSize value is specified, the encrypted content
is encoded as indefinite constructed octet string being composed of a certain number
of definite primitive encoded octet strings of blockSize length:
0x24 0x80
0x04 <blocksize> <first encrypted content block>
0x04 <blocksize> <second encrypted content block>
0x04 <blocksize> <third encrypted content block>
...
0x00 0x00
Otherwise, whole the encrypted content is encoded
as definite primitive octet string:
0x04 <length> <encrypted content>
os - the output stream to which this SignedData shall be writtenblockSize - the block size defining the encoding scheme - and specifying the
length of each primitive encoded octet string component, if positivejava.io.IOException - if an error occurs during writing the objectpublic java.lang.String toString()
EnvelopedData object.toString in class java.lang.Objectpublic java.lang.String toString(boolean detailed)
EnvelopedData object.toString in interface ContentStreamdetailed - - whether or not to give detailed information