public class SignedData extends SignedDataStream implements Content
SignedData
.
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 SignedData
content type is
defined as:
signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }
which corresponds to the OID string "1.2.840.1.113549.1.7.2".
The PKCS#7
Cryptographic Message Standard specifies the SignedData
content type for providing a syntax for building digital signatures. Content
of any type may be signed by any number of signers in parallel. For each
signer, a message digest is computed on the content (and any additional
authenticating information) 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
- together with some signer-specific information - collected into a
SignerInfo
value. Finally all created SignerInfo
values
are collected together with the content for forming a SignedData
structure.
This class implements the SignedData
structure resulting from
the last step described above. The SignedData
type is defined
as ASN.1 SEQUENCE type containing the following components (see PKCS#7 specification,
Version 1.5; notice that the ASN.1 module printed below not fully agrees
with the SignedData definition of Version 1.5, since the certificates field
is interpreted as SET OF pure X.509 certificates, whereas Version 1.5 also
allows PKCS#6 extended certificates!):
SignedData ::= SEQUENCE { version Version, digestAlgorithms DigestAlgorithmIdentifiers, contentInfo ContentInfo, certificates [0] IMPLICIT Certificates OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos }
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
Certificates ::= SET OF Certificate -- X.509
CertificateRevocationLists ::= SET OF CertificateRevocationList
SignerInfos ::= SET OF SignerInfo
The digestAlgorithms
field contains the object identifiers of
the message digest algorithms used by the several signers for digesting the
content that is supplied in the contentInfo
field. 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.
And finally, the signerInfos
field collects per-signer
information for all participated signers including the encrypted digests
of the content (i.e. the signer-specific digital signatures of the content).
If there are no signers on the content, the signed-data content type may be used for disseminating certificates and certificate-revocation lists.
Verifying some received signature(s) is done by decrypting the encrypted message digests for each signer with the signer's public key and comparing the results with independently computed message digests on the original content.
For more information consult the RSA PKCS#7 specification.
The SignedData
version implemented by this class complies with the
SignedData structure as used by the S/MIME protocol. In this way, the digest
is calculated on only the content octets of the DER encoding of the content field
of the inherent ContentInfo structure. At this point it is important to note
that currently IAIK-JCE only supports SignedData structures to be used for
signing content values of the PKCS#7 Data content type!
When creating a SignedData
object for the content to be signed by using
the SignedData(byte[] content, int mode)
constructor, additionally the transmission mode has to be specified. If the mode is
set to SignedData.IMPLICIT the raw data will be included in the SignedData
message to be transmitted, but it will be not included if the mode is set to SignedData.EXPLICIT.
However, in both cases the raw data has to be supplied when creating the SignedData
object, because it is needed for the digest computation:
byte[] data = ...; SignedData signed_data = new SignedData(data, SignedData.IMPLICIT);respectively
SignedData signed_data = new SignedData(data, SignedData.EXPLICIT);In contrast to the stream-variant of the PKCS#7 SignedData type (implemented by the
SignedDataStream
class),
where explicit and implicit mode require a different proceeding, both cases may be
handled in the same way, when using the non-stream variant. In this way, the
steps of creating a SignedData
object and preparing it for transmission
can be summarized in the following way (to simplify matters, we will assume not to include
certificate revocation lists):
SignedData
object thereby supplying the raw data
to be signed and specifying the transmission mode to be used (either
SignedData.IMPLICIT or SignedData.EXPLICIT):
byte[] data = ...; int mode = ...; SignedData signed_data = new SignedData(data, mode);
setCertificates
method. The certificates are supplied
as array of X509Certificates
an shall contain chains from a known top-level CA to all the signers in the
SignerInfo
field:
signed_data.setCertificates(certificates);
SignerInfo
object, optionally supply it with attributes, and add it to the SignedData structure
by calling the addSignerInfo
method:
SignerInfo signer1 = ...; signed_data.addSignerInfo(signer1); SignerInfo signer2 = ...; signed_data.addSignerInfo(signer2); ...You alternatively may collectively add all signers by utilizing the
setSignerInfos
method.
toASN1Object
method, the latter by using the getEncoded
method:
ASN1Object obj = signed_data.toASN1Object();respectively
byte[] encoding = signed_data.getEncoded();You alternatively may use a proper
writeTo
method of the parent
SignedDataStream
class for immediately
encoding this SignedData object to an output stream. When using writeTo
in
implicit mode, you additionally have the possibility of specifying a particular blockSize
for forcing an indefinite constructed encoding of the inherent raw data bytes, instead of
of the default definite primitive encoding, e.g:
0x24 0x80 0x04 0x02 0x01 0xAB 0x04 0x02 0x23 0x7F 0x04 0x01 0xCA 0x00 0x00instead of:
0x04 0x05 0x01 0xAB 0x23 0x7F 0xCAfor encoding the five data bytes
0x01 0xAB 0x23 0x7F 0xCA
. 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).
Unlike the procedure of newly creating a SignedData object to be transmitted, even when
using this non-stream implementation of the SignedData content type, it
has to be distinguished between IMPLICIT and EXPLICIT mode when parsing a received
SignedData message. When operating in IMPLICIT mode, the raw data is included in
the received SignedData object, and so the parsing immediately may be
performed when creating a SignedData
object from the - already decoded -
SignedData ASN1Object by calling the SignedData(ASN1Object obj)
constructor. On the other side, when
the raw data has been transmitted outside the SignedData message (EXPLICIT mode), the
SignedData(byte[] content, AlgorithmID[] hashAlgorithms)
constructor
has be to used for initializing the new SignedData object with raw data and
hash algorithms to be used for digest computation; and the decoding has to be performed
explicitly by calling the decode
method.
The initialization is necessary for computing the digests on the raw data for the
digest algorithms of all participated signers. Later, during signature verification these
digest values have to be compared against the hash values
resulting from decrypting the encrypted digests with the signer's public keys.
All further steps are the same for implicit and explicit mode, and so the proceeding
necessary for parsing a received SignedData message and verifying the signatures
may be summarized as follows:
ASN1Object obj = DerCoder.decode(received_message);
SignedData(ASN1Object obj)
constructor for creating a SignedData object and implicitly parsing the
the supplied ASN.1 structure:
SignedData signedData = new SignedData(obj);On the other hand, if the ASN1Object resulting from step 1 above represents an explicit SignedData object, use the
SignedData(byte[] content, AlgorithmID[] hashAlgorithms)
constructor for initializing a new SignedData object with raw data and
digest algorithms for hash computation, and subsequently explicitly perform
the decoding by means of the decode
method (assuming that two hash algorithms are used: SHA-1 and RIPEMD160):
AlgorithmID[] algIDs = { ... }; SignedData signedData = new SignedData(raw_data, algIDs); signedData.decode(obj);
// get the signer infos SignerInfo[] signer_infos = signed_data.getSignerInfos(); // verify the signatures 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 signature for SignerInfo at index i X509Certificate signer_cert = signed_data.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_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN()); } } }
byte[] data = signedData.getContent();
For handling large amounts of data, the parent SignedDataStream
class should be used.
PKCS#7v1.6 Support:
===================
This class also may be used for creating/parsing SignedData objects of version 1.6 as
used by the SET protocol. Main differences between v1.5 and v1.6 are the hash calculation
(v1.6 calculates the hash over 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 SignedData object you only have to call method setVersion
immediately after creating a (implicit or explicit) SignedData object:
int mode = ...; byte[] content = ...; SignedData signedData = new SignedData(content, mode); // version number 2 indicates PKCS#7v1.6 signedData.setVersion(2); ...On the receiving end you only have to take care about the version number when
initializing
this class for an explicit signed
message (in implicit mode you do not have to take care about the version number at all).
If version 1.6 is used you have to specify the version
number to indicate to calculate the hash over the content encoding:
// raw data received by other means: byte[] content = ...; // hash algorithm(s) used AlgorithmID[] hashAlgIDs = ...; // version 2 indicates PKCS#7v1.6 int version = 2; SignedData signedData = new SignedData(content, hashAlgIDs, version); ...Since PKCS#7v1.6 is unsuitable for stream handling, the stream based class
SignedDataStream
does not support PKCS#7v1.6.Content
,
ContentInfo
,
SignerInfo
,
SignedDataStream
block_size, certificates, content_type, crls, EXPLICIT, IMPLICIT, input_stream, mode, signer_infos, this_object, version
Modifier | Constructor and Description |
---|---|
protected |
SignedData()
Default constructor for dynamic object creation in ContentInfo.
|
|
SignedData(ASN1Object obj)
Creates a PKCS#7
SignedData from an ASN1Object. |
|
SignedData(byte[] content,
AlgorithmID[] hashAlgorithms)
Creates a new SignedData from a byte array holding the content that
has been transmitted by other means, and an array specifying the hash
algorithms to be used for digesting.
|
|
SignedData(byte[] content,
AlgorithmID[] hashAlgorithms,
int version)
Creates a new SignedData from a byte array holding the content that
has been transmitted by other means, and an array specifying the hash
algorithms to be used for digesting.
|
|
SignedData(byte[] content,
int mode)
Creates a SignedData object from a byte array containing the raw data to be signed.
|
|
SignedData(java.io.InputStream is)
Creates a new SignedData where the DER encoded data
is read from the given InputStream.
|
|
SignedData(ObjectID contentType)
Creates a new SignedData object without any content.
|
Modifier and Type | Method and Description |
---|---|
void |
addSignerInfo(SignerInfo signerInfo)
Adds a SignerInfo object to this SignedData.
|
void |
decode(ASN1Object obj)
Decodes the SignedData supplied as ASN1Object.
|
void |
decode(java.io.InputStream is)
Reads and decodes the SignedData from a DerInputStream.
|
byte[] |
getContent()
Returns the content.
|
byte[] |
getEncoded()
Returns the DER encoding of this SignedData object as byte array.
|
java.io.InputStream |
getInputStream()
Returns an InputStream from which the contents of this object can be read.
|
void |
setInputStream(java.io.InputStream is)
Sets the InputStream which holds the content to sign.
|
protected void |
setupMessageDigests()
Sets up the message digests for hash computation.
|
void |
setVersion(int version)
Sets the version of this SignedData.
|
protected ASN1Object |
toASN1Object(int blockSize)
Returns this SignedData as ASN1Object.
|
java.lang.String |
toString(boolean detailed)
Returns a string giving some - if requested - detailed information
about this
SignedData object. |
getBlockSize, getCertificate, getCertificates, getContentType, getCRLs, getDigestAlgorithms, getMessageDigest, getMode, getSignedDigest, getSignerInfos, getVersion, notifyEOF, setBlockSize, setCertificates, setCRLs, setMessageDigest, setSignerInfos, toASN1Object, toString, verify, verify, verify, writeTo, writeTo
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
getBlockSize, getContentType, setBlockSize, toASN1Object
protected SignedData()
public SignedData(ObjectID contentType)
contentType
- the contentType of the datapublic SignedData(java.io.InputStream is) throws PKCSParsingException, java.io.IOException
is
- the InputStream holding a DER encoded PKCS#7 SignedData objectjava.io.IOException
- if an I/O error occurs during reading from the InputStreamPKCSParsingException
- if an error occurs while parsing the objectpublic SignedData(byte[] content, int mode)
content
- the raw data to be signedmode
- IMPLICIT if the message shall be included in the DER encoding,
EXPLICIT otherwisepublic SignedData(ASN1Object obj) throws PKCSParsingException
SignedData
from an ASN1Object.
Do not use this constructor for supplying the content value
to be signed. This constructor may be used by the recipient for parsing an
already existing SignedData
object, supplied as ASN1Object
that may have been created by calling
toASN1Object
.
Use the SignedData(byte[] content, int mode)
constructor for supplying the content value to be signed when creating a
SignedData
object.
This constructor only shall be used for parsing an ASN1Object that represents
a SignedData object with included raw data bytes (implicit mode).
To initialize a SignedData object for parsing an explicit SignedData message where the
raw data is not included, use the
SignedData(byte[] content, AlgorithmID[] hashAlgorithms)
constructor, and perform the decoding explicitly by calling the
decode
method.
obj
- the PKCS#7 SignedData as ASN1Object representing an implicit SignedData objectPKCSParsingException
- if a parsing error occurspublic SignedData(byte[] content, AlgorithmID[] hashAlgorithms) throws java.security.NoSuchAlgorithmException
Do not use this constructor for supplying the content value
to be signed. This constructor may be used by the recipient for initializing
the digest computation for an already existing explicit SignedData message
where the raw data is not included. In contrast to
the equivalent constructor of the parent SignedDataStream
class, this constructor not only initializes
the digest computation, but also already computes the digests for all supplied
digest algorithms. Later, during signature verification these digest values have
to be compared against the hash values resulting from decrypting the encrypted
digests with the signer's public keys.
For subsequently performing the decoding
of the received explicit SignedData object, use the decode
method:
// the received explicit SignedData structure as ASN1Object: ASN1Object = DerCoder.decode(received_message); //initialize - and perform - digest computation: SignedData signedData = new SignedData(raw_data, hashAlgorithms); //parse the received SignedData ASN1Object signedData.decode(obj);
A sender shall use the SignedData(byte[] content, int mode)
constructor for supplying the content value to be signed when creating a
SignedData
object.
For parsing an implicit SignedData message, use the SignedData(ASN1Object obj)
constructor.
content
- the content transmitted by other meanshashAlgorithms
- the hash algorithms used by the participated signers for digesting the
content datajava.security.NoSuchAlgorithmException
- if any of the supplied hash algorithms is not supportedpublic SignedData(byte[] content, AlgorithmID[] hashAlgorithms, int version) throws java.security.NoSuchAlgorithmException
Do not use this constructor for supplying the content value
to be signed. This constructor may be used by the recipient for initializing
the digest computation for an already existing explicit SignedData message
where the raw data is not included. In contrast to
the equivalent constructor of the parent SignedDataStream
class, this constructor not only initializes
the digest computation, but also already computes the digests for all supplied
digest algorithms. Later, during signature verification these digest values have
to be compared against the hash values resulting from decrypting the encrypted
digests with the signer's public keys.
For subsequently performing the decoding
of the received explicit SignedData object, use the decode
method:
// the received explicit SignedData structure as ASN1Object: ASN1Object = DerCoder.decode(received_message); //initialize - and perform - digest computation: SignedData signedData = new SignedData(raw_data, hashAlgorithms); //parse the received SignedData ASN1Object signedData.decode(obj);
A sender shall use the SignedData(byte[] content, int mode)
constructor for supplying the content value to be signed when creating a
SignedData
object.
For parsing an implicit SignedData message, use the SignedData(ASN1Object obj)
constructor.
This constructor may be used for initializing an explicit signed v1.6 SignedData message, where the version number (2) has to be supplied for calculating the hash value(s) over the whole content encoding.
content
- the content transmitted by other meanshashAlgorithms
- the hash algorithms used by the participated signers for digesting the
content dataversion
- the version; either 1 or 2java.security.NoSuchAlgorithmException
- if any of the supplied hash algorithms is not supportedpublic void addSignerInfo(SignerInfo signerInfo) throws java.security.NoSuchAlgorithmException
Unlike the same-name method in the SignedDataStream
super class this
method already performs the digest computation for the given SignerInfo.
addSignerInfo
in class SignedDataStream
signerInfo
- the SignerInfo to addjava.security.NoSuchAlgorithmException
- if there is no implementation for the message digest algorithm
specified in the signerInfoSignerInfo
protected void setupMessageDigests() throws java.security.NoSuchAlgorithmException
java.security.NoSuchAlgorithmException
- if any of the participated digest algorithms
is not supportedpublic void decode(ASN1Object obj) throws PKCSParsingException
SignedData(byte[] content, AlgorithmID[] hashAlgorithms)
constructor for initializing it for hash computation:
// the received explicit SignedData structure as ASN1Object: ASN1Object = DerCoder.decode(received_message); //initialize - and perform - digest computation: SignedData signedData = new SignedData(raw_data, hashAlgorithms); //parse the received SignedData ASN1Object signedData.decode(obj);
decode
in interface Content
obj
- the PKCS#7 SignedData 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.
This method provides an alternative to the decoding
method where the SignedData to be decoded is given as ASN1Object.
decode
in interface ContentStream
decode
in class SignedDataStream
is
- the InputStream holding a DER encoded PKCS#7 SignedData objectjava.io.IOException
- if an I/O error occurs during reading from the InputStreamPKCSParsingException
- if an error occurs while parsing the objectpublic void setInputStream(java.io.InputStream is)
SignedDataStream
parent class.setInputStream
in class SignedDataStream
is
- InputSteam holding the content to signpublic void setVersion(int version)
version
- the version; either 1 or 2public java.io.InputStream getInputStream()
This method only overrides the corresponding getInputStream
method
of the parent SignedDataStream
class for returning the content of this SignedData
object. There should be
no real necessity for using this method since the raw data bytes immediately
can be obtained by the getContent
method.
However, in contrast to the equivalent getInputStream
method of the
parent SignedDataStream
class, this method may be called arbitrarily often;
it only returns a ByteArrayInputStream that is initialized with the raw content bytes.
getInputStream
in class SignedDataStream
SignedData
objectpublic byte[] getContent()
protected ASN1Object toASN1Object(int blockSize) throws PKCSException
If blockSize
is set to a positive value the ASN1Object returned
by this method is prepared for forcing an indefinite constructed encoding of the inherent raw
data bytes, instead of the default definite primitive encoding, e.g:
0x24 0x80 0x04 0x02 0x01 0xAB 0x04 0x02 0x23 0x7F 0x04 0x01 0xCA 0x00 0x00instead of:
0x04 0x05 0x01 0xAB 0x23 0x7F 0xCAfor encoding the five data bytes
0x01 0xAB 0x23 0x7F 0xCA
. 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).
The inherent data is encoded by means of the primitive definite method when the
blockSize
value is not positive.toASN1Object
in class SignedDataStream
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 byte[] getEncoded() throws PKCSException
writeTo(OutputStream os, int blockSize)
method
of the parent SignedDataStream
class.PKCSException
- if an encoding error occurspublic java.lang.String toString(boolean detailed)
SignedData
object.toString
in interface ContentStream
toString
in class SignedDataStream
detailed
- - whether or not to give detailed information