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 Standard 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 specification,
Version 1.5):
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 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 positive 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 0x00instead of:
0x04 <length> <encrypted content>The indefinite constructed 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.
|
int |
getBlockSize()
Gets the block size defining the length of each definite primitive encoded
octet string component.
|
ObjectID |
getContentType()
Returns the content type this class implements.
|
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 ContentStream
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 ObjectID getContentType()
getContentType
in interface ContentStream
ObjectID.pkcs7_envelopedData
public 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 informationRecipientInfo
public 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 ContentStream
blockSize
- for defining the encoding scheme and setting the octet string
component length, if positiveOCTET_STRING
public int getBlockSize()
blockSize
is smaller
or equal to zero the whole data is encoded as definite primitive octet
string. This method may be used for enforcing block encoding when wrapping
the EncryptedData into a ContentInfo.getBlockSize
in interface ContentStream
OCTET_STRING
public 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 separately, 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 content encryption algorithm: AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); // get the parameters as SEQUENCE SEQUENCE seq = (SEQUENCE)contentEA.getParameter(); // create an RC2ParameterSpec: int rc2ParameterVersion = ((BigInteger)seq.getComponentAt(0).getValue()).intValue(); int effective_key_bits = 32; switch (rc2ParameterVersion) { case 160: effective_key_bits = 40; break; case 120: effective_key_bits = 64; break; case 58: effective_key_bits = 128; break; default: throw new Exception("Invalid rc2ParameterVersion " + rc2ParameterVersion + "!"); } // the iv is the second component OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(1); byte[] iv = (byte[])oct.getValue(); RC2ParameterSpec paramSpec = new RC2ParameterSpec(effective_key_bits, iv); //now setup the cipher with previously decrypted recipient key amd params eci.setupCipher(secretKey, paramSpec); //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 separately, 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(); // create an RC2ParameterSpec: int rc2ParameterVersion = ((BigInteger) seq.getComponentAt(0).getValue()) .intValue(); int effective_key_bits = 32; switch (rc2ParameterVersion) { case 160: effective_key_bits = 40; break; case 120: effective_key_bits = 64; break; case 58: effective_key_bits = 128; break; default: throw new Exception("Invalid rc2ParameterVersion " + rc2ParameterVersion + "!"); } // the iv is the second component OCTET_STRING oct = (OCTET_STRING) seq.getComponentAt(1); byte[] iv = (byte[]) oct.getValue(); RC2ParameterSpec paramSpec = new RC2ParameterSpec(effective_key_bits, iv); // now setup the cipher with previously decrypted recipient key and params eci.setupCipher(secretKey, paramSpec); // 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 EnvelopedData
setRecipientInfos(iaik.pkcs.pkcs7.RecipientInfo[])
public RecipientInfo getRecipientInfo(java.security.cert.X509Certificate recipientCertificate)
null
if no recipient info belonging to the given
certificate can be foundsetRecipientInfos(iaik.pkcs.pkcs7.RecipientInfo[])
public 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 ContentStream
EnvelopedDataStream
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.IOException
public 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 0x00Otherwise, 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.Object
public java.lang.String toString(boolean detailed)
EnvelopedData
object.toString
in interface ContentStream
detailed
- - whether or not to give detailed information