[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: PKCS#7 in PKIX-3
Death Rays from Mars made Housley, Russ <housley@spyrus.com> write:
>I think that we are near concensus on this point. A single encapsulation
>mechanism would be ideal. PKIX Part 3 contains one suggestion, and PKCS #7
>contains another. Carlise provided an analysis for the PKIX Part 3 approach.
>PKCS #7 has a market share that should not be ignored, and the specification
>is open for reivew and update.
Just to further muddy the water, I've been using a format which is (IMHO) an
improvement on PKCS #7 (it fixes a few problems in the PKCS format and is
easier to use). In case anyone's interested, I've included the ASN.1
specification for the encapsulation below.
Peter.
-/ The basic content type. PKCS #7 defines content as a sequence of { OID,
ANY DEFINED BY } and then nests one of a number of different grand unified
data types (signed, enveloped, signedAndEnveloped, etc) inside this. This
doesn't allow for useful extensions such as compressed data or (currently)
signed then encrypted data, and has the nasty problem of a combinatorial
explosion of data types (which is also endemic among other RSADSI products
which use grand unified types) such as SignedAndEncryptedButNotCompressed,
SignedAndEncryptedAndCompressed, SignedAndEncrypted, EncryptedAndSigned,
etc etc. A neater solution is to provide at the start of the content a
SEQUENCE OF ContentInfo records which define the transformations to be
applied to the content. The contentInfo field encodes in one step a
transformation which would normally be encoded using nested ASN.1 objects,
so that for example the transformation:
Sign( Compress( data ))
would be encoded as:
Sign, Compress( data )
The reason for trying to reduce the level of nesting as much as possible
is that nested, indefinite-length ASN.1 data types are messy to encode and
lead to unnecessary message expansion. This form of encapsulation is
known as "logical wrapping", in which a single physical level of wrapping
is used to provide the equivalent of multiple logical levels of wrapping.
The sequence of ContentInfo fields encodes the progression of levels in
which the data is encased. For example a contentInfo field of { Signed,
Compressed } would specify that the content should be signature checked
and then decompressed to recover the original data (ie the compressed data
has been signed). In contrast a contentInfo field of { Compressed, Signed
} would specify that the content should be decompressed and then signature
checked (ie the decompressed data has been signed).
This system can be used to encode arbitrary transformations on the
content, with arbitrary logical nesting and an arbitrary processing order.
One disadvantage of the one-size-fits-all basic content type is that two
fields which are specific to two of the content types must be added. The
signatures field is used for the Signed data type to contain the
signatures on the data (which may be empty if the signatures are stored
separately), and the padding field is used for the Encrypted data type to
contain optional message padding /-
Content ::= SEQUENCE {
contentInfo SEQUENCE OF ContentInfo,
-- Transformation to apply to the content
content OCTET STRING, -- Data payload
signatures [0] SET OF Signature OPTIONAL, -- Signature(s) on the data
padding [1] OCTET STRING OPTIONAL -- Data padding
}
ContentInfo ::= CHOICE {
nestedData [0] IMPLICIT NestedDataInfo,
encryptedData
[1] IMPLICIT EncryptedDataInfo,
signedData [2] IMPLICIT SignedDataInfo,
compressedData
[3] IMPLICIT CompressedDataInfo,
...
}
-/ Nested data. This specifies that the data payload contains nested content
which should be further processed. This is usually used after the
encryptedData type to specify that the decrypted data should be further
processed. The use of a BOOLEAN is somewhat unnecessary since the mere
presence of the nestedData field is enough to convey the necessary
information, however providing the field and setting the value to FALSE
can also convey the information "I want to bloat up the header
unnecessarily" /-
NestedDataInfo ::= BOOLEAN
-/ Encrypted data. The encryptionKeyInfo contains the session key and bulk
data encryption algorithm details, encrypted with zero or more public or
conventional key encryption keys. If the set is empty, the session key
must be supplied by some other means. If the set is nonempty, the session
key may be reconstructed by decrypting one of the EncryptionKeyInfo
records with the appropriate private key or conventional KEK. Public and
conventional KEK's may be mixed, so that a single message could be
decrypted with one or more conventional and/or private keys.
The IV is always present as a fixed-length string to obscure the details
of the algorithm and mode being used. If necessary it is extended to more
than CRYPT_MAX_IVSIZE bytes by padding to the right with 0 bits /-
EncryptedDataInfo ::= SEQUENCE {
encryptionKeyInfo
SET OF EncryptionKeyInfo,
-- Encrypted seesion key, may be empty set
keyCookie [0] IMPLICIT KeyCookie OPTIONAL, -- Key cookie
iv OCTET STRING (SIZE(CRYPT_MAX_IVSIZE)) -- IV
}
-/ Signed data. The digestAlgorithms field contains information on which
types of message digest to compute for the data in order to allow the
signature check to be performed once the end of the data has been reached.
This is stored before the data itself to allow for one-pass processing.
The signatureCookie field is used to tie the signed data to the signature
or signatures associated with it if they are stored separately /-
SignedDataInfo ::= SEQUENCE {
digestAlgorithms
DigestAlgorithmIdentifiers,
-- Information on digests for sig.check
signatureCookie
[0] IMPLICIT SignatureCookie OPTIONAL -- Signature cookie
}
-/ Compressed data. This provides information on the compression algorithm
used and any necessary parameters. Unfortunately the only ISO-approved
algorithms are peculiar, usually patented or otherwise restricted ones
(check if Zip has an OID) /-
CompressedDataInfo ::= SEQUENCE {
compressionAlgorithm
CompressionAlgorithmIdentifier -- Compression algorithm
}