public class SignedAndEnvelopedData extends SignedData
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 digital 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 participated 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 Version 1.6 removes support for extended certificates, which already is considered by this implementation.
It is recommended 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 information other than the signature.
Assuming that signer_certs
and recipient_certs
represent the certificate chains of some single signer and some intended
recipient, a typical the following basic example shows how to build
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.aes128_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.sha256, privateKey); //add the SignerInfo just created to the SignedAndEnvelopedData object: saed.addSignerInfo(signer_info); //create a RecipientInfo for the intended recipient thereby specifying //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 0x00instead of:
0x04 <length> <encrypted content>The indefinite constructed 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 verified by decrypting the (now single-) encrypted message digest with the signer's public key for comparing it with a independently computed 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(); int numberOfSignerInfos = signer_infos.length; if (numberOfSignerInfos == 0) { System.out.println("Warning: Unsigned message (no SignerInfo included)!"); } else { for (int i = 0; i < numberOfSignerInfos; 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()); } } }The simple example above verifies the correctness of the signature value(s) and dumps the result to System.out. In practice an application will require some more sophisticated error processing.
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 calculates the has respectively
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.SignerInfo
,
RecipientInfo
,
IssuerAndSerialNumber
,
SignedData
,
EnvelopedData
block_size, certificates, content_type, crls, EXPLICIT, IMPLICIT, input_stream, mode, signer_infos, this_object, version
Modifier | Constructor and Description |
---|---|
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 keyLength)
Creates a new PKCS#7 SignedAndEnvelopedData object where the content is
supplied as byte array.
|
|
SignedAndEnvelopedData(byte[] content,
AlgorithmID contentEA,
int keyLength,
int version)
Creates a new PKCS#7 SignedAndEnvelopedData object where the content is
supplied as byte array.
|
|
SignedAndEnvelopedData(java.io.InputStream is)
Creates a new SignedAndEnvelopedData where the DER encoded data is read
from the given InputStream.
|
Modifier and Type | Method and Description |
---|---|
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(java.io.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.
|
java.io.InputStream |
getInputStream()
Returns an InputStream for reading the content.
|
RecipientInfo[] |
getRecipientInfos()
Returns all the recipient infos included in this SignedAndEnvelopedData
object.
|
void |
setRecipientInfos(RecipientInfo[] recipients)
Sets the recipient infos.
|
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
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.
|
java.lang.String |
toString()
Returns a string giving some information about this SignedAndEnvelopedData
object.
|
java.lang.String |
toString(boolean detailed)
Returns a string giving some - if requested - detailed information about
this SignedAndEnvelopedData object.
|
void |
verify(java.security.PublicKey publicKey,
int signerInfoIndex)
Uses the provided public key for verifying the signature that has been
created by the
signerInfoIndex th signer. |
addSignerInfo, getEncoded, setInputStream, setupMessageDigests, setVersion
getBlockSize, getCertificate, getCertificates, getCRLs, getDigestAlgorithms, getMessageDigest, getMode, getSignedDigest, getSignerInfos, getVersion, notifyEOF, setBlockSize, setCertificates, setCRLs, setMessageDigest, setSignerInfos, toASN1Object, verify, verify, writeTo, writeTo
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
getBlockSize, setBlockSize, toASN1Object
protected SignedAndEnvelopedData()
public SignedAndEnvelopedData(byte[] content, AlgorithmID contentEA) throws java.security.NoSuchAlgorithmException
content
- the raw data to sign and envelopecontentEA
- the content encryption algorithm for encrypting the contentjava.security.NoSuchAlgorithmException
- if there is no implementation for the specified algorithmpublic SignedAndEnvelopedData(byte[] content, 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
SignedAndEnvelopedData(byte[] content,
AlgorithmID contentEA)
constructor may be used.
content
- the raw data to sign and 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 lengths, or -1 to use the
default key length for the content encryption algorithm.
version
to keyLength
. However,
for backwards compatibility to prior versions of this class,
the keyLength
parameter is interpreted as version
if it has one of the two only possible version values 1 (default; indicating a PKCS#7v1.5
SignedAndEnvelopedData) or 2 (indicating a PKCS#71.6 SignedAndEnvelopedData). Since
1 or 2 are no reasonable key length values no mis-interpretation of the keyLength
parameter shall occur.java.security.NoSuchAlgorithmException
- if there is no implementation for the specified algorithmpublic SignedAndEnvelopedData(byte[] content, AlgorithmID contentEA, int keyLength, int version) 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
SignedAndEnvelopedData(byte[] content,
AlgorithmID contentEA)
constructor may be used.
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.
content
- the raw data to sign and 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 lengths, or -1 to use the
default key length for the content encryption algorithmversion
- either 1 (indicating a PKCS#7v1.5 SignedAndEnvelopedData) or 2
(indicating a PKCS#71.6 SignedAndEnvelopedData); default is 1.java.security.NoSuchAlgorithmException
- if there is no implementation for the specified algorithmpublic SignedAndEnvelopedData(ASN1Object obj) throws PKCSParsingException
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 existing SignedAndEnvelopedData
object,
supplied as ASN1Object that may have been created by calling
toASN1Object
.
obj
- the PKCS#7 SignedAndEnvelopedData
as ASN1ObjectPKCSParsingException
- if the object can not be parsedpublic SignedAndEnvelopedData(java.io.InputStream is) throws PKCSParsingException, java.io.IOException
is
- the InputStream holding a DER encoded PKCS#7
SignedAndEnvelopedData objectjava.io.IOException
- if an I/O error occurs during reading from the InputStreamPKCSParsingException
- if an error occurs while parsing the objectpublic void decode(ASN1Object obj) throws PKCSParsingException
decode
in interface Content
decode
in class SignedData
obj
- the PKCS#7 SignedAndEnvelopedData as ASN1ObjectPKCSParsingException
- 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
decode
in class SignedData
is
- the InputStream holding a DER encoded PKCS#7
SignedAndEnvelopedData objectjava.io.IOException
- if an I/O error occurs during reading from the InputStreamPKCSParsingException
- if an error occurs while parsing the objectpublic void setupCipher(java.security.PrivateKey recipientPrivateKey, int recipientInfoIndex) throws PKCSException, java.security.NoSuchAlgorithmException, java.security.InvalidKeyException
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.
recipientPrivateKey
- the private key of one recipient specified in a RecipientInfo
objectrecipientInfoIndex
- 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 ObjectID getContentType()
getContentType
in interface ContentStream
getContentType
in class SignedDataStream
ObjectID.pkcs7_signedAndEnvelopedData
public void setRecipientInfos(RecipientInfo[] recipients)
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);
recipients
- a collection of per-recipient informationRecipientInfo
public void addRecipientInfo(RecipientInfo recipient)
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);
recipient
- the RecipientInfo to be addedpublic void verify(java.security.PublicKey publicKey, int signerInfoIndex) throws java.security.SignatureException
signerInfoIndex
th signer.verify
in class SignedDataStream
publicKey
- the public key of the signer to verify the messagesignerInfoIndex
- the index into the SignerInfos array for identifying the
SignerInfo belonging to the signer whose signature has to be
verifiedjava.security.SignatureException
- if the signature turns out to be incorrectpublic RecipientInfo[] getRecipientInfos()
RecipientInfo
objects
included in this SignedAndEnvelopedData
setRecipientInfos(iaik.pkcs.pkcs7.RecipientInfo[])
public java.io.InputStream getInputStream()
The returned content depends on whether creating a new SignedAndEnvelopedData or parsing an existing one:
getInputStream
in class SignedData
public byte[] getContent()
The returned content depends on whether creating a new SignedEnvelopedData or parsing an existing one:
getContent
in class SignedData
public EncryptedContentInfo getEncryptedContentInfo()
protected ASN1Object toASN1Object(int blockSize) throws PKCSException
toASN1Object
in class SignedData
blockSize
- the block size defining the encoding scheme - and specifying the
length of each primitive encoded octet string component, if
positivePKCSException
- if the ASN1Object could not be createdpublic java.lang.String toString()
toString
in class SignedDataStream
public java.lang.String toString(boolean detailed)
toString
in interface ContentStream
toString
in class SignedData
detailed
- - whether or not to give detailed information