CryptoSys PKI is a programmer's toolkit for Win32 systems that enables the user to create and read secure cryptographic messages encrypted or signed using RSA public key encryption.
You can create and read both enveloped-data (encrypted) and signed-data Cryptographic Message Syntax (CMS, PKCS#7) objects, which you can use in S/MIME email messages. You can verify the digital signature in a signed-data CMS object; generate and manage RSA public and private keys; carry out "raw" RSA encryption and decryption, and create and read X.509 certificate files.
Other utilities included in the toolkit are the ability to generate message digest hash values using SHA-1, MD5, MD2, SHA-256, SHA-384, SHA-512 and [new in version 3.2] SHA-224; generate HMAC keyed-hash message authentication values, wipe files using 7-pass DOD standards, generate cryptographically-secure random numbers to the strict NIST SP800-90 standard, prompt for a password, and convert to and from base64- and hexadecimal-encoded formats.
Public Key Infrastructure (PKI) is defined as
The set of hardware, software, people, policies and procedures needed to create, manage, store, distribute, and revoke Public Key Certificates based on public-key cryptography. -[PKIX-MAP]The CryptoSys PKI toolkit provides programmers and developers with some of the more useful algorithms you need to create the software for a true PKI. We have appropriated a well-known three-letter-acronym. It's up to you to manage the hardware, people, policies, procedures and the overall software security you require.
We have used S/MIME Version 3 Message Specification [SMIME-MSG], and Cryptographic Message Syntax (CMS) [CMS] together with the relevant PKCS documents as our primary reference documents. CMS is a stricter subset of PKCS#7 [PKCS7] and is compatible with it.
Our goal has been to provide developers and programmers with a useful set of functions, not necessarily an exhaustive set (see Design Philosophy). We have excluded a few of the required ("MUST") algorithms. For example, this release of the toolkit only includes the RSA signature algorithm not DSA and does not support CRLs. UTF-16 Unicode and MBCS character sets are not explicitly supported, but usually can be managed by suitable encoding into a byte array before using the toolkit functions. However, it can verify X.509 certificates certain signedData objects signed using DSA and UTF-8 support is provided for X.509 distinguished name strings.
The CMS (PKCS#7) objects produced by this toolkit should be readable by S/MIME-compatible email clients like Microsoft Outlook Express if they are wrapped in MIME-conformant email messages. No MIME or email facilities are provided with the toolkit. You need to use a separate program to create, send and read MIME email messages. The X.509 certificate tools should be compatible with typical certificates issued by Verisign and Thawte. The certificate requests it creates are accepted by Verisign's test facility, provided you include the distinguished name attributes they require. However, there is only limited support for Unicode character sets like UTF8String and BMPString. And, sorry, there are no facilities to add an MPEG video of you playing with your cat into an X.509 certificate.
To get started, read the sections on Installation and General Programming Issues and the section on your programming language:
CryptoSys PKI uses a straightforward Win32 DLL which is compatible with all versions of 32-bit Windows® (95/98/Me/NT/2K/XP/2003/Vista). There is no "COM", no "Active-X", and no requirement to "register" it with Windows to use it. The installed executable has a small footprint of about 350 KB. Developers can easily distribute it with their projects made in Visual Basic, VBA, C, C++, VB.NET or C# (in fact, in any other programming language that will let you call Win32 API functions). For more information, see Technical Details.
Changes in version 3.2:
CIPHER_Bytes,
CIPHER_Hex, and
CIPHER_File.
HASH_HexFromHex
and HMAC_HexFromHex functions.
shaXXXWithRSAEncryption"
with SHA-224/256/384/512
for
X509_MakeCert[Self]
and
X509_CertRequest.
CMS_MakeSigData[FromString].
CMS_MakeEnvData[FromString].
CMS_MakeEnvData.
RSA_KemWrap and
RSA_KemUnwrap which will wrap (encrypt) and unwrap (decrypt)
secret keying data for a recipient with the recipient's RSA key using the
RSA-KEM ("Simple RSA") algorithm;
and added the block cipher key wrap functions
CIPHER_KeyWrap
and CIPHER_KeyUnwrap
using AES-wrap and Triple DES wrap.
(Note that the function name is RSA_KemWrap, not KeyWrap.)
RSA_SaveEncPrivateKey and
RSA_ReadEncPrivateKey
functions.
CMS_QueryEnvData function,
and included the ability to pass a base64- or PEM-encoded certificate list to
CMS_MakeEnvData and
CMS_MakeSigData.
Changes in version 3.1:
X509_MakeCert or X509_MakeCertSelf.
See Specifying Distinguished Names for more details.
RSA_KeyMatch
function to verify that a pair of RSA private and public key strings are matched.
TDEA_File.
To prevent accidental misuse, if an error occurs when using this function, the output file will now not exist.
CMS_MakeEnvData function to conform with the
PKI requirements of the German Health System.
Changes in version 3.0:
RSA_KeyHashCode
function to allow comparison of internal key strings.
HASH functions.HMAC functions to compute a keyed hash value,
HMAC_HexFromBytes and
HMAC_Bytes.
X509_KeyUsageFlags and
X509_QueryCert.
X509_ReadStringFromFile and
X509_SaveFileFromString.
Changes in version 2.9:
CMS_MakeSigDataFromSigValue
function to create a SignedData
object directly from a pre-computed signature value.
CNV_CheckUTF8
function to check whether a string contains only valid UTF-8 characters.TDEA_BytesMode.
RNG_Number function to generate
a random number in a given range.
Changes in version 2.8:
X509_GetCertFromP7Chain and
X509_GetCertFromPFX.
CMS_VerifySigData and
CMS_QuerySigData functions.
nMajor and nMinor in
PKI_Version.
Changes in version 2.7:
X509_VerifyCert()
can now verify certificates signed using DSA.
RSA_ReadEncPrivateKey()
to read PKCS#8 files encrypted with RC2.
CMS_ReadEnvData()
and
CMS_ReadEnvDataToString()
more tolerant of different input formats, including adding support to read
data encrypted with RC2.RSA_MakeKeys()
and
RSA_SaveEncPrivateKey().
RSA_MakeKeys().
X509_MakeCert()
and
X509_MakeCertSelf()
UTF8String and to decode multi-byte distinguished names
into 8-bit ASCII, if possible.
Except where otherwise noted, the CryptoSys PKI toolkit program, its executables, sample source code and this document were written by David Ireland and are copyright (c) 2002-8 by DI Management Services Pty Limited, all rights reserved. They may not be distributed or reproduced separately by any means whatsoever without express permission in writing. Users holding a valid develeoper's licence are permitted to distribute the executables as part of a value-added application according to the terms of their licence.
The latest version of CryptoSys PKI may be obtained from <http://www.cryptosys.net/pki/>.
There is no theory explained here. We assume you know what you are doing and have read and understood at least the relevant reference documents. For a good introduction to the concepts, try William Stallings, Cryptography and Network Security: Principles and Practice [STAL02]. For a more detailed work, see Some Examples of the PKCS Standards by RSA Laboratories [PKCS-EX] and the primary references we used.
You are assumed to have a reasonable knowledge of the basics of cryptography, public key encryption, and Secure/MIME electronic messaging, and that you can program in the language of your choice to an advanced standard. If you don't understand what this is trying to do, you really shouldn't be using it.
You must understand the difference between binary data and base64-encoded data and know how to read them, and you must understand the difference between a CMS object and a MIME body.
It will also help that you:
DUMPASN1 program.
rsaEncryption").kem-rsa" within a "ac-generic-hybrid")
[provisonally added in v3.2].
sha1WithRSAEncryption" (default)md5WithRSAEncryption"md2WithRSAEncryption"sha224WithRSAEncryption" [added v3.2]sha256WithRSAEncryption" [added v3.2]sha384WithRSAEncryption" [added v3.2]sha512WithRSAEncryption" [added v3.2]dsaWithSha1" and
DSA public key "DSAPublicKey" for verifying X.509 certificates and CMS SignedData objects only
(but inherited DSS parameters are not supported).des-EDE3-CBC" (default)aes128-CBC"aes192-CBC"aes256-CBC"aes128-Wrap" (default)aes192-Wrap"aes256-Wrap"cms3DESWrap"sha1" or "sha-1" (default)md5"md2"sha224" [added v3.2]sha256"sha384"sha512"We keep MD2 here so we can reproduce the examples from RSA Laboratories' 1993 paper [PKCS-EX] and because we still find the odd X.509 certificate using it. You are recommended to use at least SHA-1 in new applications.
HMAC_ functions.
hmacWithSHA1"hmacWithSHA224" [added v3.2]hmacWithSHA256"hmacWithSHA384"hmacWithSHA512"hmacWithMD5"RSA_SaveEncPrivateKey and RSA_MakeKeys functions:
pbeWithSHAAnd3-KeyTripleDES-CBC" (default)pbeWithMD5AndDES-CBC"pbeWithMD2AndDES-CBC"pbeWithSHA1AndDES-CBC"pkcs5PBES2" with
des-EDE3-CBC"aes128-CBC" [added v3.2]aes192-CBC" [added v3.2]aes256-CBC" [added v3.2]RSA_ReadEncPrivateKey function:
pkcs5PBES2" with "desCBC"pkcs5PBES2" with "rc2CBC"pbeWithSHAAnd128BitRC2-CBC"pbeWithSHAAnd40BitRC2-CBC"pbeWithMD5AndRC2-CBC"pbeWithMD2AndRC2-CBC"pbeWithSHA1AndRC2-CBC"RSAPublicKey (default for public keys)
PrivateKeyInfo" with "rsaEncryption"
EncryptedPrivateKeyInfo" with "rsaEncryption" (default for private keys)
EnvelopedData content type (CMS version 0) with EncryptedContentInfo.
SignedData content types (CMS version 1) with optional
signed attributes:
SignedData suitable for use in S/MIME
"application/pkcs7-mime" messages.multipart/signed" messages.id-data inner content type are supported.
The RecipientIdentifier must be issuerAndSerialNumber.
rfc822Name subject alternative name extension can be added.DHPublicKey")id-keyExchangeAlgorithm")RSAPublicKey as per PKCS#1 for public keysEncryptedPrivateKeyInfo as per PKCS#8 for private keys.PrivateKeyInfo format for private keys are also supported.PrivateKeyInfo form at your own risk.String type
or a zero-terminated char string in C makes for a simpler life than dealing with binary data.
Note there is deliberate half-second delay when reading an encrypted private key file.
WIPE_Data to erase them when no longer needed.
RSA_ToXMLString and
RSA_FromXMLString functions to convert back and forth
between XML and the toolkit's internal string format. Be very careful as the XML private key data is not encrypted.
Dim strData As String Dim nRet As Long strData = "Hello world" Debug.Print strData ' ... etcCode in C/C++ is shown as:
char *str = "Hello world";
printf("%s\n", str);
Code in VB.NET is shown as:
Dim nDataLen As Integer Dim abData() As Byte If strData.Length = 0 Then Exit Function abData = System.Text.Encoding.Default.GetBytes(strData) nDataLen = abData.LengthCode in C# is shown as:
public static string ToHex(byte[] binaryData)
{
int nBytes = binaryData.Length;
int nChars = 2 * nBytes;
if (nBytes == 0) return String.Empty;
StringBuilder sb = new StringBuilder(nChars);
nChars = CNV_HexStrFromBytes(sb, nChars, binaryData, nBytes);
return sb.ToString(0, nChars);
}
Output from code samples is shown as:
Result=OKAll functions called directly in the CryptoSys PKI toolkit begin with 3 or 4 capital letters followed by an underscore "_", e.g.
nRet = RSA_ReadPublicKey(rsaReadPublicKey, nKeyLen, strKeyFile, 0)
For VB users, there are some wrapper functions provided in basCrPKI
which avoid the complications of having to pre-dimension strings, etc. These
begin with lowercase letters and no underscore. They are shown in our examples as follows:
strPublicKey = rsaReadPublicKey(strKeyFile)
To install on a test or developer system, use the setup.exe program provided with the distribution.
This installs the core Win32 DLL in your main windows system directory and creates copies of all other required files,
including this manual, in the directory C:\Program Files\CryptoSysPKI.
To distribute a Licensed Version to your end users, please read the instructions in the file distrib.txt.
You do not use the setup.exe program to distribute to end users.
Note that the core DLL file is completely different for the Trial and Developer versions.
Important: You must use the setup.exe
program to install the Trial version on your system
and you
must have administrator rights when installing or uninstalling.
To uninstall, use Start > Settings > Control Panel > Add/Remove Programs > CryptoSys PKI Toolkit to remove the application and all associated files.
diCrPKI.dll which is a Win32 DLL. This file must
exist in the library search path on the user's system for all programming language interfaces.
The executable diCrPKI.dll is not registered with Regsvr32 (it can't be).
The VB6/VBA and C/C++ interfaces access this core executable directly.
diCrSysPKINet.dll is a .NET Class Library which exposes various classes
to allow programming access from C# and VB.NET projects.
This file is referenced from your .NET project. It is not registered.
Long in VB6/VBA and a long in C/C++, but an Integer in VB.NET
and an int in C#.
The wrapper functions provided in the .NET
interface behave differently (and more conveniently) - please
refer to the detailed documentation on that interface.
Functions either
PKI_Version function.
The function will demonstrate that the Toolkit is properly installed.
Here is some example source code in VB6, C, C# and VB.NET, respectively.
Public Sub ShowVersion()
Dim nRet As Long
nRet = PKI_Version(0, 0)
Debug.Print "Version=" & nRet
End Sub
#include <stdio.h>
#include "diCrPKI.h"
int main(void)
{
long version;
version = PKI_Version(NULL, NULL);
printf("Version=%ld\n", version);
return 0;
}
using CryptoSysPKI;
static void ShowVersion()
{
int ver;
ver = General.Version();
Console.WriteLine("Version={0}", ver);
}
Imports CryptoSysPKI
Shared Sub ShowVersion()
Dim ver As Integer
ver = General.Version()
Console.WriteLine("Version={0}", ver)
End Sub
In VB6/VBA, use the StrConv function.
Dim abData() As Byte Dim Str As String Dim i As Long Str = "Hello world!" ' Convert string to bytes abData = StrConv(Str, vbFromUnicode) For i = 0 To UBound(abData) Debug.Print Hex(abData(i)); "='" & Chr(abData(i)) & "'" Next ' Convert bytes to string Str = StrConv(abData, vbUnicode) Debug.Print "'" & Str & "'"
48='H' 65='e' 6C='l' 6C='l' 6F='o' 20=' ' 77='w' 6F='o' 72='r' 6C='l' 64='d' 21='!' 'Hello world!'
In VB.NET use System.Text.Encoding.
Dim abData() As Byte Dim Str As String Dim i As Long Str = "Hello world!" ' Convert string to bytes abData = System.Text.Encoding.Default.GetBytes(Str) For i = 0 To UBound(abData) Console.WriteLine(Hex(abData(i)) & "='" & Chr(abData(i)) & "'") Next ' Convert bytes to string Str = System.Text.Encoding.Default.GetString(abData) Console.WriteLine("'" & Str & "'")You could be more explicit by replacing
.Default with .GetEncoding(1252),
and then use the appropriate code page for your character set (1252 is Western European).
In C#, use System.Text.Encoding, which has identical behaviour to the function in VB.NET.
byte[] abData;
string Str;
int i;
Str = "Hello world!";
// Convert string to bytes
abData = System.Text.Encoding.Default.GetBytes(Str);
for (i = 0; i < abData.Length; i++)
{
Console.WriteLine("{0:X}", abData[i]);
}
// Convert bytes to string
Str = System.Text.Encoding.Default.GetString(abData);
Console.WriteLine("'{0}'", Str);
In C and C++, the distinction between a string and an array of bytes is often blurred.
A string is a zero-terminated sequence of char types and
bytes are stored in the unsigned char type.
A string needs an extra character for the null terminating character;
a byte array does not, but it needs its length to be stored in a separate variable
A byte array can can contain a zero (NUL) value but a string cannot.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void pr_hexbytes(const unsigned char *bytes, int nbytes)
/* Print bytes in hex format + newline */
{
int i;
for (i = 0; i < nbytes; i++)
printf("%02X ", bytes[i]);
printf("\n");
}
int main()
{
char szStr[] = "Hello world!";
unsigned char *lpData;
long nbytes;
char *lpszCopy;
/* Convert string to bytes */
/* (a) simply re-cast */
lpData = (unsigned char*)szStr;
nbytes = strlen(szStr);
pr_hexbytes(lpData, nbytes);
/* (b) make a copy */
lpData = malloc(nbytes);
memcpy(lpData, (unsigned char*)szStr, nbytes);
pr_hexbytes(lpData, nbytes);
/* Convert bytes to a zero-terminated string */
lpszCopy = malloc(nbytes + 1);
memcpy(lpszCopy, lpData, nbytes);
lpszCopy[nbytes] = '\0';
printf("'%s'\n", lpszCopy);
free(lpData);
free(lpszCopy);
return 0;
}
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 'Hello world!'
The types char and unsigned char
might be identical on your system, or they might not be.
We strongly recommend that you explictly distinguish between strings and byte arrays in your code by using
the correct type and consistently treating them differently.
If your string is a Unicode string, then it consists of a sequence of wchar_t types.
Converting wide-character strings to a sequence of bytes in C is more problematic.
You can either convert the Unicode string directly to a string of bytes (in which case every second byte will be zero for
US-ASCII characters),
or use the stdlib wcstombs function or the Windows WideCharToMultiByte function
to convert to a sequence of multi-byte characters (some will be one byte long, some two)
and then convert the multi-byte string to bytes (you can do this with a simple cast).
Each party encrypting and decrypting must agree on which way to do it.
Long to Integer.
CryptoSys PKI always uses 32-bit integers.
Declare Function Foobarto
Declare Ansi Function Foobar
strBuffer = String(n, " ")to
strBuffer = New String(" "c, n)
Note the 'c' after the " ". This is required for the Strict On option.
System.Text.Encoding.Default.GetBytes(Str) and
System.Text.Encoding.Default.GetString(abData) to convert between text strings and bytes
instead of StrConv(Str, vbFromUnicode) and StrConv(abData, vbUnicode).
To call the CryptoSys PKI functions
from a classic Visual Basic project or VBA application,
just add the module basCrPKI.bas to your project
(VBA users should delete the first line Attribute VB_Name = "basCrPKI").
The VB functions work in the same way as you would call the Win32 API functions from VB. You must use the correct variable types and must pre-dimension strings and byte arrays that are to receive output or you will suffer the wrath of the great god Gee-pee-eff.
For examples, see the test code
provided in the distribution in the folder
C:\Program Files\CryptoSysPKI\VB6.
Dim sData As String * 40or
Dim sData As String sData = String(40, " ")If you know the output string needs to be the same size as the input, do this:
Dim strInput As String Dim strOutput As String strInput = "......" strOutput = String(Len(strInput), " ")To create a byte array of a given length:
Dim abData() As Byte Dim nLen As Long nLen = 40 ReDim abData(nLen - 1)Note that byte arrays in VB are indexed from 0 to nLen - 1.
To create a byte array of the same length as an existing array, do this:
Dim abOutput() As Byte ReDim abOutput(Ubound(abInput))
To find the length of an existing byte array:
nLen = UBound(abData) - LBound(abData) + 1Be careful, as this will cause a run-time error if
abData() has not been ReDim'd.
Look at the code for the function cnvHexStrFromBytes in basCrPKI.bas
to see how this can be handled.
Integer types are never used in CryptoSys PKI.
Only Long types are used.
Compile error: ByRef argument type mismatchit means you have omitted the "(0)" after a Byte parameter, e.g.
Dim abData() As Byte
' ...
nRet = WIPE_Data(abData, nBytes) ' WRONG: compile error
nRet = WIPE_Data(abData(0), nBytes) ' CORRECT
To use with a C or C++ program, include the diCrPKI.h file with your source code
and link with the diCrPKI.lib library. The only non-ANSI C requirement
is that your complier supports the __stdcall
calling convention to call Win32 API functions from a Win32 DLL.
The sample C code PKI_Examples.c provided in the distribution carries out a variety
of tests using known test vectors. There are examples given for each function.
You'll also realise that this manual is written primarily for Visual Basic programmers. Apologies, sort of, because we know you can easily work what to do from the examples given in the sample C programs and the rest of this manual.
The VB6/VBA functions and C/C++ functions are identical in form. The function names are identical, the arguments are the same (even though we may have used different parameter names in the VB and C syntaxes), and the same warnings given in the Remarks section apply to both. Just be careful to add an extra character for output string types in C.
The following type conversions apply between VB6/VBA and C/C++:-
| VB6/VBA | C/C++ |
|---|---|
| ByVal x As String | char *x |
| ByRef x As Byte | unsigned char *x |
| ByVal x As Long | long x |
You can still use Boolean type for the fEncrypt variable in VB6/VBA,
which is equivalent to a 32-bit integer when passed to the core DLL.
We recommend that you use the defined constants ENCRYPT and DECRYPT in your VB6 and C code.
Here is some minimal code:-
/* mysource.c */
#include <stdio.h>
#include "diCrPKI.h"
int main(void)
{
long version;
version = PKI_Version(NULL, NULL);
printf("Version=%ld\n", version);
return 0;
}
To create mysource.exe with MSVC++:
cl mysource.c diCrPKI.libRunning this program should result in
Version=298where the actual number displayed depends on which version you have installed.
implib diCrPKI.lib diCrPKI.dllTo compile with Borland C++:
bcc32 mysource.c diCrPKI.lib
char strings require an extra char for the NUL terminating
character, so add one more to the size a VB programmer would use, e.g.
char sDigest_sha1[41]; /* SHA-1 digest is 40 hex chars plus NUL */ char sDigest_sha2[65]; /* SHA-256 digest is 64 hex chars plus NUL */or
#include <stdlib.h>
char *buf;
buf = malloc(PKI_MAX_HASH_CHARS + 1);
/* ... */
free(buf);
PKI_MAX_SHA1_CHARS
and PKI_MAX_SHA1_BYTES rather than hard-coded numbers, e.g.
char sDigest_sha1[PKI_MAX_SHA1_CHARS + 1];See the constants in
diCrPKI.h.
For examples, see the test code PKI_Examples.c and PKICheck.c
provided in the distribution.
diCrSysPKINet.dll library file into a convenient folder
(the setup program puts this file by default in C:\Program Files\CryptoSysPKI\DotNet).
diCrSysPKINet.dll.using CryptoSysPKI;or (for VB.NET)
Imports CryptoSysPKIto your code.
Alternatively, with C#, you can just include the source code module CryptoSysPKI.cs in your project
and there is no need to reference the class library DLL.
All methods in the CryptoSysPKI .NET Class Library are static methods. You do not need to instantiate or dispose of any objects.
For examples, see the test code TestPKIcsharp.cs and TestPKIvbnet.vb
provided in the distribution.
In older versions we suggested using direct upgrades of the VB6 code in VB.NET with all the pre-dimensioning and other unsafe practices. The .NET Class Library interface is cleaner, safer and more convenient. If you are writing VB.NET code from scratch, please use the .NET Class Library interface. If you need to upgrade old VB6 code, see Upgrading VB6 to VB.NET.
CryptoSysPKI.chm supplied with the distribution
(Hint: it should be in the folder C:
rogram Files\CryptoSysPKI\DotNet).
diCrPKI.dll. It is intended to meet FIPS 140-2 security level 1.
The cryptographic boundary for CryptoSys PKI
is defined as the enclosure of the computer on which the cryptographic module is installed.
As a pure software product, CryptoSys PKI provides no physical security by itself.
The computer itself must be appropriately physically secured.
HKCU\Software\DI Management
and reads entries created by the setup utility in HKLM\Software\DI Management.
Do not attempt to change these entries.
X509_CertIsValidNow function, no checks are made on
the validity period of X.509 certificates by any other functions in this toolkit.
It's up to you to make your own checks.
Use these functions in your tests by all means, but if you are using this toolkit to make an application to be used by less-experienced end users (and this is almost always the case), follow the following guidelines:
WIPE_Data function to wipe the
internal private key string as soon as you've finished using it.
WIPE_File function to erase it immediately afterwards.
Make sure this happens even if an error condition occurs.
[New in Version 3.0]
As of version 3.0, the "internal" RSA key strings are stored in encrypted form. A fresh, random key-encryption key (KEK) is generated for each new process. That meansRSA_KeyHashCode function.
MIICXAIBAAKBgQDOMfnp2hxoUuapC0oAits0T0cs0uQb7mRa+...The new encrypted internal key strings are still in base64 format, but will begin with either "PVT" or "PUB" to denote a private or public key respectively.
PVTRujwuI4e5gQP1SRLmc0n9viayF37krSHbHE... PUBRg1UO9GCVDFLoxE1ihi5p0PoR3Bdrx1U7ky...
We always said we might change the format in future versions. Now we have. We may do it again.
pbeWithSHAAnd3-KeyTripleDES-CBC" from PKCS-5.
The approximate ranking for the encryption schemes in increasing order of security is
pbeWithMD2AndDES-CBC" pbeWithMD5AndDES-CBC" pbeWithSHA1AndDES-CBC" pbeWithSHAAnd3-KeyTripleDES-CBC" (default)pkcs5PBES2" + "des-EDE3-CBC" pkcs5PBES2" + "AES128-CBC" pkcs5PBES2" + "AES192-CBC" pkcs5PBES2" + "AES256-CBC"
The less-secure algorithms pbeWithXAndDES-CBC with single DES are provided just in case you need compatibility with
an older system. Do not use them unless you have to.
The PBES2 scheme uses the PKCS-5 PBKDF2 key derivation function with
hmacWithSHA1 as the default psuedo-random function (PRF). To use a stronger message digest function
from the SHA-2 family in the PRF, add one of the following options
hmacWithSHA224hmacWithSHA256hmacWithSHA384hmacWithSHA512Dim nOptions As Long nOptions = PKI_PBE_PBES2 + PKI_BC_AES256 + PKI_HASH_SHA512Remember that the security of all these schemes is limited by the strength of the password used. Also, other systems may not necessarily support all the alternatives provided here.
We have introduced a whole new set of combinations of new encryption algorithms in this version.
rsaEncryption algorithm in
S/MIME enveloped-data objects.Increased combinations of options for algorithms:
| Function | Combinations Before | Combinations Now |
|---|---|---|
CMS_MakeEnvData | 1 | 24 |
CMS_MakeSigData | 2 | 6 |
X509_MakeCert | 3 | 7 |
RSA_SaveEncrPrivateKeyRSA_MakeKeys |
5 | 24 |
We must add that most of these new additions are overkill for the average user.
It's convenient for us to add all the combinations at once, but expect the standard CMS algorithms of
rsaEncryption with SHA-1 and Triple DES for encryption, and sha1WithRSAEncryption for signatures
to stay as a standard for several years to come.
Most other applications will not accept the new AES/SHA-2 algorithms yet,
so check with your recipients whether they support them.
We note that signatures using SHA-256 are starting to be required and we expect
AES-128 will become a commonplace requirement instead of Triple DES soon.
Otherwise, most other options (AES-192/256 and SHA-384/512) should be kept in reserve.
Please consult your security adviser for the latest recommendations.
Remember that it's the overall security of your entire process that matters, not that you've decided to use AES-256 and SHA-512 just because they are the strongest items on the menu. A security level of 128 bits can be satisfied with AES-128 and SHA-256 and an RSA key of 3072 bits. Any keys and random numbers used should be to the same security level (which is harder to do than you might think). And a password of the same strength needs to be approximately 98 characters long! See NIST SP800-57 [SP80057] for more details on consistent security levels.
rsaEncryption algorithm and is an alternative to the hardly-implemented-by-anybody
RSAES-OAEP from PKCS#1 v2.
RSA-KEM is described in ISO 18033-2 [ISO18033-2] and in an Internet draft document [CMSRSAKEM]. It involves using RSA to encrypt a randomly generated number and uses both key wrapping with a symmetric block cipher and a message-digest-based key derivation function (KDF). There are three parameters required to define it (block cipher, KDF, message digest).
The default parameters in this Toolkit are AES128-Wrap and KDF2 with SHA-1. Note that, in this case, Triple DES is not the default block cipher, although it is available as an option.
When using the
CMS_QueryEnvData function to find the
keyEncryptionAlgorithm, it will be described as
"ac-generic-hybrid", not RSA-KEM, which is strictly the "KeyEncapsulationMechanism" or KEM.
The KEM is a parameter of the overarching key encryption algorithm,
together with the "DataEncapsulationMechanism", DEM, the symmetric key wrap algorithm.
To add to the fun, the official name for the
OID is the other way around, as in "kem-rsa".
In an ASN.1-style format, the KeyEncryptionAlgorithm element for PKCS#1 rsaEncryption
SEQUENCE {
rsaEncryption,
NULL
}
is replaced by a typical element like this for RSA-KEM
SEQUENCE {
id-ac-generic-hybrid, -- generic cipher
SEQUENCE { -- GenericHybridParameters
SEQUENCE { -- key encapsulation mechanism
id-kem-rsa, -- RSA-KEM
SEQUENCE { -- RsaKemParameters
SEQUENCE { -- key derivation function
id-kdf-kdf2, -- KDF2
SEQUENCE { -- KDF2-HashFunction
id-sha256 -- SHA-256; no parameters (preferred)
}
},
16 -- KEK length in bytes
}
}
SEQUENCE { -- data encapsulation mechanism
id-aes128-Wrap -- AES-128 Wrap; no parameters
}
}
}
This is a provisional version of the algorithm in this release [v3.2]. We reserve the right to change the related function and method parameters and options in a future release, and to fix any errors once proper test vectors are available.
We have tried hard to keep this interface simple, but it's not a simple topic. There are thousands of options and alternative ways of doing things. The basic user requires an interface that is straightforward to use and the advanced user expects all the frills and expert options.
CMS objects, X.509 certificates and public key encryption all have lots of alternatives and we've tried to provide a set of the most commonly used variations. Our original design intention was to create a reasonably small set of functions that are simple to use to carry out the basic functions while providing the advanced user with the options they expect.
We have tried to strike a balance between dozens of functions all doing minor variations on a theme with long Java-like names and a smaller set that uses option flags where the developer has to master the addition of complicated bit values. On testing, we found that the over use of options generally made it more confusing, so we've added more functions than we initially planned. We do think some of the functions names are getting a bit long and we do admit that some functions have too many parameters, but, overall, we hope we have been successful in our original intentions.
As a general guide, choosing a zero value for the option flag will probably do what you expect.
Functions you would expect to be used repeatedly to decrypt messages require the private key to be stored in a string. This is messy to do for one-offs, and clutters up our simple examples, but is more efficient on a server handling large quantities of incoming messages. Remember that there is a deliberate delay when trying to decrypt private keys. See also Internal key strings.
We have avoided COM with all its potential problems and have kept the old-fashioned Windows API format where strings need to be pre-dimensioned before use. This makes it trickier to use but easier to use the toolkit with different programming languages. It is a simple matter to write wrapper functions to manage the tricky bits and examples are given.
We always strive to improve our products and will be grateful for any feedback. If you think we've got it wrong, please tell us.
The core executable diCrPKI.dll is a Win32 DLL compiled with
Microsoft Visual C++ Version 5.0.
All programming language interfaces require this DLL to exist in the library search path of the user's system.
The core cryptographic functions are written in pure ANSI C with extensive internal checks for memory leaks and overflow issues.
The executable diCrPKI.dll is compatible with all versions of 32-bit Windows
(95/98/Me/NT4/2K/XP/2003/Vista).
It does not require any other special libraries to work apart from the standard Win32 libraries available in all
versions of Windows.
It is totally independent of the Microsoft Cryptographic API.
The wrapper executable diCrSysPKINet.dll is a .NET Class Library
created with Microsoft .NET Framework 1.0 Version 1.0.3705 and
Microsoft Visual C# .NET 55524-652-0000007-18869. This should be upwardly compatible with all later versions,
including Visual Studio 2005 Express.
It calls the functions in the core Win32 DLL using System.Runtime.InteropServices.
The source code for this wrapper DLL is provided in the distribution.
For more details on its use, see Using with .NET.
In addition to this automatic software integrity test, the integrity of the entire DLL file can be independently verified by the user using published message digest and checksum values before and after installation - see CryptoSys PKI Toolkit Integrity Checks.
* The error log file will be given a filename "pkierr.log". If the process does not have permissions to write to that directory, no log file be created.
** By terminate its own process, we mean that the CryptoSys DllMain function will return false. This will cause statically-linked applications to terminate, and applications that use LoadLibrary, like Visual Basic, to return an error message saying it cannot find the DLL file.
You can make settings in the machine's registry to prevent the message box displaying and to change the destination directory of the log file. See Optional Registry Settings. It is not possible to prevent the DLL from exiting if a critical error happens.
The user may call the power-up self-tests on demand with the PKI_PowerUpTests
function. In the event that such an "on demand" test fails, the module will log the error event and return an error
code but will not terminate the process.
Be aware that the automatic self-tests fail only in exceptional circumstances. You should never see one in practice unless the software module has been tampered with.
Disclaimer Modifying the registry can cause serious problems that may require you to reinstall your operating system. We cannot guarantee that problems resulting from the incorrect use of the registry can be solved. Use the information provided at your own risk.
[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysPKI\Options]NoMessageBox[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysPKI\Options]ErrorLogDir[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysPKI\Options]NoErrorLogThe description for Event ID ( 8xxx ) in Source ( diCrPKI ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer.For correct formatting of the message, create the REG_SZ and REG_DWORD values in the key below. The message will still be recorded even if this entry is not present.
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\diCrPKI]EventMessageFileTypesSupportedNote that event log messages are only written in exceptional circumstances. You should never see one in practice unless the software module has been tampered with.
The underlying RNG functions use the algorithms recommended in NIST SP 800-90 [SP80090] (the "DRBG Standard") to provide a Deterministic Random Bit Generator (DRBG). The HMAC_DRBG mechanism is used with SHA-1 as the underlying hash function. This outputs a sequence of binary bits that appears to be statistically independent and unbiased. The output is effectively random so long as internal actions of the process are hidden from observation. In particular the algorithm provides good Backtracking Resistance and, depending how it is used, good Prediction Resistance.
Entropy is accumulated at startup and whenever any function in the library is called. Only inobtrusive methods of collecting entropy are used, so you can use the toolkit safely in any application. The "Fortuna" method of pooling is used to prevent certain attacks from someone who controls some but not all of the entropy sources (see chapter 10 of [FERG03]). The more times your application calls the functions in the library before needing some random data, the more entropy will be accumulated. The user cannot control how or when the Fortuna entropy is added to the RNG process - this is by design. The advantage of the Fortuna system is that the level of entropy does not need to be measured. There is, however, a period of vulnerability just after start up when there may not be sufficient entropy in the pools. This can be overcome by initializing with a seed file.
We strongly recommend that you use and initialize with a seed file wherever possible.
RNG_Initialize
function to specify a seedfile with a known minimum amount of entropy
to initialise the PRNG. This seed file is updated automatically when used. You
can optionally call the
RNG_UpdateSeedFile
from time to time in your
application, and use
RNG_MakeSeedFile
to create a new one. The security of this
method is as good as the security you have over the seed file. If an attacker
controls the seed file, it does not mean they control the random output data; it
just means that using a seedfile does not increase the security strength of the PRNG.
RNG_BytesWithPrompt function
when generating random data to force the user to
generate entropy using random keystrokes and mouse movements.
RNG_MakeSeedFile
also uses such a prompt.
This works provided you know the user's keyboard strokes and mouse movements are secure
(e.g. are not being transmitted over a network).
RNG_Bytes function.
If you assume zero security strength for the
internally-generated entropy and you add input with, say, 128 bits of security
strength, then the output from the RNG will have at least 128 bits of security
strength.
User-supplied entropy (a.k.a. a "seed") is added as "additional input" to the generation process. It does not affect the accumulation pools and cannot be used by an attacker to control the output.
Remember it's not how "random" your user-supplied entropy is, but how little an
attacker knows about it. Using the current time is no use. If you can provide
32 bytes* of data of which an attacker knows nothing and cannot later discover,
then you have added 128 bits of security strength.
* The bytes must have been selected randomly from the range 0 to 255.
Here is an example in VB6 of how you could use the RNG to generate user-supplied entropy when creating a new pair of RSA keys. (The password should be entered separately, not hard-coded like this!)
Dim nRet As Long Dim nBits As Long Dim strPublicKeyFile As String Dim strPrivateKeyFile As String Dim strPassword As String Dim strSeed As String nBits = 512 strPublicKeyFile = "mykeypub.bin" strPrivateKeyFile = "mykeypri.bin" strPassword = "password" ' 1. Generate some user-derived entropy using the keyboard strSeed = String(64, " ") nRet = RNG_StringWithPrompt(strSeed, Len(strSeed), "", 0) ' 2. Create a new pair of RSA key files, adding this seed to the process Debug.Print "About to create a new RSA key pair..." nRet = RSA_MakeKeys(strPublicKeyFile, strPrivateKeyFile, nBits, _ PKI_RSAEXP_EQ_65537, 50, 1000, strPassword, strSeed, Len(strSeed), 0) Debug.Print "RSA_MakeKeys returns " & nRet & " (expected 0)" ' 3. Immediately wipe the sensitive data Call WIPE_String(strSeed, Len(strSeed)) Call WIPE_String(strPassword, Len(strPassword))
And the same example in C# (VB.NET is very similar)
int r;
byte[] seed;
int nbits = 512;
string publicKeyFile = @"C:\Test\mykeypub.bin";
string privateKeyFile = @"C:\Test\mykeypri.bin";
StringBuilder sbPassword = new StringBuilder("password");
// 1. Generate some user-derived entropy using the keyboard
seed = Rng.BytesWithPrompt(64,"",Rng.Strength.Default);
Debug.Assert(seed.Length > 0, "Failed to create a seed");
// 2. Create a new pair of RSA key files, adding this seed to the process
r = Rsa.MakeKeys(publicKeyFile, privateKeyFile, nbits,
Rsa.PublicExponent.Exp_EQ_65537, 1000, sbPassword.ToString(),
Rsa.PbeOptions.Default, false, seed);
Console.WriteLine("Rsa.MakeKeys returns {0} (expected 0)", r);
// 3. Immediately wipe the sensitive data
Wipe.Data(seed);
Wipe.String(sbPassword);
For more details on the security aspects of the random number generator, see the technical details published on our web site.
key=value(;key=value)*Supported keys are:
"C=US;O=Example Organisation;CN=Test User 1" "CN=Carol" "CN=My User;O=My Org;OU=Unit;C=AU;L=My Town;S=NSW;E=myuser@my.org"
At least one attribute must be specified. Spaces are significant between the "=" and the ";". Only a semicolon can be used as a separator - commas are treated as normal characters. Semicolons are not permitted in attribute values. The distinguished name attributes are written to the certificate name in the order they are found. Keys may be repeated. Note that the Windows Certificate Manager displays the attributes in reverse order.
We keep the deprecated emailAddress attribute here because it seems so popular.
Note that the emailAddress attribute of the distinguished name
is independent of the RFC822 address in a subjectAltName extension, which can be specified
separately.
IA5String for the emailAddress attribute
and PrintableString for all other attributes.
If the input string includes characters that are not valid for these encodings, then
a T61String (TeletexString) will be used instead as a fudge.
Certificates created with a T61String may not be accepted as valid by some profiles.
To force UTF-8 encoding, specify the PKI_X509_UTF8 flag.
UTF8String.
(According to Section 4.1.2.4 of [PKIX],
UTF-8 encoding is now mandatory for all new certificates issued under that profile after 31 December 2003.)
If the value contains only valid UTF-8 characters, then [New in Version 3.1] the string will be copied directly. Otherwise the input is assumed to be 8-bit Latin-1 and will be converted to UTF-8 accordingly, with each 8-bit character being converted to two UTF-8 bytes.
#x.
Examples:
| Input string | Default encoding | With PKI_X509_UTF8 |
|---|---|---|
OU=abc | PrintableString "abc" | UTF8String "abc" |
OU=#x616263 | PrintableString "abc" | UTF8String "abc" |
C=México | T61String "México" | UTF8String "México" |
C=#x4de97869636f | T61String "México" | UTF8String "México" |
C=#x4dc3a97869636f | T61String (garbage) | UTF8String "México" |
CN=#xE5A4A7E58DAB | T61String (garbage) | UTF8String (U-5927,U-536B) |
Note that the entire value must be preceded by the two characters "#" (number sign, hash sign) and "x" (lower-case letter X).
The remainder of the value must consist only of valid hexadecimal characters [0-9A-Fa-f].
Note, too, that the hex digits are not case sensitive, but the "x" is.
If you actually want to enter a value string that begins with "#x", then enter as "##x"; e.g. "OU=##xabc"
will produce the OU value "#xabc".
Valid algorithm names are:
| Value | Algorithm | Option |
|---|---|---|
| tdea | Triple DES, a.k.a. 3DES, des-ede3 | PKI_BC_TDEA |
| 3des | Alternate for Triple DES | PKI_BC_3DES |
| des-ede3 | Another alternate for Triple DES | PKI_BC_DESEDE3 |
| aes128 | AES-128 | PKI_BC_AES128 |
| aes192 | AES-192 | PKI_BC_AES192 |
| aes256 | AES-256 | PKI_BC_AES256 |
We have used "TDEA" consistently in CryptoSys products to refer to the Triple DES algorithm (as in its official name "Triple Data Encryption Algorithm"). In this case, we have given you the alternative ways of expressing the algorithm as any one of "tdea", "3des" or "des-ede3". These are all equivalent and all yield identical results.
Valid mode names are:
| Value | Mode | Option |
|---|---|---|
| ecb | Electronic Code Book mode (default) | PKI_MODE_ECB |
| cbc | Cipher Block Chaining mode | PKI_MODE_CBC |
| ofb | Output Feedback mode | PKI_MODE_OFB |
| cfb | 64-bit Cipher Feedback mode | PKI_MODE_CFB |
| ctr | Counter mode | PKI_MODE_CTR |
Some examples of valid string values for the strAlgAndMode parameter are:
| strAlgAndMode | Description | Alternative Option value |
|---|---|---|
| tdea-cbc | Triple DES in CBC mode | PKI_BC_TDEA+PKI_MODE_CBC |
| 3des-cbc | ditto (alternate name) | PKI_BC_3DES+PKI_MODE_CBC |
| des-ede3-cbc | ditto (alternate name) | PKI_BC_DESEDE3+PKI_MODE_CBC |
| tdea-ecb | Triple DES in ECB mode | PKI_BC_TDEA+PKI_MODE_ECB |
| tdea | ditto (ECB is default mode) | PKI_BC_TDEA |
| aes128-cbc | AES-128 in CBC mode | PKI_BC_AES128+PKI_MODE_CBC |
| aes256-ctr | AES-256 in Counter mode | PKI_BC_AES2568+PKI_MODE_CTR |
Punctuation and space characters and upper- and lower-case are ignored in the strAlgAndMode string, so
"tdea-cbc",
"TDeA---cBc",
"tdea cbc", and
"TDEACBC" are equivalent
(as indeed is
"t*D$e^A c@b!C"!!)
It is an error to use both the strAlgAndMode and nOptions parameters to specify the algorithm and mode. The algorithm must be explicitly specified. There is no default algorithm. The default cipher mode is ECB mode, which is not recommended because of security issues. It is recommended to use either CBC or CTR mode with a IV value that is unique each time it is used with a given key.
| Algorithm | Key size (bytes) | Block size (bytes) | IV size (bytes) | Valid ECB/CBC data lengths |
|---|---|---|---|---|
| Triple DES | 24 | 8 | 8 | 8, 16, 24, 32, ... bytes |
| AES-128 | 16 | 16 | 16 | 16, 32, 48, 64, ... bytes |
| AES-192 | 24 | 16 | 16 | 16, 32, 48, 64, ... bytes |
| AES-256 | 32 | 16 | 16 | 16, 32, 48, 64, ... bytes |
[New in Version 3.1] Those X.509 functions which require you to pass the filename of an X.509 certificate
will now accept a base64 string representation of the certificate instead.
This is the base64 string that can be obtained using the
X509_ReadStringFromFile function.
The first character in such a string should always be an "M".
[New in Version 3.2] You can also pass a string containing the certificate in PEM format. PEM format looks like
-----BEGIN CERTIFICATE----- MIHgMIGaAgEBMA0GCSqG... -----END CERTIFICATE-----
See PEM string alternative below for more details.
The example below shows how each of the filename, the base64 string, or the PEM-format string can be used in a typical X.509 function.
Dim nRet As Long Dim strCertFileOrB64String As String Dim strHexHash As String ' Compute the SHA-1 `thumbprint' of an X.509 certificate in two forms strHexHash = String(PKI_SHA1_CHARS, " ") ' Refer to file itself... strCertFileOrB64String = "smallca.cer" nRet = X509_CertThumb(strCertFileOrB64String, strHexHash, Len(strHexHash), 0) Debug.Print "X509_CertThumb returns " & nRet & " for '" & strCertFileOrB64String & "'" Debug.Print "SHA-1 thumbprint=" & strHexHash ' Use base64 string representation directly... strCertFileOrB64String = _ "MIHgMIGaAgEBMA0GCSqGSIb3DQEBBQUAMAwxCjAIBgNVBAMTAUEwHhcNMDcwODAyMDIwMDAxWhc" _ & "NMTEwODAyMDIwMDAxWjAMMQowCAYDVQQDEwFBMEowDQYJKoZIhvcNAQEBBQADOQAwNgIxA1KS" _ & "JlPSmQAqQgDHUISaUsCrHbIZe249i6jFtfN3rA7czrP4CXS3mjvMFf0AsxV6BwIBAzANBgkqh" _ & "kiG9w0BAQUFAAMyAACeT7GtgmBRKUN20cIyNEGneEvmNxaliuBEVkg2npbyEBgeHXOH6jqj9Ase348UN/Q=" nRet = X509_CertThumb(strCertFileOrB64String, strHexHash, Len(strHexHash), 0) Debug.Print "X509_CertThumb returns " & nRet & " for '" & strCertFileOrB64String & "'" Debug.Print "SHA-1 thumbprint=" & strHexHash ' Again using a PEM-style string... strCertFileOrB64String = _ "-----BEGIN CERTIFICATE-----" & vbCrLf _ & "MIHgMIGaAgEBMA0GCSqGSIb3DQEBBQUAMAwxCjAIBgNVBAMTAUEwHhcNMDcwODAyMDIwMDAxWhc" & vbCrLf _ & "NMTEwODAyMDIwMDAxWjAMMQowCAYDVQQDEwFBMEowDQYJKoZIhvcNAQEBBQADOQAwNgIxA1KS" & vbCrLf _ & "JlPSmQAqQgDHUISaUsCrHbIZe249i6jFtfN3rA7czrP4CXS3mjvMFf0AsxV6BwIBAzANBgkqh" & vbCrLf _ & "kiG9w0BAQUFAAMyAACeT7GtgmBRKUN20cIyNEGneEvmNxaliuBEVkg2npbyEBgeHXOH6jqj9Ase348UN/Q=" & vbCrLf _ & "-----END CERTIFICATE-----" nRet = X509_CertThumb(strCertFileOrB64String, strHexHash, Len(strHexHash), 0) Debug.Print "X509_CertThumb returns " & nRet & " for '" & strCertFileOrB64String & "'" Debug.Print "SHA-1 thumbprint=" & strHexHash
X509_CertThumb returns 40 for 'smallca.cer' SHA-1 thumbprint=a36b1bfa0af41a2785066b2d5135b67011ac3b7f X509_CertThumb returns 40 for 'MIHgMIGaAgEBMA0GCSq...(snip)...HXOH6jqj9Ase348UN/Q=' SHA-1 thumbprint=a36b1bfa0af41a2785066b2d5135b67011ac3b7f X509_CertThumb returns 40 for '-----BEGIN CERTIFICATE----- MIHgMIGaAgEBMA...(snip)...BgeHXOH6jqj9Ase348UN/Q= -----END CERTIFICATE-----' SHA-1 thumbprint=a36b1bfa0af41a2785066b2d5135b67011ac3b7f
[New in Version 3.2] In the same way you can pass a base64 string instead of an X.509 filename, you can now pass a string containing the certificate in PEM format. The PEM format looks like
-----BEGIN CERTIFICATE----- MIHgMIGaAgEBMA0GCSqG... -----END CERTIFICATE-----
Similarly, those RSA functions which require you to pass the filename of an RSA key file will now accept a string that contains the file contents in PEM format. An RSA key file in PEM format looks like
-----BEGIN ENCRYPTED PRIVATE KEY----- MIICojAcBgoqhkiG9w0BDAEDMA4ECHPQz6NdAmoFAgIH0ASCAoBKn9KXr+dm Vtc0ZhEog7t3Prs4rJazwUsXExU78ePLMquxLi/cPmqtyjb472r6XUOa... -----END ENCRYPTED PRIVATE KEY-----
The functions will accept all strings that start with "-----BEGIN" and are of the form
-----BEGIN XXX----- (base64-encoded data) -----END XXX-----
provided there is a newline character (LF or CRLF) between the pre-encapsulation boundary
"-----BEGIN XXX-----" and the start
of the base64 data, and another before the post-encapsulation boundary "-----END XXX-----".
So do not remove the newline characters from the PEM string.
The exact word or words used for "XXX" do not matter.
Any non-base64 characters found in the encoded data will be ignored.
This means, for example, that you can store your certificates and encrypted private keys as strings in a database. Note that an X.509 certificate can be passed either as a plain base64 string or in PEM format; that is, both with and without the "-----BEGIN CERTIFICATE-----" encapsulation; but RSA key data can only be passed in PEM format.
Dim strKeyPemData As String ' The vbCrLf after the first line is important and so is the one before the last line strKeyPemData = _ "-----BEGIN ENCRYPTED PRIVATE KEY-----" & vbCrLf & _ "MIICojAcBgoqhkiG9w0BDAEDMA4ECHPQz6NdAmoFAgIH0ASCAoBKn9KXr+dm" & vbCrLf & _ "Vtc0ZhEog7t3Prs4rJazwUsXExU78ePLMquxLi/cPmqtyjb472r6XUOa9J/v" & vbCrLf & _ "g2gYHlJ7D7FfAdTdVbHmXWfZzdIqI+AKZmrMoIfSVSSrI8mLDXLDgJVm2Gxa" & vbCrLf & _ "r/YJ154L4fwqWjj0b06v8nTrXTp7G3ZSxjmXc3auf8tS1RatpDuSn027jBGt" & vbCrLf & _ "Pg2CGPjeSomOU7Efd89R+gryW3RfXaMEv1TtGmdS+szxN4TAzgFTzjzE7qJ2" & vbCrLf & _ "+WL09hBRxSyi5JybbxblrO5zDbGJD8rq4kGawWUj4PYDpOkxQYQyK/cALEvv" & vbCrLf & _ "EipLeWvk03CadKER3EcpL7wQT3N5wJGNx7GR3efkO7lO/VfGf6kYFsJ8Qt94" & vbCrLf & _ "vBlgq84abgSD+rlRX03re/NLJQ00Qxl3bDrkSiRoXSfBiOeVzBVTsh03Sj4B" & vbCrLf & _ "V0v2KLENsMXr40rMqTGfKD3V+FyYUehWEkEl3NrIVpBSJir+g4H3tl76SdNe" & vbCrLf & _ "mq/cTtQP+EY8fpC3I46dyDXFat3wQfubw+E5nGfv7xp6vRVRRolpZx7DpuB/" & vbCrLf & _ "z1tzO3uP0vJ0pjATriO/ZAVs6UrXx+DJ6XsfrAVt0jpW5Ngr8rm2EiD3/1T9" & vbCrLf & _ "7q1dELJ7GzCY1dG99XVjt9ZXb7cI8zsPpT/gzQJLfeLe3U5Mdw0hKZLfPCex" & vbCrLf & _ "0urs3ytK0XNu+jZAYeSaysG8/rHJaH74WOgJ8gnSPY4QtWsu6+3qBErS2jbq" & vbCrLf & _ "7E2jRvBKWICVd1yiQCDq/c6s9LeYhNhZsmcWxuX9b4lG9f1LHZy0djhIYi4x" & vbCrLf & _ "IpcEfjkTH+7zUOkMQ+fXZHtSEVFt9L2Ci49jB8YReqbfOuDFzzwsk3xxfL2h" & vbCrLf & _ "ZoRK" & vbCrLf & _ "-----END ENCRYPTED PRIVATE KEY-----" Dim nLen As Long Dim strPassword As String Dim strPrivateKey As String strPassword = "password" ' How long is PrivateKey string? nLen = RSA_ReadEncPrivateKey("", 0, strKeyPemData, strPassword, 0) If nLen <= 0 Then Debug.Print "ERROR: RSA_ReadEncPrivateKey returns " & nLen Exit Sub End If ' Pre-dimension the string to receive data strPrivateKey = String(nLen, " ") ' Read in the Private Key nLen = RSA_ReadEncPrivateKey(strPrivateKey, Len(strPrivateKey), strKeyPemData, strPassword, 0) If nLen <= 0 Then Debug.Print "ERROR: RSA_ReadEncPrivateKey returns " & nLen Exit Sub End If Debug.Print "Private key is " & RSA_KeyBits(strPrivateKey) & " bits long." ' ... do something with the private key... ' then make sure it is deleted strPrivateKey = wipeString(strPrivateKey)
This should produce the output
Private key is 1024 bits long.
SignedData
and encrypted EnvelopedData objects according to the CMS version 1 specifications.
CMS_MakeSigData,
CMS_MakeSigDataFromString,
CMS_MakeSigDataFromSigValue, or
CMS_MakeDetachedSig
functions.
The original specification for a SignedData object is in RSA Lab's PKCS#7 Cryptographic Message Syntax Standard [PKCS7]. The last complete version of this document is version 1.5, which is also republished as RFC 2315. There is also a version 1.6 which is currently just an addendum note [PKCS7-EXT] and which extends the original specification. The CMS specification Cryptographic Message Syntax [CMS] is based on PKCS#7 version 1.5 and ties down some of its ambiguities. S/MIME [SMIME-MSG] uses the CMS specification. Between them, these various documents define five versions of a SignedData object. We support CMS version 1 only (but with a side order of PKCS#7 version 1.6 "naked" SignedData objects also thrown in) - see Supported Algorithms.
A CMS version 1 SignedData object has a variety of possible combinations in what it can contain:
eContent) may be included or not (the latter case
is known as a "detached signature").signedAttributes and PKCS#7 calls them authenticatedAttributes).
Once signed attributes are included, the method for computing and verifying the digital signature is different.
signedAttributes themselves, formatted
in a specific way. If signed attributes are not present, then the signer signs the message digest of the eContent
directly.
CMS_VerifySigData
carries out steps 2 and 3 directly with options for the user to pass the signer's certificate details if they are not already included
and also to pass the message digest of the eContent for detached signatures.
The function
CMS_GetSigDataDigest
will extract the message digest, if possible, to enable the user to perform their own separate comparison
with an independently-computed message digest. Note that being able to retrieve the message digest
with this function implicitly verifies that the purported signer really did use their private key to sign the object.
However,
unlike the CMS_VerifySigData function, success with this function does not necessarily mean that the signer actually signed
the eContent itself. Furthermore, if the signer used the DSA signature algorithm and did not include message attributes,
then you cannot directly extract the message digest of the eContent.
Confused so far? Try writing this manual.
To extract just the certificates themselves from a SignedData object, use the
X509_GetCertFromP7Chain
function. This will work for all types of SignedData objects, not just the "certs-only" type.
CMS_MakeEnvData
or CMS_MakeEnvDataFromString function
is a CMS object. This CMS object needs to be inserted into an application/pkcs7-mime MIME entity
before being sent as an email message.
The CMS object is sent as an attachment to the email, usually with the name smime.p7m.
If your email program allows you to tailor the headers, you should identify the Content-Type as
application/pkcs7-mime; smime-type=enveloped-data.
Most email programs convert the binary CMS object file into base64 encoding automatically.
If not, you can use the PKI_CMS_FORMAT_BASE64 option to generate the output directly
in base64 encoding.
Ann creates a message for Ben using the default option:
nRet = CMS_MakeEnvDataFromString("smime.p7m", _
"Be in bar at 7 pm wearing a red rose", "ben.cer", "", 0, 0);
The output file smime.p7m will be in binary BER-encoded format.
To send as an S/MIME email, you attach this file to your email message and you need to add the following headers:-
Date: Mon, 23 Feb 2004 12:00:52 +1100
From: alice@example.com
To: ben@example.com
Subject: Secret message
MIME-Version: 1.0
Content-Type: application/pkcs7-mime;
smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64
Content-Description: attachment;filename=smime.p7m
MIAGCSqGSIb3DQEHA6CAMIACAQAxgZMwgZACAQAwOjA0MQswCQYDVQQGEwJBVTEV
...
When Bob receives the email, he can either:-
CMS_ReadEnvData function.
application/pkcs7-mime and enveloped-data
should ensure that the receiving email
program correctly identifies the file as an encrypted email and treats it accordingly. This is optional,
and you may find it more convenient to agree with your sending parties not to do this so you can just save the attached data file directly
without getting involved in the "security" hoops that programs like Outlook Express will put you through.
If you can't save the attachment file directly from your email program, remember that email files are just
simple text files (even though they may have a .EML extension) and can be edited using a simple text editor
like NotePad. Open the email in your favorite text editor and do a cut-and-paste of the attachment data to another file.
Use the PKI_CMS_FORMAT_BASE64 option when reading what you've saved.
CMS_MakeSigData or CMS_MakeSigDataFromString
functions.
The MIME headers are similar to the enveloped-data example above:-
MIME-Version: 1.0
To: ben@example.com
From: ann@example.com
Subject: Signed message
Date: Mon, 23 Feb 2004 12:00:52 +1100
Content-Type: application/pkcs7-mime; smime-type=signed-data;
name=smime.p7m
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7m
MIIDmwYJKoZIhvcNAQcCoIIDjDCCA4gCAQExCTAHBgUrDgMCGjAtBgkqhkiG9w0BBwGgIA
QeDQpUaGlzIGlzIHNvbWUgc2FtcGxlIGNvbnRlbnQuoIIC4jCCAt4wggKdoAMCAQICAgDI
...
To read, just save the attachment to your hard drive and read the file with
CMS_ReadSigData or CMS_ReadSigDataToString.
The second format, S/MIME multipart/signed, includes the actual content in the body of the email message
and attaches a "detached signature" signed-data object identified as
Content-Type: application/pkcs7-signature; name=smime.p7s
in the MIME part header. This has the advantage that the signed content can be read directly in the email message,
but you need a more sophisticated email program to create the final message.
The detached signature CMS object can be created using the
CMS_MakeDetachedSig function.
A typical multipart/signed message is:
MIME-Version: 1.0 To: ben@example.com From: ann@example.com Subject: Multi-part signed message Date: Mon, 23 Feb 2004 12:00:52 +1100 Content-Type: multipart/signed; micalg=SHA1; boundary="----=_NextBoundry"; protocol="application/pkcs7-signature" This is a multi-part message in MIME format. ------=_NextBoundry This is some sample content. ------=_NextBoundry Content-Type: application/pkcs7-signature; name=smime.p7s Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=smime.p7s MIIDeQYJKoZIhvcNAQcCoIIDajCCA2YCAQExCTAHBgUrDgMCGjALBgkqhkiG9w0BBwGggg LiMIIC3jCCAp2gAwIBAgICAMgwCQYHKoZIzjgEAzASMRAwDgYDVQQDEwdDYXJsRFNTMB4X ... ------=_NextBoundry--You are strongly recommended to use quoted-printable encoding for the content part of the message to prevent the signed data being changed during transmission.
For more details on how to insert your CMS objects into MIME bodies (honest, that's the term they use), please refer to S/MIME Version 3 Message Specification [SMIME-MSG] and Examples of S/MIME Messages [SMIME-EX]. Stallings also covers the topic well in Cryptography and Network Security [STAL02].
The functions RSA_RawPublic and RSA_RawPrivate just carry out the basic RSA encryption or decryption operation on a "raw" block of data. The block must be exactly the same length in bytes as the length of the RSA key modulus; it must obey certain mathematical properties (in practice, make sure the first byte is zero); and it should be "padded" in a certain way to improve security and make it easier to pass to other systems (the built-in cryptographic functions in .NET hide this part of the process from you).
Use the function RSA_EncodeMsg to encode or "pad" the message data you want to encrypt or sign. Remember that Encoding is Not Encryption.
+-------------------+----+------------------------------------------------+ | RSA-encrypted-CEK | IV | Data-encrypted-with-symmetric-cipher-using-CEK | +-------------------+----+------------------------------------------------+where IV is the initialization Vector for the block cipher encryption, generated uniquely (and secretly) each time. To decrypt, parse the input into its three components, use the RSA private key (held separately) to decrypt the RSA block and hence get the CEK to use to decrypt the main body of data. This technique is as strong as its weakest link. Triple DES with a full 192-bit triple key is equivalent in security to a 2048-bit RSA key (see [SP80057]).
For digital signing, unless the message is very short, we generate a message digest of the original message using a hash function, "encrypt" the digest using the RSA private key, and then send this block on to our recipient as a "digital signature", usually together with the message itself.
+------------------+---------------------+ | Original message | RSA Signature block | +------------------+---------------------+
To verify, the recipient parses the input into its two components and then "decrypts" the RSA block using the sender's public key to recover the message digest. She then independently computes the message digest hash of the received message and compares the two. If they are the same, then the signature has been verified.
* New in Version 3.2
CMS_MakeEnvData - Create an encrypted CMS enveloped-data object for one or more recipients using their X.509 certificates.
CMS_MakeEnvDataFromString - ditto using data directly from a string instead of a file.
CMS_ReadEnvData - Reads and decrypts a CMS enveloped-data object using recipient's private key.
CMS_ReadEnvDataToString - ditto writing data directly into a string instead of a file.
CMS_MakeSigData - Create a CMS signed-data object using sender's private key.
CMS_MakeSigDataFromString - ditto using data directly from a string instead of a file.