Create a CMS enveloped-data object for one or more recipients.
Public Declare Function CMS_MakeEnvData Lib "diCrPKI.dll"
(ByVal strFileOut As String, ByVal strFileIn As String,
ByVal strCertList As String, ByVal strKeyString As String, ByVal nCount As Long,
ByVal nOptions As Long) As Long
nRet = CMS_MakeEnvData(strFileOut, strFileIn,
strCertList, strKeyString, nCount, nOptions) As Long
long __stdcall CMS_MakeEnvData(const char *szFileOut, const char *szFileIn, const char *szCertList, const char *szKeyString, long nCount, long nOptions);
Special cases: Set as "type=@pwri"
to create a single recipientInfo of the PasswordRecipientInfo
(pwri) type;
or set as "type=@kekri,keyid=<string>"
to create a single recipientInfo of the KEKRecipientInfo
(kekri) type. See Remarks.
"#x<hex-digits>"
to pass a string of arbitrary octet values.
Required for pwri and kekri types.
Examples: "abc"
will pass the 3 bytes (0x61, 0x62, 0x63);
"#xdeadbeef01"
will pass the 5 bytes (0xde, 0xad, 0xbe, 0xef, 0x01).
rsaEncryption
for key transport or
ECDH with ANSI-X9.63-KDF using SHA-1 with cms3DESwrap
for key agreement; and
aes128-CBC
for content encryption, and output in binary format.
NOTE: some default algorithms [changed in v23.0]
To set the content encryption algorithm (default is aes128-CBC
), add one of the following:
PKI_BC_AES128 to use aes128-CBC
for content encryption (default as of [v23.0])
PKI_BC_AES192 to use aes192-CBC
for content encryption
PKI_BC_AES256 to use aes256-CBC
for content encryption
PKI_BC_3DES to use Triple-DES des-EDE3-CBC
for content encryption [legacy]
PKI_AEAD_AES_128_GCM to use aes128-GCM
for content encryption and authentication
PKI_AEAD_AES_192_GCM to use aes192-GCM
for content encryption and authentication
PKI_AEAD_AES_256_GCM to use aes256-GCM
for content encryption and authentication
PKI_AEAD_CHACHA20_POLY1305 to use AEAD_CHACHA20_POLY1305
for content encryption and authentication
To set the key transport scheme or key encapsulation mechanism (KEM) for RSA, use one of
PKI_KT_RSAES_PKCS (0) to encrypt the key using the RSAES-PKCS1-v1_5 encryption scheme rsaEncryption
(default)
PKI_KT_RSAES_OAEP to encrypt the key using the RSAES-OAEP encryption scheme (see RSA signature and encryption schemes).
PKI_KEM_RSA [New in v23.0] to encrypt the key using the RSA Key Encapsulation Mechanism (RSA-KEM) algorithm
(see RSA-KEM).
To select a hash function where applicable, add one of:
PKI_HASH_SHA1 to use SHA-1 (default - CAUTION)
PKI_HASH_SHA224 to use SHA-224
PKI_HASH_SHA256 to use SHA-256 [minimum recommended]
PKI_HASH_SHA384 to use SHA-384
PKI_HASH_SHA512 to use SHA-512
If you have selected PKI_KT_RSAES_OAEP then, optionally, add
PKI_MGF_MGF1SHA1 to force the MGF hash function to be SHA-1 (default = same as encoding hash function set above)
To set the key derivation function (KDF) (where applicable), add one of:
PKI_KDF_X963 to use the ANSI-X9.63-KDF key derivation function (default for ECDH).
PKI_KDF_HKDF to use the HMAC-based Key Derivation Function (HKDF) from [RFC5869]
PKI_KDF_KDF2 to use KDF2 from ANSI-X9.44 [X9-44]
PKI_KDF_KDF3 to use KDF3 from ANSI-X9.44 (default for RSA-KEM)
and add the key wrap algorithm for the ECDH key agreement scheme or the kekri key encryption algorithm or RSA-KEM.
Default (0) is to match the key agreement algorithm to the content encryption algorithm:
PKI_KWRAP_3DES to use cms3DESwrap
[legacy - only valid if Triple DES used for the content encryption algorithm].
PKI_KWRAP_AES128 to use aes128-wrap
PKI_KWRAP_AES192 to use aes192-wrap
PKI_KWRAP_AES256 to use aes256-wrap
If successful, the return value is the number of successful recipients; otherwise it returns a negative error code.
Public Function cmsMakeEnvData
(szFileOut As String, szFileIn As String, szCertList As String, Optional szKeyString As String = "", Optional nOptions As Long = 0, Optional nCount As Long = 0) As Long
Cms.MakeEnvData Method ( String, String, String, CipherAlgorithm, Cms.EnvDataOptions )
Cms.MakeEnvData Method ( String, String, String, CipherAlgorithm, Cms.KeyEncrAlgorithm, HashAlgorithm, Cms.EnvDataOptions, Kdf.KdfAlg, Kdf.KeyWrapAlg, String, Int32, Cms.ContentEncrAlg )
static int dipki::Cms::MakeEnvData (const std::string &outputFile, const std::string &inputFile, const std::string &certList, CipherAlg cipherAlg=CipherAlg::Default, KeyEncrAlg keyEncrAlg=KeyEncrAlg::Default, HashAlg hashAlg=HashAlg::Default, EnvDataOptions advOpts=EnvDataOptions::Default_EnvDataOpt, Format format=Format::Default, bool bigFile=false, Kdf::KdfAlg kdfAlg=Kdf::KdfAlg::X963, Kdf::KeyWrapAlg keyWrapAlg=Kdf::KeyWrapAlg::Default, const std::string &keyString="", int count=0)
static int dipki::Cms::MakeEnvData (const std::string &outputFile, const std::string &inputFile, const std::string &schemeType, const std::string &keyString, CipherAlg cipherAlg=CipherAlg::Default, Kdf::KeyWrapAlg keyWrapAlg=Kdf::KeyWrapAlg::Default, EnvDataOptions advOpts=EnvDataOptions::Default_EnvDataOpt, Format format=Format::Default, HashAlg hashAlg=HashAlg::Default, int count=0)
static Cms.make_envdata(outputfile, inputfile, certlist, cipheralg=ContentEncrAlg.DEFAULT, keyencralg=KeyEncrAlg.DEFAULT, hashalg=0, opts=EnvDataOpts.DEFAULT, bigfile=False, kdfalg=Kdf.KdfAlg.X963, keywrapalg=0, keyString="", count=0)
The output is a file containing a CMS EnvelopedData
or AuthEnvelopedData
object which can be sent as part of an S/MIME message or used directly as a binary .p7m file.
The supported objects are those described in CMS Content Types.
RecipientInfo types can be KeyTransRecipientInfo
(ktri), KeyAgreeRecipientInfo
(kari), KEKRecipientInfo
(kekri) and PasswordRecipientinfo
(pwri).
If the content encryption algorithm is an authenticated encryption algorithm (AES*-GCM
), then the output will be an Authenticated-Enveloped-Data object (AuthEnvelopedData
) as specified in
[RFC5083] and [RFC5084].
TL;DR. The RecipientInfo type will be set according to the type of public key in the X.509 certificates. If the certificate has an RSA key, then a key-transport RecipientInfo (ktri) will be created (default PKCS#1v1.5 or RSA-OAEP if flag set). If the certificate contains a supported ECC public key, then a key-agreement type (kari) will be used (default ANSI-X9.63-KDF and keywrap to match the content encryption algorithm, HKDF if flag set). You can pass a list of mixed certificates and the appropriate RecipientInfo type will be chosen for each. There are special cases for KEK and password recipientInfo types - see below.
By default, the RecipientInfo
type is set automatically depending on the public key found in each certificate in szCertList,
one for each certificate.
If the public key is RSA (rsaEncryption) then the key transport technique (ktri) will be used to create that particular recipientInfo
.
If the public key is a supported ECC key, then the standard ECDH ephemeral-static key agreement technique (kari) will be used as per [RFC5753] and [RFC8418].
No checks are made on the validity period of the recipients' X.509 certificates or their key usage attributes. A PKCS#7 certificate chain file may be specified as an argument for szCertList. In which case, all certificates in the chain file will be used as recipients.
It is an error if any specified certificate file in szCertList is missing or corrupted. Call PKI_LastError() to find more details of the errors that occurred.
[New in v20.6]
RecipientInfo types KEKRecipientInfo
(kekri) and PasswordRecipientinfo
(pwri) are supported for a single recipient only.
For these types no certificates are required and the szCertList argument must include "type=@kekri"
or "type=@pwri"
, respectively, all characters lower case.
In addition, for a kekri type, this argument should be of the form "type=@kekri,keyid=<string>"
(Note separated by a comma ',')
where the value for the keyid attribute is used to set the keyIdentifier
field in the KEKIdentifier
.
This can be a simple ASCII string or can use the format "#x<hex-digits>"
to pass a string of arbitrary octet values. If omitted, the default keyIdentifier
is "keyid".
Optionally add a date
field in the KEKIdentifier
by adding a date=<iso-date-string>
,
e.g. "type=@kekri,keyid=ourkeyid,date=2022-07-31T12:01"
.
The szKeyString argument must include either the key encryption key (KEK) for kekri or the password for pwri.
This parameter is expected to be an ASCII string. A binary KEK may be passed by using the format "#x<hex-digits>"
to pass a string of arbitrary octet values.
Examples
nRet = CMS_MakeEnvData("xscipher_kekri.p7m", "plain.txt", "type=@kekri,keyid=oursharedkey", "#x0123456789ABCDEFF0E1D2C3B4A59687", 0, PKI_BC_AES192 Or PKI_KWRAP_AES128);
Creates an EnvelopedData file with a single recipientInfo of type KEKRecipientInfo
(kekri) with a keyid of "oursharedkey" using the 128-bit KEK
(0x)0123456789ABCDEFF0E1D2C3B4A59687
wrapped with aes128-wrap
. The content is encrypted with AES-192 using a random content encryption key.
nRet = CMS_MakeEnvData("xscipher_pwri.p7m", "plain.txt", "type=@pwri", "This is my password", 6666, PKI_BC_AES256);Creates an EnvelopedData file with a single recipientInfo of type
PasswordRecipientinfo
(pwri) using the password "This is my password".
The iteration count for the KDF is 6666 (default=4096). The content is encrypted with AES-256 using a random content encryption key.
Use the PKI_CMS_BIGFILE
option flag to handle large files more efficiently.
This results in improvements of speed typically of 30% and enables, in theory, unlimited sizes of enveloped files
(subject, of course, to your level of boredom in waiting for the output). Binary output only. Not available for authenticated encryption or kekri or pwri recipientInfo types.
Specialist Option: If the PKI_CMS_ALT_ALGID
option flag is present, an alternative
Content Encryption Algorithm Identifier will be used as follows:
Default Content Encryption Algorithm Identifier | with PKI_CMS_ALT_ALGID |
---|---|
des-ede3-cbc (1.2.840.113549.3.7) | des_3CBC_pad (1.3.36.3.1.3.2.1) |
This alternative TeleTrusT algorithm identifier was added for compatibility with certain (superseded?) German Health profiles.
The following example reproduces example 5.1 from [SMIME-EX].
It creates an EnvelopedData
CMS object for Bob from the data file
excontent.txt
using Bob's X.509 certificate BobRSASignByCarl.cer
.
Dim nRet As Long Dim strOutputFile As String Dim strInputFile As String Dim strCertFile As String strOutputFile = "cmsalice2bob.p7m" strInputFile = "excontent.txt" strCertFile = "BobRSASignByCarl.cer" ' This should return 1 (indicating one successful recipient) nRet = CMS_MakeEnvData(strOutputFile, strInputFile, strCertFile, "", 0, 0) Debug.Print "CMS_MakeEnvData returns " & nRet
Note that the output file will always be different from the smime-example because the content-encryption key, IV and the encrypted-content will be different each time.
The next example does the same except for two recipients: Bob and Carl.
' This should return 2 (indicating two successful recipients) nRet = CMS_MakeEnvData("cms2bobandcarl.p7m", "excontent.txt", _ "BobRSASignByCarl.cer;CarlRSASelf.cer", "", 0, 0) Debug.Print "CMS_MakeEnvData returns " & nRet
The third example is the same as the first, except it uses AES-128 instead of Triple-DES to encrypt the content.
nRet = CMS_MakeEnvData("cms2bob_aes128.p7m", "excontent.txt", _ "BobRSASignByCarl.cer", "", 0, PKI_BC_AES128) Debug.Print "CMS_MakeEnvData returns " & nRet
This example shows how to use the PKI_CMS_BIGFILE
option.
Dim nRet As Long Dim strEnvDataFile As String Dim strInputFile As String Dim strCheckFile As String strEnvDataFile = "envbig.p7m" strInputFile = "bigfile.txt" Debug.Print "File " & strInputFile & "=" & FileLen(strInputFile) & " bytes" ' Make enveloped-data file: this should return 1 (indicating one successful recipient) nRet = CMS_MakeEnvData(strEnvDataFile, strInputFile, "bob.cer;", "", 0, PKI_CMS_BIGFILE) Debug.Print "CMS_MakeEnvData returns " & nRet & " (expected 1 => # recipients)" ' Now decrypt using Bob's private key Dim strPrivateKey As String strPrivateKey = rsaReadPrivateKey("bob.p8e", "password") strCheckFile = strEnvDataFile & ".out.txt" nRet = CMS_ReadEnvData(strCheckFile, strEnvDataFile, "", strPrivateKey, PKI_CMS_BIGFILE) Debug.Print "CMS_ReadEnvData returns " & nRet & " (expected 0 => success)" ' Check file length Debug.Print "File " & strCheckFile & "=" & FileLen(strCheckFile) & " bytes"
File bigfile.txt=10485700 bytes CMS_MakeEnvData returns 1 (expected 1 => # recipients) CMS_ReadEnvData returns 0 (expected 0 => success) File envbig.p7m.out.txt=10485700 bytes
Dim n As Long ' Create an enveloped CMS object (ktri type) to Bob using Bob's RSA key... n = cmsMakeEnvData("cms2bob_aes128.p7m", "excontent.txt", "BobRSASignByCarl.cer", "", PKI_BC_AES128 Or PKI_KT_RSAES_OAEP) ' Same but using authenticated encryption and creating an authEnvelopedData object... n = cmsMakeEnvData("cms2bob_aes128auth.p7m", "excontent.txt", "BobRSASignByCarl.cer", "", PKI_AEAD_AES_128_GCM Or PKI_KT_RSAES_OAEP) ' Create an enveloped CMS object (kari type) to Dana using Dana's ECC key... n = cmsMakeEnvData("cms2dana_hkdf.p7m", "excontent.txt", "lamps-dana.encrypt.crt", "", PKI_BC_AES256 Or PKI_HASH_SHA256 Or PKI_KDF_HKDF Or PKI_KWRAP_AES256) ' Create an enveloped CMS object (ktri + kari type) to Bob and Dana using respective RSA and ECC keys... n = cmsMakeEnvData("cms2bob_dana.p7m", "excontent.txt", "BobRSASignByCarl.cer;lamps-dana.encrypt.crt", "", PKI_BC_AES256 Or PKI_KT_RSAES_OAEP Or PKI_HASH_SHA256 Or PKI_KDF_HKDF) ' Create an enveloped CMS object (kekri type) using a previously distributed symmetric key-encryption key (KEK)... n = cmsMakeEnvData("cms_envdata_kekri.p7m", "excontent.txt", "type=@kekri,keyid=ourcommonkey", "#x0123456789ABCDEFF0E1D2C3B4A59687", PKI_BC_AES256 Or PKI_HASH_SHA256 Or PKI_KWRAP_AES128) ' Create an enveloped CMS object (pwri type) using password-based key management... n = cmsMakeEnvData("cms_envdata_pwri.p7m", "excontent.txt", "type=@pwri", "password12345", PKI_BC_AES192)
CMS_MakeEnvDataFromString CMS_MakeEnvDataFromBytes