When using openssl cms -encrypt -pwri_password
, it follows the process described in RFC 3211, which passes the user-provided password into a KDF, but then, rather than using the output of that KDF to encrypt the content, it instead uses that key as a KEK to encrypt the actual content-encryption key (CEK), which is then bundled alongside the content:
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.7.3 envelopedData (PKCS #7)
[0] (1 elem)
SEQUENCE (3 elem)
INTEGER 3
SET (1 elem)
[3] (4 elem)
INTEGER 0
[0] (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.5.12 pkcs5PBKDF2 (PKCS #5 v2.0)
SEQUENCE (2 elem)
OCTET STRING (8 byte) D81093AEE45462EE
INTEGER 2048
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.9.16.3.9 pwriKEK (S/MIME Algorithms)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.3.7 des-EDE3-CBC (RSADSI encryptionAlgorithm)
OCTET STRING (8 byte) BB96EE7A71BA5792
OCTET STRING (32 byte) 569E1E845BA33D24D4243ED28B265B0974C486B813E6B9582B014D7E53DD01B9
SEQUENCE (3 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.7.1 data (PKCS #7)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.3.7 des-EDE3-CBC (RSADSI encryptionAlgorithm)
OCTET STRING (8 byte) 05AD3B1BDCB767CC
[0] (16 byte) 7FA32912ECCCD7C421D4F122FD1ED172
The specification states (emphasis added):
§1.2.1 Rationale
Password-based key wrapping is a two-stage process, a first stage in which a user-supplied password is converted into a KEK if required, and a second stage in which the KEK is used to encrypt a CEK.
But I'm unclear on why password-based CMS encryption is done via this two-stage process. Does it somehow protect against future to-be-discovered vulnerabilities in the KDF?
The KDF already has salt as-implemented, so its output is clearly already secure against e.g. rainbow tables— so what is the advantage behind this password-derived-KEK-with-bundled-encrypted-CEK model over simply using the output of the KDF directly as the CEK?