public class SMimeSigned extends SignedDataStream
S/MIME (Secure/Multipurpose Internet Mail Extensions) provides two formats for signed messages:
application/pkcs7-mime and SignedData (or
application/x-pkcs7-mime for early implementations)
multipart/signed
The first format (application/pkcs7-mime and SignedData) processes
the whole (prepared) MIME entity to be signed into a CMS object of
type SignedData which subsequently is inserted into an
application/pkcs7-mime MIME entity.
The smime-type parameter for messages signed using this format is
"signed-data", the file extension is ".p7m" (see
S/MIME Version 3 Message Specification). The "Content-" headers of a sample
message would look like:
Content-Type: application/pkcs7-mime; smime-type="signed-data";
name="smime.p7m"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="smime.p7m"
fgwehds...
For viewing a message that is signed using this format, a recipient must take care for an
S/MIME aware mail client.
The second format (multipart/signed) makes use of the multipart/signed
MIME type, consisting
of two parts. The first part contains the MIME entity to be signed, and the second
part contains the signature, which is a CMS detached signature,
inserted into a MIME entity of type application/pkcs7-signature (or
application/x-pkcs7-signature for early implementations) leaving the
ContentInfo (content) field of the SignedData object empty.
The multipart/signed Content type requires two parameters (see
S/MIME Version 3 Message Specification):
application/pkcs7-signature" - or
"application/x-pkcs7-signature" for early implementations)
md5, sha1, or any other (i.e. unknown))
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1;
boundary="----=_NextPart_000_00AA_01BD7FE8.1CD08610"
------_NextPart_000_00AA_01BD7FE8.1CD08610
Content-Type: text/plain ;
charset="iso-8859-1"
Content-Transfer-Encoding: 8bit
This is a clear-signed message.
------_NextPart_000_00AA_01BD7FE8.1CD08610
Content-Type: application/pkcs7-signature;
name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename=smime.p7s
sdefqj...
------_NextPart_000_00AA_01BD7FE8.1CD08610--
Messages signed using this format can be viewed by the recipient, regardless of
being equipped with S/MIME aware software. If no S/MIME service is installed, the
signature will be shown as attachment.
This class supports both formats, application/x-pkcs7-mime and
multipart/signed. For distinguishing between the two formats when creating
a new SMimeSigned object using the SMimeSigned(InputStream is, int mode) constructor, the mode parameter
has to be set either to IMPLICIT or to EXPLICIT, depending on whether to use the
application/x-pkcs7-mime format or the multipart/signed format:
This class extends theInputSrteam[] data_stream = ...; // the raw data supplying input stream SMimeSigned signed = new SMimeSigned(data_stream, SMimeSigned.IMPLICIT); respectively SMimeSigned signed = new SMimeSigned(data_stream, SMimeSigned.EXPLICIT);
SignedDataStream class of the iaik.cms
package of the IAIK-JCE toolkit for taking advantage of the stream architecture
introduced there. Following this practice, the data is prepared according
CMS (Cryptographic Message Syntax) by piping it through a digest stream to calculate a SHA-1 hash as
proposed by the
S/MIME Version 3 Message Specification.
The steps for creating a SMimeSigned object and writing it to a stream may
be summarized as follows (notice that the usage in general complies with that
of the parent SignedDataStream, but is adapted to the specific
requirements of the S/MIME protocol):
SMimeSigned object thereby supplying the raw data
to be signed as input stream and specifying the transmission mode to be used
(either SMimeSigned.IMPLICIT or SMimeSigned.EXPLICIT):
InputStream data_stream data_stream = ...;
int mode = ...;
SMimeSigned signed = new SMimeSigned(data_stream, mode);
setCertificates(Certificate[]) method.
signed.setCertificates(certificates);
addSigner(PrivateKey, IssuerAndSerialNumber) method thereby supplying signer
private key and IssuerAndSerialNumer of the signer certificate (note that when
adding a Signer using this method, rsaEncryption will be used together with
SHA-1 for signing the content. If you want to use some other digest/signature
algorithm you may call some alternative addSigner method):
X509Certificate signerCertificate = certificates[0];
signed.addSigner(signerPrivateKey, new IssuerAndSerialNumber(signerCertificate));
The addSigner method creates an iaik.cms.SignerInfo
object from the given information and supplies the SignerInfo to the SMimeSigned
object. This step also initializes the digest computation is by wrapping a
digest stream for the SHA-1 digest algorithm around the data carrying input stream.
When creating the SignerInfo object for the signer, the following
attributes are set for the SignerInfo structure, as proposed by CMS and S/MIME:
addSigner method allowing to select the algorithms to be used:
signed.addSigner(signerPrivateKey, new IssuerAndSerialNumber(signerCertificate),
AlgorithmID.sha1, AlgorithmID.dsa_With_SHA1);
if (mode == SMimeSigned.EXPLICIT) {
InputStream data_is = signed.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = data_is.read(buf)) > 0) {
// do something useful
}
}
When using the implicit mode, do not explicitly read data from the input stream
at all! This will be done automatically during the last step when performing the encoding.
writeTo method for wrapping
the SignedData object into a ContentInfo and writing it BER encoded to an output
stream.
signed.writeTo(output_stream);
On recipient side it also has to be distinguished between IMPLICIT and EXPLICIT
mode for parsing a received SMimeSigned message. When operating in IMPLICIT mode,
the raw data is included in the received object, and so the parsing immediately
may be performed when creating a SMimeSigned object from the DER encoded
SMimeSigned message by calling the SMimeSigned(InputStream is) constructor. On the other side, when
the raw data has been transmitted outside the signature (EXPLICIT mode), the
SMimeSigned(InputStream data_is, AlgorithmID[] hashAlgorithms) constructor
has to be used for initializing the new SMimeSigned 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 preparing the digest computation on the raw
data. Later, during signature verification the digest value computaion is
finished and the result is compared against the hash value resulting from
decrypting the encrypted digest with the signer´s public key.
The individual steps necessary for parsing a received SMimeSigned message and
verifying the signature may be summarized as follows:
SMimeSigned(InputStream is)
constructor for creating a SMimeSigned object and implicitly performing the
decoding:
SMimeSigned signed = new SMimeSigned(encoded_stream);
On the other hand, if the BER encoding represents an
explicit SMimeSigned object, use the
SMimeSigned(InputStream data_is, AlgorithmID[] hashAlgorithms)
constructor for initializing a new SMimeSigned object with raw data and
digest algorithm for hash computation (only one signer):
AlgorithmID[] algID = { AlgorithmID.sha1 };
SMimeSigned signed = new SMimeSigned(data_is, algIDs);
InputStream dataIs = signed.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = dataIs.read(buf)) > 0) {
// do something useful
}
decode method:
signed.decode(encoded_stream);
try {
X509Certificate cert = signed.verify();
System.out.println("Signature OK from: "+cert.getSubjectDN());
} catch (SignatureException ex) {
System.out.println("Signature ERROR!");
}
blockSize_, certSet_, contentType_, crls_, encapContentInfo_, EXPLICIT, IMPLICIT, inputStream_, mode_, securityProvider_, signerInfos_, thisObject_, version_| Constructor and Description |
|---|
SMimeSigned(java.io.InputStream is)
Reads a SMimeSigned message from an InputStream.
|
SMimeSigned(java.io.InputStream is,
AlgorithmID[] hashAlgorithm)
Creates a new SMimeSigned from an InputStream holding the signed data and
an algorithm specifying the hash algorithm to use for digesting.
|
SMimeSigned(java.io.InputStream is,
int mode)
Creates a SMimeSigned object from an input stream which supplies
the data to be signed.
|
| Modifier and Type | Method and Description |
|---|---|
void |
addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer)
Signs this message using the supplied signer´s RSA private key.
|
void |
addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm)
Signs this message using the supplied signer´s private key with the
given signature algorithm.
|
void |
addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm,
Attribute[] signedAttributes)
Signs this message using the supplied signer´s private key with the
given signature algorithm.
|
void |
addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm,
CertificateIdentifier encrypter,
boolean includeEncryptionCertIDForMSOE)
Signs this message using the supplied signer´s private key with the
given signature algorithm.
|
void |
addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
CertificateIdentifier encrypter,
boolean includeEncryptionCertIDForMSOE)
Signs this message using the supplied signer´s RSA private key.
|
void |
decode(java.io.InputStream is)
Reads and decodes a BER encoded SMimeSigned object from an input stream.
|
int |
getMode()
Returns the mode of this message.
|
void |
requestTimeStamp(TimeStampClient timeStampClient)
Requests that the last signature that exists in this SMimeSigned structure
be time-stamped.
|
void |
requestTimeStamp(TimeStampClient timeStampClient,
int signerInfoIndex)
Requests that the signature at the indicated index in this SMimeSigned
structure be time-stamped.
|
ASN1Object |
toASN1Object()
Returns this SMimeSigned object as an ASN1Object.
|
java.lang.String |
toString()
Returns a string representation of this message.
|
X509Certificate |
verify()
Verifies the signature and returns the certificate of the signer (i.e.
|
void |
verify(java.security.PublicKey publicKey)
Verifies the signature of the signer (i.e.
|
void |
writeTo(java.io.OutputStream os)
Writes this SMimeSigned object to the supplied output stream.
|
void |
writeTo(java.io.OutputStream os,
int blockSize)
Writes this SMimeSigned object to the supplied output stream.
|
addSignerInfo, getAttributeCertificates, getBlockSize, getCertificate, getCertificates, getCertificateSet, getContentType, getCRLs, getDigestAlgorithms, getEncapsulatedContentType, getInputStream, getMessageDigest, getSignedDigest, getSignerInfos, getVersion, getX509Certificates, notifyEOF, setBlockSize, setCertificates, setCertificateSet, setCRLs, setInputStream, setMessageDigest, setSignerInfos, toASN1Object, toString, verify, verify, verify, verifyAndValidate, verifyAndValidatepublic SMimeSigned(java.io.InputStream is,
int mode)
There are two possible modes:
is - a stream supplying the data to signIMPLICIT - if the message shall be included in the DER encoding,
EXPLICIT otherwisepublic SMimeSigned(java.io.InputStream is,
AlgorithmID[] hashAlgorithm)
throws java.io.IOException
This constructor shall be used to process an already existing EXPLICIT
SMimeSigned object. It will setup a DigestInputStream where the signed data
is piped through. The new InputStream can be retrieved using the method
getInputStream for reading the data and thereby
piping it through a digest stream for hash calculation.
For subsequently processing the DER encoded SMimeSigned object, use method
decode(InpuStream), e.g.:
AlgorithmID[] algID = { AlgorithmID.sha1 };
SMimeSigned signed = new SMimeSigned(data_is, algIDs);
InputStream dataIs = signed.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = dataIs.read(buf)) > 0) {
// do something useful
}
signed.decode(encoded_stream);
is - the InputStream holding the raw data supplied by other means, i.e. the
first part of a multipart/signed messagehashAlgorithm - the hash algorithm used for digesting the signed data;
supplied as array of hash algorithms as required by the parent
SignedDataStream class, but actually containing only one digest
algorithm identifier for the one and only signerjava.io.IOException - if an I/O error occurs or there is no implementation for the specified
hash algorithmpublic SMimeSigned(java.io.InputStream is)
throws java.io.IOException
Do not use this constructor for supplying the content data
to be signed. This constructor may be used by the recipient for parsing an
already exisiting SMimeSigned object, supplied as DER encoding
from an input stream, that may have been created by means of the
writeTo method.
Use the SMimeSigned(InputStream data_is, int mode)
constructor for supplying the content data to be signed when creating a
SMimeSigned object.
This constructor only shall be used for decoding a SMimeSigned object with
included raw data (implicit mode).
To initialize a SMimeSigned object for parsing an explicit message where the
raw data is not included, use the
SMimeSigned(InputStream is, AlgorithmID[] hashAlgorithms)
constructor, and perform the decoding explicitly by calling the
decode method.
is - the input stream where the DER encoded message shall be read fromjava.io.IOException - if an I/O error or parsing problem occurspublic void decode(java.io.InputStream is)
throws java.io.IOException
This method implicitly is called from inside the corresponding constructor for
decoding an received implicit SMimeSigned object where the raw data is
included.
This method has to be explicitly called for decoding a received explicit
SMimeSigned object where the raw data is not included. Before calling
this method for decoding an explicit message, a new SMimeSigned object
has to be created by means of the
SMimeSigned(InputStream is, AlgorithmID[] hashAlgorithm)
constructor for initializing it for hash computation, and the data has to be
read from the stream for upadating the hash value:
// initialize for hash computation:
SMimeSigned signed = new SMimeSigned(is, hashAlgorithm);
//read the stream thereby updating the hash values:
InputStream data_is = signed.getInputStream();
byte[] buf = new byte[1024];
int r;
while ((r = data_is.read(buf)) > 0) {
// do something useful
}
// explicitly perform the decoding
signed.decode(encoded_stream);
decode in interface ContentStreamdecode in class SignedDataStreamis - the InputStream holding a DER encoded objectjava.io.IOException - if an I/O error occurs during reading from the InputStreampublic void addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer)
throws java.security.NoSuchAlgorithmException
SHA algorithm as specified in FIPS PUB 180-2.
Since rsaEncryption is used, the supplied "signing" key has to be a
RSAPrivateKey. The second parameter value, a IssuerAndSerialNumber
object, sets the issuerAndSerialNumber field of the
SignerInfo structure of the inherent SignedData object. This field is used
for specifying the signer´s certificate by issuer distinguished name and
issuer-specific serial number.
Calling this method also initializes the digest computation by wrapping a
digest stream for the SHA-1 digest algorithm around the data carrying input
stream. When creating the SignerInfo object for the signer, the
following attributes are set for the SignerInfo structure:
privateKey - the (RSA) private key which shall be used for signingsigner - IssuerAndSerialNumber from the certificate which must be used
for verifying the signaturejava.security.NoSuchAlgorithmException - if no implementation of the SHA-1
message digest algorithm is available.public void addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
CertificateIdentifier encrypter,
boolean includeEncryptionCertIDForMSOE)
throws java.security.NoSuchAlgorithmException
SHA algorithm as specified in FIPS PUB 180-2.
Since rsaEncryption is used, the supplied "signing" key has to be a
RSAPrivateKey. The second parameter value, a IssuerAndSerialNumber
object, sets the issuerAndSerialNumber field of the
SignerInfo structure of the inherent SignedData object. This field is used
for specifying the signer´s certificate by issuer distinguished name and
issuer-specific serial number.
Calling this method also initializes the digest computation by wrapping a
digest stream for the SHA-1 digest algorithm around the data carrying input
stream. When creating the SignerInfo object for the signer, the
following attributes are set for the SignerInfo structure:
encryptionCertID is not null
encryptionCertID is not null and
includeEncryptionCertIDForMSOE allowing MSOE to recognize
the encryption certificate is different certs are used for signing
and encryption
Inclusion of a special private Microsoft signed attribute (type: 1.3.6.1.4.1.311.16.4)
for identifying the sender´s encryption certificate by IssuerAndSerialNumber
might be useful to tell Outlook Express the encryption certificate to be used if
separate certificates are used for signing and encryption. If you want to include
this attribute, set includeEncryptionCertIDForMSOE to true
and supply the IssuerAndSerialNumber of the encryption certificate ("encrypter").
privateKey - the (RSA) private key which shall be used for signingsigner - IssuerAndSerialNumber from the certificate which must be used
for verifying the signatureencrypter - the identifier of the encryption certificate of the
sender by the SMIMEEncryptionKeyPreference attribute (or
null if signing and encryption cert are the
same or no encryption certificate shall be indicated)includeEncryptionCertIDForMSOE - if true and an
encryptionCertID of type IssuerAndSerialNumber is provided,
a private MS attribute will be included allowing MSOE to recognize
the encryption cert of the signer if using different certs for
signing/encryptionjava.security.NoSuchAlgorithmException - if no implementation of the SHA-1
message digest algorithm is available.public void addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm)
throws java.security.NoSuchAlgorithmException
Calling this method also initializes the digest computation by wrapping a
digest stream for the digest algorithm around the data carrying input
stream. When creating the SignerInfo object for the signer, the
following attributes are set for the SignerInfo structure:
privateKey - the (RSA) private key which shall be used for signingsigner - IssuerAndSerialNumber from the certificate which must be used
for verifying the signaturedigestAlgorithm - the digest algorithm; default: SHA-1 (used, if null)signatureAlgorithm - the signature algorithm; default: rsaEncryption (used, if null);
Attention! use AlgorithmID.rsaEncryption for RSA signing!java.security.NoSuchAlgorithmException - if no implementation of the requested
message digest algorithm is availablepublic void addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm,
CertificateIdentifier encrypter,
boolean includeEncryptionCertIDForMSOE)
throws java.security.NoSuchAlgorithmException
Calling this method also initializes the digest computation by wrapping a
digest stream for the digest algorithm around the data carrying input
stream. When creating the SignerInfo object for the signer, the
following attributes are set for the SignerInfo structure:
encryptionCertID is not null
encryptionCertID is not null and
includeEncryptionCertIDForMSOE allowing MSOE to recognize
the encryption certificate is different certs are used for signing
and encryption
Inclusion of a special private Microsoft signed attribute (type: 1.3.6.1.4.1.311.16.4)
for identifying the sender´s encryption certificate by IssuerAndSerialNumber
might be useful to tell Outlook Express the encryption certificate to be used if
separate certificates are used for signing and encryption. If you want to include
this attribute, set includeEncryptionCertIDForMSOE to true
and supply the IssuerAndSerialNumber of the encryption certificate ("encrypter").
privateKey - the (RSA) private key which shall be used for signingsigner - IssuerAndSerialNumber from the certificate which must be used
for verifying the signaturedigestAlgorithm - the digest algorithm; default: SHA-1 (used, if null)signatureAlgorithm - the signature algorithm; default: rsaEncryption (used, if null);
Attention! use AlgorithmID.rsaEncryption for RSA signing!encrypter - the identifier of the encryption certificate of the
sender by the SMIMEEncryptionKeyPreference attribute (or
null if signing and encryption cert are the
same or no encryption certificate shall be indicated)includeEncryptionCertIDForMSOE - if true and an
encryptionCertID of type IssuerAndSerialNumber is provided,
a private MS attribute will be included allowing MSOE to recognize
the encryption cert of the signer if using different certs for
signing/encryptionjava.security.NoSuchAlgorithmException - if no implementation of the requested
message digest algorithm is availablepublic void addSigner(java.security.PrivateKey privateKey,
IssuerAndSerialNumber signer,
AlgorithmID digestAlgorithm,
AlgorithmID signatureAlgorithm,
Attribute[] signedAttributes)
throws java.security.NoSuchAlgorithmException
Calling this method also initializes the digest computation by wrapping a digest stream for the digest algorithm around the data carrying input stream.
Please note that no signed attributes are created by this method (as done by the
other addSigner methods. This method sets the supplied attributes
for the SignerInfo to be created for the signer.
privateKey - the private key which shall be used for signingsigner - IssuerAndSerialNumber from the certificate which must be used
for verifying the signaturedigestAlgorithm - the digest algorithm; default: SHA-1 (used, if null)signatureAlgorithm - the signature algorithm; default: rsaEncryption (used, if null);
Attention! use AlgorithmID.rsaEncryption for RSA signing!java.security.NoSuchAlgorithmException - if no implementation of the requested
message digest algorithm is availablepublic X509Certificate verify() throws java.security.SignatureException
This method may be used for verifying the signature when only one signer is included.
java.security.SignatureException - if this message does not verify or the signer
certificate is not included in the messagepublic void verify(java.security.PublicKey publicKey)
throws java.security.SignatureException
This method may be used for verifying the signature when only one signer is included.
publicKey - the public key of the signer to verify the messagejava.security.SignatureException - if this message does not verifypublic int getMode()
getMode in class SignedDataStreampublic ASN1Object toASN1Object() throws CMSException
toASN1Object in interface ContentStreamtoASN1Object in class SignedDataStreamCMSException - if the ASN1Object could not be createdpublic void writeTo(java.io.OutputStream os)
throws java.io.IOException
writeTo in class SignedDataStreamos - the output stream to which this SMimeSigned shall be writtenjava.io.IOException - if an error occurs while writing to the streampublic void writeTo(java.io.OutputStream os,
int blockSize)
throws java.io.IOException
blockSize parameter indicates the block size to
be used for performimg block encoding.writeTo in class SignedDataStreamos - the output stream to which this SMimeSigned shall be writtenblockSize - the block size for performing block encodingjava.io.IOException - if an error occurs while writing to the streampublic java.lang.String toString()
toString in class SignedDataStreampublic void requestTimeStamp(TimeStampClient timeStampClient)
This API can be used following any of the addSigner()
APIs to ensure that the signature that was just added is time-stamped.
When this SMimeSigned structure is encoded to an ASN1 object, a time-stamp will automatically be requested for the signature from the TimeStamp Authority. It will then be added to the SignerInfo structure that represents the signature, causing the signature to be time-stamped.
A time-stamp will NOT be requested under the following circumstances:
timeStampRequestInfo parameter is
nullnulltimeStampClient - the time-stamp client that will be used to request a time-stamp for
the signature value from a TimeStamp Authoritypublic void requestTimeStamp(TimeStampClient timeStampClient, int signerInfoIndex)
When this SMimeSigned structure is encoded to an ASN1 object, a time-stamp will automatically be requested for the signature from the TimeStamp Authority. It will then be added to the SignerInfo structure that represents the signature, causing the signature to be time-stamped.
A time-stamp will NOT be requested under the following circumstances:
timeStampRequestInfo parameter is
nullnulltimeStampClient - the time-stamp client that will be used to request a time-stamp for
the signature value from a TimeStamp AuthoritysignerInfoIndex - the index of the signature that is to be time-stamped