NOTICE: No representations are made concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind.
License to copy and use this software is granted provided that these notices are retained in any copies of any part of this documentation and/or software.
Throughout this document, "you" refers to you, the programmer, who are writing an application which makes calls to the RIPEM API. It is assumed you have read the RIPEM User's Guide and are therefore familiar with the following topics:
Questions should be sent to the Usenet newsgroup alt.security.ripem.
The rest of this chapter introduces RIPEM with an example application. The chapter "RIPEM API Function Overview" gives a quick overview of the RIPEM API functions in their natural groupings. And, the chapter "RIPEM API Function Details" lists the API functions alphabetically and gives details on calling requirements. Also, some relevant excerpts from the RSAREF reference manual are reproduced in Appendix A.
Of course, another great source of "documentation by example" is the command-line applications RIPEM and RCERTS provided with the standard RIPEM release. To see any of the RIPEM API functions used in context, you can scan the files in the ripem/cmdline directory. Also, the full source of the RIPEM library is in the ripem/main directory, and looking at how an API functions is coded may answer many of your questions.
#include<stdio.h> #include<string.h> #include "global.h" #include "rsaref.h" #include "ripem.h"All RIPEM applications must include stdio.h, global.h, rsaref.h, and ripem.h. stdio.h is needed because ripem.h uses the FILE type for database I/O, etc. global.h is used by rsaref.h and ripem.h. rsaref.h is the header file for the RSAREF cryptographic library which defines types like R_PUBLIC_KEY. And ripem.h is the header file for the RIPEM API.
void main () { RIPEMInfo ripemInfo;The ripemInfo structure holds the information used by RIPEM such as the user's name, public and private keys, and certification preferences. The ripemInfo structure also holds internal state information used between calls such as RIPEMEncipherUpdate and RIPEMEncipherFinal. This is because the RIPEM API library is fully reentrant, meaning that all dynamic memory is maintained by the caller of the library, as opposed to a global variable within the library's data space.
RIPEMDatabase ripemDatabase;The RIPEMDatabase structure holds information and FILE handles for the RIPEM data files such as pubkeys, privkey, crls and preferen.
char *errorMessage; unsigned char *partOut, line[256]; unsigned int partOutLen, lineLen; RIPEMInfoConstructor (&ripemInfo);This initializes ripemInfo, setting memory pointers to NULL, etc. RIPEMInfoConstructor must be called for any RIPEMInfo structure before it is used. Also, RIPEMInfoDestructor must be called when it is done being used to free up any memory, etc. It is fair to say that the RIPEM API is a C++ "wannabe". If the RIPEM API were actually C++, RIPEMInfo would be a class and the constructor and destructor would automatically be called.
RIPEMDatabaseConstructor (&ripemDatabase);Similar to RIPEMInfoConstructor, RIPEMDatabaseConstructor must be called for any RIPEMDatabase structure before it is used. Also, RIPEMDatabaseDestructor must be called when it is done being used to close files, etc.
/* For error, break to end of do while (0) block. */Processing must be aborted if any RIPEM API function returns an error (except for some explicitly stated cases such as RIPEMLoginUser returning ERR_PREFERENCES_CORRUPT). The do {} while (0) construct simply sets up a block where any break statement (usually due to error) will cause a jump to the end of the block. This is a more structured approach than using a goto statement.
do { /* Initialize the database to the home directory, which must already exist and end in the directory seperator. */ if ((errorMessage = InitRIPEMDatabase (&ripemDatabase, ".\\", &ripemInfo)) != (char *)NULL) break;InitRIPEMDatabase opens all the data files in the RIPEM home directory, or creates them if they don't exist. For simplicity here, the directory name is hard-coded. A full implementation would get this from an environment variable, a command line argument, etc.
To get a full path name, RIPEM simply concatentates the file name to the supplied RIPEM home directory. Therefore, the correct seperator for the operating system must be at the end of the directory string as supplied to InitRIPEMDatabase. (Here it is the DOS backslash, but it could be the UNIX forward slash, etc.) Also, even though RIPEM can create the data files, it cannot create the RIPEM home directory itself. Therefore, the calling application is responsible for checking that the directory exists and for creating it if it doesn't. The documentation for InitRIPEMDatabase suggests a way to check if the directory exists.
As with most RIPEM API functions, this returns (char *)NULL for success or a pointer to an error string. The if statement here simply calls the function, assigns errorMessage to the return value and breaks if it is not null.
if ((errorMessage = RIPEMLoginUser (&ripemInfo, "test", &ripemDatabase, (unsigned char *)"password", strlen ("password"))) != (char *)NULL) break;This operation unlocks the user's private key using the supplied password and places the user's private key, public key, self-signed certificate and other information into ripemInfo.
For simplicity here, the username and password are hard-coded. A full implementation would typically get the username from an environment variable and get the password by prompting the user.
/* Prepare for a MIC-CLEAR message. Use null values for encryptionAlgorithm, recipientKeys and recipientKeyCount. */ if ((errorMessage = RIPEMEncipherInit (&ripemInfo, MODE_MIC_CLEAR, MESSAGE_FORMAT_RIPEM1, 0, (RecipientKeyInfo *)NULL, 0)) != (char *)NULL) break;The RIPEM API uses an Init/Update/Final structure to process data, similar to RSAREF. Here, the operation to create a MIC-CLEAR message is initialized. Note that a pointer to the ripemInfo is passed in so that RIPEM can use it to keep state information between init, update and final.
/* Read in the test string from the stdin. Using fgets instead of gets means we get the ending '\n' as required by RIPEMEncipher. */ puts ("Enter text to sign (one line):"); fgets ((char *)line, sizeof (line), stdin); lineLen = strlen ((char *)line);The documentation for RIPEMEncipherUpdate describes what format is required for the text to be processed. Specifically, using fgets instead of gets keeps the '\n' end of line character required by RIPEM.
/* Digest the message. (In a file-based application, this would be called multiple times as each part of the file is read in.) */ if ((errorMessage = RIPEMEncipherDigestUpdate (&ripemInfo, line, lineLen)) != (char *)NULL) break;There is a "bug" in the standard for PEM messages in that the signature information is placed before the text, instead of after. This means that an application must output the digital signature before it writes the text to the output. Therefore a first pass on the text is necessary to simply digest it for computing the signature, hence the need for RIPEMEncipherDigestUpdate. After this, the signature is written to the output stream and RIPEMEncipherUpdate is used to actually write out the text, encoding and encrypting it as necessary. (For deciphering a message, only one pass is necessary, so there is no "RIPEMDecipherDigestUpdate".)
RIPEM can process text of unlimited length by calling update multiple times. Here, we have a short buffer so only one call is necessary. But a full implementation would read in part of the text, call RIPEMEncipherDigestUpdate, read in the next part, etc.
/* Produce the message output and write to stdout. (In a file-based application, the file would first be rewound and this function would be called multiple times as each part of the file is read in.) */ if ((errorMessage = RIPEMEncipherUpdate (&ripemInfo, &partOut, &partOutLen, line, lineLen, &ripemDatabase)) != (char *)NULL) break;RIPEMEncipherUpdate performs the second pass on the text, producing the output text encoded and encrypted as necessary. Here, we have a short buffer so only one call is necessary. But a full implementation would read in part of the text, call RIPEMEncipherUpdate, write out the result, read in the next part, etc. (The first call to RIPEMEncipherUpdate also outputs all the header information including the signature.) When using a file, don't forget to rewind it after reading it in for RIPEMEncipherDigestUpdate.
Note that partOut itself is an unsigned char *, and we pass a pointer to it so that RIPEMEncipherUpdate can set partOut to a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. This way, the caller does not need any guesswork to allocate output buffers of the right size. But, the buffer pointed to by partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, the caller does not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor.
fwrite (partOut, 1, partOutLen, stdout);Write the output buffer returned by RIPEMEncipherUpdate.
/* Finalize and flush the final output. */ if ((errorMessage = RIPEMEncipherFinal (&ripemInfo, &partOut, &partOutLen, &ripemDatabase)) != (char *)NULL) break; fwrite (partOut, 1, partOutLen, stdout);This finalizes the enciphering process, flushing any internal buffers and writing out the end message boundary. The output buffer is returned by RIPEMEncipherFinal in the same manner as with RIPEMEncipherUpdate. RIPEMEncipherFinal should only be called once.
} while (0); RIPEMInfoDestructor (&ripemInfo); RIPEMDatabaseDestructor (&ripemDatabase);These are the matching destructors to RIPEMInfoConstructor and RIPEMDatabaseConstructor, zeroizing any sensitive data and freeing allocated memory. These should be called regardless of any errors during processing.
if (errorMessage != (char *)0) printf ("ERROR: %s\n", errorMessage);If we broke out of the do while (0) block because of an error, errorMessage is set to the string returned by the function, so print it here.
}
For examples of the flags to use when compiling, see the makefile that
is used to make the RIPEM command-line application for your platform. Note
that the Unix makefile, for example, compiles all of the RIPEM library
source in ripem/main as well as the command-line source in ripem/cmdline
and links all these object files explicitly into the executable. However,
it is also possible (and preferable) to load the object files from ripem/main
into an object library first, such as ripemlib.a, and then to link the
library to the object files for the application (such as those in ripem/cmdline).
ripem -g -R eks -H . -u test -k passwordCompile rsign.c and link it to the RIPEM library and the RSAREF library. When you run RSIGN, it gives the prompt:
Enter text to sign (one line):Enter a single line of text, such as "This is a signed message." RSIGN then prints the MIC-CLEAR message which looks like:
-----BEGIN PRIVACY-ENHANCED MESSAGE----- Proc-Type: 2001,MIC-CLEAR Content-Domain: RFC822 Originator-Name: test Originator-Certificate: MIIBrzCCAVkCEQCg1MQZAAQ8vfaemcgFv1WjMA0GCSqGSIb3DQEBAgUAMFwxCzAJ BgNVBAYTAlVTMSAwHgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEcMBoG A1UECxMTUGVyc29uYSBDZXJ0aWZpY2F0ZTENMAsGA1UEAxMEdGVzdDAeFw05NTAz MDcwNTAwMzhaFw05NjAzMDYwNTAwMzhaMFwxCzAJBgNVBAYTAlVTMSAwHgYDVQQK ExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEcMBoGA1UECxMTUGVyc29uYSBDZXJ0 aWZpY2F0ZTENMAsGA1UEAxMEdGVzdDBZMAoGBFUIAQECAgIAA0sAMEgCQQDOdsGA xEgbvsKuOZcQSRHvHXP9IrVOzfqP/FKuuamitXvq2iU0+zYXhqYZLivY5elgZZz0 gaTWzv9CfRV33wD5AgMBAAEwDQYJKoZIhvcNAQECBQADQQA77MfGHXhNHFPEIIAr +p9kQ9L3zqvaog02WgtidUNZmYWNSZSHgkE0ARprtHiLyeBcx+qwtH+AKpKG6piR 0ytq MIC-Info: RSA-MD5,RSA, zB6IRi4tdvg0ORL31shK7GEzT0Hedc35gLUWKKGcJHDUsa9tJ8YlS58UBQkLQp8r W4EROcRzfXdUYzSUUOQmXw== This is a signed message. -----END PRIVACY-ENHANCED MESSAGE-----
typedef struct struct_list { struct struct_list_entry *firstptr; /* NULL if empty list */ struct struct_list_entry *lastptr; } TypList; typedef struct struct_list_entry { struct struct_list_entry *nextptr; /* NULL if no next */ struct struct_list_entry *prevptr; /* NULL if no previous */ void *dataptr; unsigned int datalen; } TypListEntry;When you declare a TypList structure, you must call InitList before using it and FreeList when finished. You can add a generic entry to a TypList using AddToList or PrependToList, and you can add an entry which is a null-terminated C string using AppendLineToList. But typically, RIPEM adds the entries to the list and you only need to access them.
To see if a list has any entries at all, check firstptr: if it is (TypListEntry *)NULL then the list is empty. To access each entry in order, you can use the following code:
TypList list; TypListEntry *entry; /* ... a call to a function which fills the list */ for (entry = list.firstptr; entry; entry = entry->nextptr) { /* entry->dataptr and entry->datalen are the current entry and its length. For example, if the list was filled by SelectCertChain, the certificate encoding is (unsigned char *)entry->dataptr */ }
A distinguished name has a hierarchy of levels called "relative distinguished names" (RDN). In the example above, the RDN at the most significant level is "country = US" and the least significant RDN is "common name = fred@snark.edu". Within a level, there can be one or more "attribute value assertions" (AVA). Typically, each RDN only has one AVA, but in the example above it is possible that the least significant RDN might have two AVAs such as "common name = fred@snark.edu" as well as "locality = Cambridge".
An attribute value assertion has an attribute type, such as "country", and a value, such as "US". Furthermore, the value has a tag which tells the character set of the value. RIPEM recognizes the value tags for "printable string" and "T.61 string". You can think of printable string as a simple subset of ASCII and T.61 as a superset of ASCII which you can use as a catchall for values which don't fit printable string. Use the function IsPrintableString to distinguish these.
RIPEM uses the DistinguishedNameStruct to represent distinguished names, which is defined as follows:
typedef struct DistinguishedNameStruct { /* Most significant AVAs and RDN are listed first. */ short AVATypes[MAX_AVA]; /* -1 means none. */ int AVATag[MAX_AVA]; /* ATTRTAG_PRINTABLE_STRING, etc. */ char AVAValues[MAX_AVA][MAX_NAME_LENGTH + 1]; /* C strings */ short RDNIndexStart[MAX_RDN]; /* index into AVAs for ea. RDN. */ short RDNIndexEnd[MAX_RDN]; /* -1 means none. */ } DistinguishedNameStruct;Typically, you need to access a distinguished name returned by RIPEM, such as in a CertificateStruct returned by DERToCertificate or the userDN in RIPEMInfo set during RIPEMLoginUser. The simplest information to get about a distinguished name is the "smart name", which is defined by GetDNSmartNameIndex in the next chapter. (Usually, the smart name is the common name.) To get the index of the AVA which is the smart name, use GetDNSmartNameIndex which lets you get the attribute type, attribute value and its tag. But usually, you only care about the value, so you can use the "convenience" function GetDNSmartNameValue.
To scan all the information in a name, you can use the following code:
DistinguishedNameStruct name; int rdn, ava; /* ... a call to a function which fills the name */ /* Scan starting from the least significant RDN. */ for (rdn = MAX_RDN - 1; rdn >= 0; --rdn) { if (name.RDNIndexStart[rdn] == -1) /* -1 means there is no RDN here, so try the next one */ continue; /* scan the array of AVAs. (If RDNIndexEnd != RDNIndexStart, then there are multiple AVAs in this RDN.) */ for (ava = name.RDNIndexStart[rdn]; ava <= name.RDNIndexEnd[rdn]; ++ava) { /* Now, name.AVATypes[ava] is the attribute type, name.AVAValues[ava] is the attribute value and name.AVATag[ava] is the value's tag. */ } }The attribute values in AVAValues are null-terminated C strings. The attribute types in AVATypes are one of the ATTRTYPE_ values listed in ripem.h such as ATTRTYPE_COMMONNAME. The AVATag values are usually ATTRTAG_PRINTABLE_STRING or ATTRTAG_T61_STRING. However, it is possible for a value tag to contain an integer other than these, which you can display as "unrecognized."
Sometimes you need to construct a new name, for example to create a
new user by calling RIPEMGenerateKeys. Before adding values to the DistinguishedNameStruct,
you should initialize the structure with InitDistinguisheNameStruct. Then
you can add AVA information to the AVATypes, AVAValues and AVATag arrays.
For each RDN level, set the entries in the RDNIndexStart and RDNIndexEnd
arrays to the appropriate indexes in the AVA arrays. Remember that arrays
are indexed starting from zero and that the first entry in the RDN array
is the most significant level. When setting the AVATypes for a new name,
you can use IsPrintableString to choose between ATTRTAG_PRINTABLE_STRING
and ATTRTAG_T61_STRING.
RIPEM uses the RIPEMAttributes structure to represent attributes, which is defined as follows:
typedef struct { BOOL haveSigningTime; unsigned long signingTime; BOOL haveSigningDescription; char *signingDescription; /* C string */ BOOL haveChallengePassword; char *challengePassword; /* C string */ BOOL haveUnstructuredName; char *unstructuredName; /* C string */ } RIPEMAttributes;RIPEM supports attributes which can be returned from RIPEMDecipherPKCSFinal and attributes which can be supplied to RIPEMCertifyRequestPKCS. See the details on these functions in the next chapter for a description of these attributes. For each type, such as signingTime, there is a BOOL flag, such as haveSigningTime. If the flag is FALSE, then the associated value, such as signingTime, is undefined. If the flag is TRUE then the associated value is present.
Sometimes you need to construct new attributes structure, for example
to supply attributes to RIPEMCertifyRequestPKCS. Before adding attributes
to the structure, you should initialize it with InitRIPEMAttributes. Then
you can add the attributes you want.
When RIPEM returns a certificate, it is as a block of data in encoded form. To decode it, use DERToCertificate which uses a CertificateStruct structure to return the certificate contents. CertificateStruct is defined as follows:
typedef struct CertificateStruct { unsigned int version; unsigned char serialNumber[16]; /* up to 128 bits. */ int digestAlgorithm; DistinguishedNameStruct issuer; unsigned long notBefore; /* seconds since 1970 */ unsigned long notAfter; /* seconds since 1970 */ DistinguishedNameStruct subject; R_RSA_PUBLIC_KEY publicKey; unsigned char signature[MAX_SIGNATURE_LEN]; int signatureLen; } CertificateStruct;For this release of RIPEM, the certificate version is one. (Don't be confused by the fact that a version of one is represented by the integer zero in the encoding.) The serialNumber array holds the certificate's serial number, most significant byte first, right justified and zero padded to the left out to 16 bytes. digestAlgorithm is one of the tokens defined by RSAREF such as DA_MD2 or DA_MD5. For example, if digestAlgorithm is DA_MD5, it means the certificate information was digested with MD5 and then encrypted with the issuer's private key to make the signature. You should also use the digestAlgorithm specified by the certificate when using R_DigestBlock to get a self-signed certificate digest.
The issuer and subject names are represented with a DistinguishedNameStruct
as defined in the previous section. notBefore is the beginning of the certificate's
validity period and notAfter is the end. These are represented as number
of seconds since January first, 1970 at midnight Greenwich Mean Time. publicKey
is the certificate subject's public key represented as an R_RSA_PUBLIC_KEY
as defined by RSAREF. Finally, the signature array contains the certificate's
signature, where signatureLen is its length in bytes.
To create a new user, you can use RIPEMGenerateKeys which generates
a public/private keypair and other important information.
You can use SelectCertChain to get the certificate chain for any user, for example to get that user's public key to use when encrypting a message, or to get the user's certificate serial number in order to revoke the user. SelectCertChain requires the distinguished name of the user in question, not just a the common name or "username". To get the distinguished name, you can use GetCertsBySmartname and then use DERToCertificate to decode the certificate which contains the distinguished name. (See GetDNSmartNameIndex in the next chapter for a definition of "smart name".) Note that GetCertsBySmartname may return multiple matches for a given smart name such as "common name = bob, organization = Gadgets" and "common name = Bob, organization = Widgets", so you need to make sure the application user picks the correct one.
You can use SetChainLenAllowed to set the chain length allowed of another user, and GetChainLenAllowed to get the current chain length allowed. These operations identify the user in question by the digest of that user's public key, which you should compute using GetPublicKeyDigest.
Each RIPEM user maintains a certificate revocation list (CRL). If the CRL expires (causing a CRL EXPIRED certificate status) you can use RIPEMUpdateCRL to update the CRL issued by the logged-in user. RIPEMUpdateCRL will also create a new CRL if one doesn't exist. By passing the serial number of another user, you can also use RIPEMUpdateCRL to revoke that user by adding the serial number to the CRL.
If the logged-in user's CRL is of interest to others, you can create a CRL message which can be sent to others. To create such a CRL message in PEM format, use RIPEMPublishCRLInit, RIPEMPublishCRLUpdate and RIPEMPublishCRLFinal. To create such a CRL message in PKCS format, use RIPEMCertsAndCRL_PKCSInit, RIPEMCertsAndCRL_PKCSUpdate and RIPEMCertsAndCRL_PKCSFinal. If the logged-in user's certificates are of interest to others, these functions can also be used add any or all of the certificates in the database to the message which can be sent to others.
If the user's certification preferences become corrupt (as indicated by RIPEMLoginUser), you can use RIPEMSavePreferences to save a fresh copy of the preferences in the database.
Use RIPEMCertifyRequestPKCS to create a PKCS-compliant certification
request which can be sent to a certification authority to obtain a certificate.
(Note that this certification request message is not a self-signed certificate.
To send a self-signed certificate to another user in a PKCS-compliant message,
simply use RIPEMEncipherPKCSInit, RIPEMEncipherPKCSUpdate and RIPEMEncipherPKCSFinal
as described in the next section.)
Use RIPEMEncipherPKCSInit, RIPEMEncipherPKCSUpdate and RIPEMEncipherPKCSFinal to create a PKCS-compliant signed, enveloped or signed and enveloped message. Use RIPEMDecipherPKCSInit, RIPEMDecipherPKCSUpdate and RIPEMDecipherPKCSFinal to decipher a PKCS-compliant message which may be a certs-and-CRLs-only message as well as signed, enveloped or signed and enveloped. Note that you can use RIPEMCertsAndCRL_PKCSInit, RIPEMCertsAndCRL_PKCSUpdate and RIPEMCertsAndCRL_PKCSFinal (as mentioned in the previous section) to create a PKCS-compliant certs-and-CRLs-only message.
To create a PEM-compliant CRL retrieval request message, use RIPEMRequestCRLsInit, RIPEMRequestCRLsUpdate and RIPEMRequestCRLsFinal.
RIPEM also supports "detached" PKCS signatures where the message data
is external to the PKCS signature information. Use RIPEMSignDetachedPKCSInit,
RIPEMSignDetachedPKCSDigestUpdate and RIPEMSignDetachedPKCSFinal to create
a PKCS-compliant detached signature. Use RIPEMVerifyDetachedPKCSInit, RIPEMVerifyDetachedPKCSDigestUpdate,
RIPEMVerifyDetachedPKCSUpdate and RIPEMVerifyDetachedPKCSFinal to verify
a PKCS-compliant detached signature.
R_realloc provides a more robust version of the standard realloc (such as freeing the buffer if it can't be reallocated.)
RIPEM functions return an error using a message string, however RSAREF functions do not. You can use FormatRSAError to convert an RSAREF error token into a message string.
CrackLine is a utility function to parse a string of comma-delimited items into a list of separate items. This is useful for parsing environment variables containing multiple items.
CrackRecipients is a utility function to parse a string of comma-delimited
email addresses into a list of separate items. This also extracts the user@domain
portions from addresses like " jefft@netcom.com (Jeff Thompson)". This
is useful for getting names of recipients in the To: field of an outgoing
message in order to encrypt the message for them.
Instead of using a RIPEM home directory, early versions of RIPEM required the user to explicitly specify input and output files for public and private keys. InitRIPEMDatabase will automatically open the public and private key files in the RIPEM home directory, but if you need to specify extra files which were created with early versions of RIPEM, you can use AddKeySourceFilename.
RIPEMGenerateKeys will automatically create a self-signed certificate
for the new user. However, if a user is upgrading from an early version
of RIPEM which did not have self-signed certificates, you can use WriteSelfSignedCert
to create one.
char *AddKeySourceFilename (TypKeySource *keySource, char *filename);
This adds the filename to the beginning of keySource's filelist. This copies the filename, so you do not need to preserve the string pointed to by filename after this is called. If filename is already in keySource's filelist, this does nothing. keySource points to a TypKeySource which should be either pubKeySource, privKeySource or crlSource within a RIPEMDatabase structure. (You must construct the RIPEMDatabase using RIPEMDatabaseConstructor before calling this.) This does not actually open the file given by filename. After you have used this to add one or more filenames to pubKeySource, privKeySource and crlSource in the RIPEMDatabase, you should call InitRIPEMDatabase to actually open the files.
Typically, you do not need to call AddKeySourceFilename. This is only provided for compatibility with RIPEM versions 1.1 and earlier where the user manipulated public and private key files outside the RIPEM home directory.
Returns: (char *)NULL for success, otherwise an error string.
char *AddToList (TypListEntry *prevEntry, void *entry, unsigned int entryLen, TypList *list);
This is a generic TypList handling function to add an entry to the list. Usually, you would call a more specific function like AppendLineToList. (See "Lists: The TypList Structure" in the previous chapter for a full discussion on handling lists.) You must initialize list using InitList before calling this. entry is a pointer to an allocated data block of length entryLen. This does not make a copy of the data pointed to by entry. However, it is assumed that entry points to data which you have allocated with malloc: be aware that FreeList will first zeroize the data block and then call free on it.
If prevEntry is (TypListEntry *)NULL, then the entry is added to the end if the list. Otherwise, prevEntry must be one of the existing TypListEntry nodes in the list, and the entry is inserted into the list after prevEntry. (To insert an entry at the beginning of the list, use PrependToList.)
Returns: (char *)NULL for success, otherwise an error string.
char *AppendLineToList (char *line, TypList *list);
This adds the line to the end of the list. (See "Lists: The TypList Structure" in the previous chapter for a full discussion on handling lists.) line is a null-terminated string. You must initialize list using InitList before calling this. This copies the line, so you do not need to preserve the string pointed to by line after this is called.
Returns: (char *)NULL for success, otherwise an error string.
char *CrackLine (char *line, TypList *valList);
This is a utility function to parse a line of delimited items and to return each as a separate item in a list. line is a null-terminated string containing items which are delimited by a comma or a '\n', such as "A, B \n C". You must initialize valList using InitList before calling this. Before processing the line, this calls FreeList on valList to make sure it is empty. Each item is added to valList as a null-terminated string entry. line itself is not modified by this function.
Whitespace before and after each item is removed, where whitespace is defined as ' ', '\t', or '\n'. (Even though '\n' is considered whitespace, it is also considered a delimiter between items.) If the length of any item is more than 1023 characters, it is broken into separate 1023 character items (due to an internal buffer length limit).
Returns: (char *)NULL for success, otherwise an error string.
char *CrackRecipients (char *line, TypList *recipientNames);
This is a utility function to parse a line of delimited email addresses and to return each as a separate item in a list. line is a null-terminated string containing email addresses which are delimited by a comma or a '\n'. You must initialize recipientNames using InitList before calling this. This does not call FreeList on recipientNames. Therefore, CrackRecipients may be called repeatedly to add more names to recipientNames. Each email address is added to recipientNames as a null-terminated string entry. line itself is not modified by this function. This is meant to be used to get usernames from To: and cc: fields from an email message you are encrypting in order to look up the recipients' certificates and public keys.
Whitespace before and after each email address is removed, where whitespace is defined as ' ', '\t', or '\n'. This extracts the user@domain portion from addresses like president@whitehouse.gov (The Prez) or "Mark Riordan" <riordanmr@clvax1.cl.msu.edu> and places only that portion in recipientNames.
Returns: (char *)NULL for success, otherwise an error string.
int DERToCertificate (unsigned char *der, CertificateStruct *cert, CertFieldPointers *fieldPointers);
This parses the certificate, returning the component parts in a CertificateStruct. (See "Decoding Certificates" in the previous chapter for details of the CertificateStruct type.) der points to the buffer containing the encoded certificate. You should declare a CertificateStruct and pass a pointer to it in cert, which is filled with the certificate components. On return, fieldPointers->innerDER points inside the der buffer to the beginning of the "inner" portion of the certificate and fieldPointers->innerDERLen is the length of the "inner" portion. (The inner DER is the data to be verified when checking the certificate signature. This is also the data which you should digest with MD5, using R_DigestBlock, to present a "self-signed certificate digest.") If fieldPointers is (CertFieldPointers *)NULL, it is ignored.
Returns: a negative value for any parsing error. Otherwise returns the total length of the der encoding.
char *FormatRSAError (int errorCode);
This is a utility function to covert an integer error return code from RSAREF to a error string in the style returned by RIPEM. For example, when called with an errorCode of RE_DIGEST_ALGORITHM, this returns "Message-digest algorithm is invalid". If called with an errorCode of 0 or an unrecognized value, this returns "Unknown error returned from RSAREF routines".
void FreeList (TypList *list);
This is the complement to InitList. You must call this when you are done with the TypList structure pointed to by list. This zeroizes all entry buffers in the list and calls free on each. This also returns list to the state it was in after the first call to InitList, so the list can be used again if necessary. Do not call FreeList for a list unless you have previously called InitList on it.
char *GetCertsBySmartname (RIPEMDatabase *ripemDatabase, TypList *certs, char *smartName, RIPEMInfo *ripemInfo);
This searches every file listed in ripemDatabase->pubKeySource for certificate records where the User: field matches the given value of smartName. This is called "smartName" because when RIPEM originally writes a certificate to the database, it uses the smart name of the certificate's subject distinguished name in the User: field. Each matching certificate is added to the TypList pointed to by certs. You must initialize the certs list using InitList before calling this. ripemInfo is used only for accessing the debugStream.
Returns: (char *)NULL for success, otherwise an error string.
unsigned int GetChainLenAllowed (RIPEMInfo *ripemInfo, unsigned char *publicKeyDigest);
This returns the chain length allowed for the user with the key given by publicKeyDigest. The public key in question usually comes from a user's certificate in a certificate chain. To get the public key digest, you should decode the certificate using DERToCertificate and pass the public key from the CertificateStruct to GetPublicKeyDigest.
This uses publicKeyDigest as an index into the certificate preferences in ripemInfo which were loaded during RIPEMLoginUser. If the public key digest is not found, this returns zero as the default, otherwise it returns the specified chain length allowed.
unsigned int GetDNSmartNameIndex (DistinguishedNameStruct *name);
This locates the "smart name" in the array of AVAs in name and returns its index. (See "Distinguished Names" in the previous chapter for a full discussion "distinguished name" and "AVA".) The returned value can be used as an index into name's AVATypes, AVATag and AVAValues arrays.
A distinguished name is usually big, such as " common name = fred@snark.edu, org unit = Persona Certificate, organization = RSA Data Security, Inc., country = US". Therefore, RIPEM defines the concept of a smart name which is the most distinctive of the distinguished name's attributes. (Typically, this is the common name.) The smart name is used to look up certificates, and (through looking up certificates) to identify the user during log in (-u in the RIPEM command line application) and to identify recipients of encrypted messages (-r in the RIPEM command line application).
RIPEM chooses a distinguished name's smart name in the following manner: If there is a common name AVA in the distinguished name, the smart name is the least significant common name. ("Least significant" is defined in "Handling Distinguished Names and Certificates" in the previous chapter.) If there is no common name, the smart name is the least significant title AVA. If there are no common name or title attributes, the smart name is the least significant AVA in the distinguished name, whatever that is.
char *GetDNSmartNameValue (DistinguishedNameStruct *name);
This returns the value of the distinguished name's smart name by calling GetDNSmartNameIndex and returning a pointer to the resulting AVAValue in the name. In other words, this is a function provided for convenience which returns name->AVAValues[GetDNSmartNameIndex (name)]. The return value is a null-terminated C string. See the description of GetDNSmartNameIndex for a definition of "smart name".
char *GetPublicKeyDigest (unsigned char *digest, R_RSA_PUBLIC_KEY *publicKey);
This returns the MD5 digest of the DER encoding of publicKey. The digest buffer which receives the digest must be at least 16 bytes long. This function is necessary for a subtle reason: There are multiple object identifiers for an RSA key that people use for the DER encoding of the public key, and so it is possible for the digest of the same public key to come out differently if the object identifier is different. Since many RIPEM functions, such as GetChainLenAllowed, rely on the public key digest to be a unique identifier of the public key, you should always use GetPublicKeyDigest to obtain it (as opposed, for example, to directly digesting the encoded public key yourself). GetPublicKeyDigest always encodes the publicKey with the same object identifier before computing the digest ("rsa" as opposed to "rsaEncoding", to be specific).
Returns: (char *)NULL for success, otherwise an error string.
char *GetUnvalidatedPublicKey (char *user, TypKeySource *source, R_RSA_PUBLIC_KEY *key, BOOL *found, RIPEMInfo *ripemInfo);
This is provided only as a fallback for compatibility with RIPEM 1.1 and earlier in the case that a public key for a user cannot be found through the normal method of calling SelectCertChain. If your application requires a public key to be validated within a certificate instead of just sitting out "in the raw", then you should not use this function.
user is the username whose public key you are looking for. source should point to the pubKeySource in the RIPEMDatabase which is already initialized with InitRIPEMDatabase. If the key is found, this sets found to TRUE and copies the public key to the R_RSA_PUBLIC_KEY pointed to by key. Otherwise, this sets found to FALSE and the R_RSA_PUBLIC_KEY is undefined. ripemInfo is used only for accessing the debugStream.
Returns: (char *)NULL for success, otherwise an error string. Note that for the simple case that the key for user is not found, this only sets found to FALSE and returns (char *)NULL.
void InitDistinguishedNameStruct (DistinguishedNameStruct *name);
This initializes the DistinguishedNameStruct pointed to by name by pre-zerozing and then setting all the AVATypes and RDN indexes to -1. This also presets all AVATag to ATTRTAG_PRINTABLE_STRING. You should always call InitDistinguishedNameStruct before constructing a new name so that the AVATypes and RDN indexes are -1 by default, and all you have to do is add the needed values. Also, when each DistinguishedNameStruct is pre-zeroized to the same initial value in this manner, two name structures with the same information can easily be compared with the bit-wise = = operator.
void InitList (TypList *list);
This initializes the TypList pointed to by list by setting up its pointers for an empty list. You should call this on any TypList structure you create before using it. (If the RIPEM API were C++, this would be the constructor for this type.) You must also call FreeList when you are done using the TypList.
void InitRIPEMAttributes (RIPEMAttributes *attributes);
This initializes the RIPEMAttributes structure pointed to by attributes by zerozing which sets all "have" flags to FALSE. You should always call InitRIPEMAttributes before preparing attributes to pass in to a function such as RIPEMCertifyRequestPKCS. In this way, all attributes are absent by default and only the ones you add by setting haveChallengePassword, for example, will be present.
char *InitRIPEMDatabase (RIPEMDatabase *ripemDatabase, char *homeDir, RIPEMInfo *ripemInfo);
This initializes ripemDatabase by opening the database files in the RIPEM home directory given by homeDir. You must construct ripemDatabase using RIPEMDatabaseConstructor before calling this. ripemInfo is used only for accessing the debugStream. The directory name given by homeDir must end in the appropriate directory separator for your operating system, such as '/' in Unix or a backslash in DOS. This is so that RIPEM can construct a full pathname for a file in the RIPEM home directory simply by appending the filename to homeDir.
You are also responsible for making sure the RIPEM home directory exists before calling InitRIPEMDatabase, since RIPEM is capable of creating new files but not new directories. Here is a simple way to test for the existence of the directory before calling InitRIPEMDatabase: Append "crls" to the homeDir and to use fopen to try to open it in append mode "a". If the directory exists, the crls file will be opened if the file exists, or created if the file does not exist (which is all right). If the directory does not exist, fopen will return (FILE *)NULL in which case you should use your platform's operating system call to create the directory. (Note that on most platforms, you need to make sure there is no directory separator at the end of the directory name when you create the directory.)
When you call InitRIPEMDatabase, pubKeySource, privKeySource and crlSource in ripemDatabase may already have filenames which you added with AddKeySourceFilename (only for compatibility with RIPEM versions 1.1 and earlier). InitRIPEMDatabase adds the filenames pubkeys, privkey and crls in the RIPEM home directory if they are not already in ripemDatabase. This also makes sure each of these files can be opened for output and that each is the first entry listed in their respective TypKeySource list. (The first file listed is the one RIPEM uses for output.) This then uses fopen to open all the files for read. This also sets ripemDatabase->preferencesFilename to preferen in the RIPEM home directory and makes sure it can be opened for output.
Returns: (char *)NULL for success, otherwise an error string.
int IsPrintableString (unsigned char *valuePointer, unsigned int valueLen);
This scans the buffer pointed to by valuePointer, of length valueLen, and returns 1 if all the characters are in the PrintableString character set, otherwise it returns 0. You should use this when constructing a new distinguished name: if IsPrintableString returns 1, you should set the AVATag to ATTRTAG_PRINTABLE_STRING, otherwise you should set it to ATTRTAG_T61_STRING.
char *PrependToList (void *entry, unsigned int entryLen, TypList *list);
This is a generic TypList handling function to add an entry to the beginning of the list. This function is a necessary companion to AddToList since, when the prevEntry parameter to AddToList is NULL, it will add the entry to the end of the list. (See "Lists: The TypList Structure" in the previous chapter for a full discussion on handling lists.) You must initialize list using InitList before calling this. entry is a pointer to an allocated data block of length entryLen. This does not make a copy of the data pointed to by entry. However, it is assumed that entry points to data which you have allocated with malloc: be aware that FreeList will first zeroize the data block and then call free on it.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertCursorInit (RIPEMDatabaseCursor *cursor, char *smartName, RIPEMDatabase *ripemDatabase);
This prepares the RIPEMDatabaseCursor structure pointed to by cursor to search for certificates in every file listed in ripemDatabase->pubKeySource. You must construct cursor using RIPEMDatabaseCursorConstructor and initialize ripemDatabase using InitRIPEMDatabase before calling this.
If smartName is (char *)NULL, this will cause RIPEMCertCursorUpdate to find all certificates in the database. Otherwise, this will cause RIPEMCertCursorUpdate to find certificate records in the database where the User: field matches the given value of smartName. This is called "smartName" because when RIPEM originally writes a certificate to the database, it uses the smart name of the certificate's subject distinguished name in the User: field.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertCursorUpdate (RIPEMDatabaseCursor *cursor, BOOL *found, TypList *certs, RIPEMDatabase *ripemDatabase, RIPEMInfo *ripemInfo);
This selects the next certificate from ripemDatabase according to the search criteria set up by RIPEMCertCursorUpdate. ripemInfo is used only for accessing the debugStream.
You must declare a BOOL and pass a pointer to it in found. If there are no more matching certificates, this sets found FALSE. Otherwise, this sets found TRUE and the matching certificate is added to the TypList structure pointed to by certs. You must initialize the certs list using InitList before calling this. You may keep calling RIPEMCertCursorUpdate until it sets found to FALSE.
You should not call any other functions which use ripemDatabase in between calls to RIPEMCertCursorUpdate.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertifyRequestPKCS (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, RIPEMAttributes *attributes);
Use this to create a PKCS-compliant certification request message (PKCS #10) for the user logged in to ripemInfo. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo.
attributes may point to a RIPEMAttributes structure which contains attributes to add to the certification request. (Before setting the values in the RIPEMAttributes structure you should call InitRIPEMAttributes.) If haveChallengePassword is TRUE, then challengePassword points to a null-terminated C string containing the challenge password. If haveUnstructuredName is TRUE, then unstructuredName points to a null-terminated C string containing the unstructured name. You are responsible for allocating the memory that challengePassword or unstructuredName points to. Other attributes in the RIPEMAttributes structure are ignored.
attributes may be (RIPEMAttributes *)NULL in which case no attributes are used.
To obtain the output, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEM can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
The output is "as is." No translation of '\n' to <CR><LF>should be done because the PKCS message format is binary.
(Note that this certification request message is not a self-signed certificate. To send a self-signed certificate to another user in a PKCS-compliant message, simply use RIPEMEncipherPKCSInit, RIPEMEncipherPKCSUpdate and RIPEMEncipherPKCSFinal.)
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertsAndCRL_PKCSFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, BOOL includeSenderCerts, BOOL includeCRL, RIPEMDatabase *ripemDatabase);
Use this to add issuer certificates or a CRL to a PKCS-compliant certs-and-CRLs-only message and to produce the final output. You should already have called RIPEMCertsAndCRL_PKCSInit and called RIPEMCertsAndCRL_PKCSUpdate zero or more times. ripemInfo points to the same structure which was initialized in RIPEMCertsAndCRL_PKCSInit.
If includeSenderCerts is TRUE, this adds the self-signed certificate for the user logged in to ripemInfo and any issuer certificates for the logged-in user. If includeCRL is true, this adds the CRL for the user by getting it from the database. (If neither includeSenderCerts or includeCRL is TRUE, the only use of this message is if certificates were added with RIPEMCertsAndCRL_PKCSUpdate.)
If includeCRL is TRUE, this returns an error if the CRL cannot be found or the signature is corrupt. Otherwise, the CRL is used even if it is expired.
The output is returned in partOut and partOutLen as in RIPEMCertsAndCRL_PKCSInit. RIPEMCertsAndCRL_PKCSFinal should only be called once per message.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertsAndCRL_PKCSInit (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *);
This initializes a PKCS-compliant certs-and-CRLs-only message can contain the CRL for the user logged in to ripemInfo, the certificates for the logged in user and any other certificates from the database. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo.
To obtain the output, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEM can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
The output is "as is." No translation of '\n' to <CR><LF>should be done because the PKCS message format is binary.
After calling this, you should call RIPEMCertsAndCRL_PKCSUpdate zero or more times to add extra certificates, and RIPEMCertsAndCRL_PKCSFinal output the CRL or issuer certificates and to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMCertsAndCRL_PKCSUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, TypList *certs);
Call this zero or more times to add extra certificates to a PKCS-compliant certs-and-CRLs-only message. You should already have called RIPEMCertsAndCRL_PKCSInit. ripemInfo points to the same structure which was initialized in RIPEMCertsAndCRL_PKCSInit. The output is returned in partOut and partOutLen as in RIPEMCertsAndCRL_PKCSInit.
Each entry in the certs list is added to the certs-and-CRLs-only message. Note that this can be called an arbitrarily large number of times without overrunning memory. You can use RIPEMCertCursorUpdate to repeatedly select a certificate from the database and then add it to the message. If you do this, then you can use FreeList to clear the certs list after it has been written to the message, allowing you to select more certificates from the database.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMChangePassword (RIPEMInfo *ripemInfo, unsigned char *newPassword, unsigned int newPasswordLen, RIPEMDatabase *ripemDatabase);
This re-encrypts the user's private key with a new password and writes it to the first private key file listed privKeySource in ripemDatabase. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and to load the private key. The new password is supplied in the newPassword buffer of length newPasswordLen.
When the private key is re-written to the private key file, the smart name of ripemInfo->userDN is used in a User: field to identify it. If ripemInfo->z.usernameAliases is not NULL, then it points to a TypList where each entry is a null-terminated string containing an alias for the username and each is also written in a User: field. The username aliases are only useful for RIPEM version 1.1 and earlier when the recipient of a message was identified by an email address instead of the public key. If you do not require compatibility with version 1.1 and earlier, then you may leave ripemInfo->z.usernameAliases NULL as it is initialized by RIPEMInfoConstructor.
This also calls RIPEMSavePreferences so that the preferences are re-authenticated under the new password. Therefore, this will return an error if ripemDatabase->preferencesFilename cannot be opened.
Returns: (char *)NULL for success, otherwise an error string.
void RIPEMDatabaseConstructor (RIPEMDatabase *ripemDatabase);
This initializes the RIPEMDatabase structure pointed to by ripemDatabase. You should call this on any RIPEMDatabase structure you create before using it. (If the RIPEM API were C++, this would be the constructor for this type.) You must also call RIPEMDatabaseDestructor when you are done using the RIPEMDatabase.
void RIPEMDatabaseCursorConstructor (RIPEMDatabaseCursor *cursor);
This initializes the RIPEMDatabaseCursor structure pointed to by cursor. You should call this on any RIPEMDatabaseCursor structure you create before using it. (If the RIPEM API were C++, this would be the constructor for this type.) You must also call RIPEMDatabaseCursorDestructor when you are done using the RIPEMDatabaseCursor.
void RIPEMDatabaseCursorDestructor (RIPEMDatabaseCursor *cursor);
This finalizes the RIPEMDatabaseCursor structure pointed to by cursor. This is the complement to RIPEMDatabaseCursorConstructor. You must call this when you are done with the RIPEMDatabaseCursor structure. (If the RIPEM API were C++, this would be the destructor for this type.)
void RIPEMDatabaseDestructor (RIPEMDatabase *ripemDatabase);
This finalizes the RIPEMDatabase structure pointed to by ripemDatabase by closing all the files in pubKeySource, privKeySource and crlSource. This is the complement to RIPEMDatabaseConstructor. You must call this when you are done with the RIPEMDatabase structure. (If the RIPEM API were C++, this would be the destructor for this type.)
char *RIPEMDecipherFinal (RIPEMInfo *ripemInfo, TypList *certChain, ChainStatusInfo *chainStatus, enum enhance_mode *enhanceMode);
Use this to finalize the processing of a PEM-compliant message which you are deciphering. You should already have called RIPEMDecipherUpdate one or more times to process the message. ripemInfo points to the same structure which was initialized in RIPEMDecipherInit. You must initialize a TypList using InitList and pass a pointer to it in certChain. You must also declare a ChainStatusInfo structure and pass a pointer to it in chainStatus. This returns the sender's certificate chain in certChain and the certification status of the certificates in chainStatus.
You must declare an enum enhance_mode and pass a pointer to it in enhanceMode. RIPEM uses this to return the enhancement mode of the message which is MODE_ENCRYPTED for an encrypted message, MODE_MIC_ONLY for a mic-only message, MODE_MIC_CLEAR for a mic-clear message or MODE_CRL for a CRL message. For a MODE_CRL, certChain is unmodified since there are no senders, and chainStatus->overall is set to zero. In all these cases, any certificates or CRLs in the message are added to the database. RIPEM checks that the signature from the issuer of CRLs is valid, but does not check the signature on certificates. (This is done more efficiently by SelectCertChain when retrieving the certificates.)
If enhanceMode is not MODE_CRL and chainStatus->overall is zero, RIPEM could not find a trusted certificate for the sender. In this case, if the message contained a self-signed certificate from the sender and certChain contains one entry which is the self-signed certificate. (You may check for an entry in certChain by checking if certChain->firstptr is non-null.) This would typically happen when receiving a message from an unknown sender which the application user may want to validate. You may decode the certificate using DERToCertificate and present the self-signed certificate digest to the user who may choose whether to validate the certificate. (Use R_DigestBlock with the digest algorithm specified in the certificate to digest the certificate's inner DER.) To validate, see ValidateAndWriteCert. On the other hand, if there is no entry in certChain (certChain->firstptr is null) then there was no self-signed certificate in the message, which means that the sender is completely unrecognized. You may suggest to the user that the sender provide a message with a self-signed certificate.
If chainStatus->overall is CERT_UNVALIDATED, RIPEM could not find a trusted certificate for the sender, but it could find an unvalidated public key. In this case, certChain contains one entry which is the sender's username. This is provided only for compatibility with RIPEM version 1.1 and earlier which did not use certificates. If you do not wish to support unvalidated public keys, then treat this like the completely unrecognized sender as above.
For other values of chainStatus->overall, certChain and chainStatus contain values as described in SelectCertChain. Note that the sender name is the subject name of the first certificate listed in certChain.
Returns: (char *)NULL for success, otherwise an error string. There is a special error strings which RIPEMDecipherFinal may return: ERR_NO_PEM_HEADER_BEGIN. (You should use strcmp to compare the error return string to the value.) You may wish to "catch" this special error cases and give it special treatment as follows:
If this returns ERR_NO_PEM_HEADER_BEGIN, then the string "-----BEGIN PRIVACY-ENHANCED MESSAGE-----" could not be found. This string marks the beginning of the cryptographic information in a PEM message. The message may be in PKCS format and you may wish to try to process the message using RIPEMDecipherPKCSInit, etc.
char *RIPEMDecipherInit (RIPEMInfo *ripemInfo, BOOL prependHeaders);
This initializes ripemInfo for deciphering a PEM-compliant message. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. If prependHeaders is TRUE, RIPEM will copy email headers (encountered before the begin enhanced message boundary) from the input message through to the output. Otherwise, prependHeaders should be FALSE.
After calling RIPEMDecipherInit, you should call RIPEMDecipherUpdate to decipher the message by parts, and RIPEMDecipherFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMDecipherPKCSFinal (RIPEMInfo *ripemInfo, TypList *certChain, ChainStatusInfo *chainStatus, int *pkcsMode, RIPEMAttributes *authenticatedAttributes);
Use this to finalize the processing of a PKCS-compliant message which you are deciphering. You should already have called RIPEMDecipherPKCSUpdate one or more times to process the message. ripemInfo points to the same structure which was initialized in RIPEMDecipherPKCSInit. You must initialize a TypList using InitList and pass a pointer to it in certChain. You must also declare a ChainStatusInfo structure and pass a pointer to it in chainStatus. This returns the sender's certificate chain in certChain and the certification status of the certificates in chainStatus.
You must declare an int and pass a pointer to it in pkcsMode. RIPEM uses this to return the PKCS mode of the message which is PKCS_SIGNED for a signed message, PKCS_ENVELOPED for an enveloped-only message, PKCS_SIGNED | PKCS_ENVELOPED for a signed and enveloped message or PKCS_CERTS_AND_CRLS_ONLY for a certs-and-CRLs-only message. For PKCS_ENVELOPED and PKCS_CERTS_AND_CRLS_ONLY, certChain is unmodified since there are no senders, and chainStatus->overall is set to zero. In all these cases, any certificates or CRLs in the message are added to the database. RIPEM checks that the signature from the issuer of CRLs is valid, but does not check the signature on certificates. (This is done more efficiently by SelectCertChain when retrieving the certificates.)
If pkcsMode is PKCS_SIGNED or PKCS_SIGNED | PKCS_ENVELOPED and chainStatus->overall is zero, RIPEM could not find a trusted certificate for the sender. In this case, the message contained a self-signed certificate from the sender and certChain contains one entry which is the self-signed certificate. This would typically happen when receiving a message from an unknown sender which the application user may want to validate. You may decode the certificate using DERToCertificate and present the self-signed certificate digest to the user who may choose whether to validate the certificate. (Use R_DigestBlock with the digest algorithm specified in the certificate to digest the certificate's inner DER.) To validate, see ValidateAndWriteCert.
RIPEMDecipherPKCSFinal does not support a chain status of CERT_UNVALIDATED as RIPEMDecipherFinal does, since PKCS-compliant messages always use a certificate to identify senders and recipients.
For other values of chainStatus->overall, certChain and chainStatus contain values as described in SelectCertChain. Note that the sender name is the subject name of the first certificate listed in certChain.
If authenticatedAttributes is not NULL, then it points to a RIPEMAttributes structure which receives the authenticated attributes in the message, if any. Upon return, if haveSigningTime is TRUE, then signingTime contains the signing time represented as the number of seconds since January first, 1970 at midnight Greenwich Mean Time. If haveSigningDescription is TRUE, then signingDescription points to a null-terminated C string containing the signing description. The memory for the signingDescription is allocated inside ripemInfo and should be treated as "read only". Other attributes in the RIPEMAttributes structure are undefined. Upon return, the value of an attribute must be copied before a future call to RIPEM since RIPEM may modify it.
authenticatedAttributes may be (RIPEMAttributes *)NULL, in which case it is ignored.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMDecipherPKCSInit (RIPEMInfo *ripemInfo);
This initializes ripemInfo for deciphering a PKCS-compliant message. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo.
After calling RIPEMDecipherPKCSInit, you should call RIPEMDecipherPKCSUpdate to decipher the message by parts, and RIPEMDecipherPKCSFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMDecipherPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned int partInLen, RIPEMDatabase *ripemDatabase);
Use this to process a PKCS-compliant message which you are deciphering and to produce the output which is decoded and decrypted as necessary. You should already have called RIPEMDecipherPKCSInit. ripemInfo points to the same structure which was initialized in RIPEMDecipherPKCSInit. ripemDatabase must already be initialized using InitRIPEMDatabase and is used to process the certificate information in the message.
partIn points to the buffer of the input message to process, of length partInLen. partIn may contain any number of bytes.
To obtain the output from each call to RIPEMDecipherPKCSUpdate, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEMDecipherPKCSUpdate can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
The input and output are "as is." No translation of '\n' to <CR><LF>is done because the PKCS message format can support binary data. The calling routine must do end-of-line character translation on the output text if necessary.
You can process a message of unlimited length by calling this multiple times by reading in a part of the message and calling RIPEMDecipherPKCSUpdate, reading in the next part, calling again, etc. When you are done processing the message in this manner, proceed to use RIPEMDecipherPKCSFinal.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMDecipherUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned int partInLen, RIPEMDatabase *ripemDatabase);
Use this to process a PEM-compliant message which you are deciphering and to produce the output which is decoded and decrypted as necessary. You should already have called RIPEMDecipherInit. ripemInfo points to the same structure which was initialized in RIPEMDecipherInit. ripemDatabase must already be initialized using InitRIPEMDatabase and is used to process the certificate information in the message header.
partIn points to the buffer of the input message to process, of length partInLen. Textual lines must be delimited by the '\n' character (not <CR><LF>). This can be done, for example, by using fgets or fread to read from a file which has been opened in text mode "r". partIn may contain parts of lines or multiple lines.
To obtain the output from each call to RIPEMDecipherUpdate, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEMDecipherUpdate can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
Textual lines in the output are delimited by the character '\n'. You must convert these to your platform's local line delimiter, which is done automatically if you use fwrite to write the output to a file which has been opened in text mode "w".
You can process a message of unlimited length by calling this multiple times by reading in a part of the message and calling RIPEMDecipherUpdate, reading in the next part, calling again, etc. When you are done processing the message in this manner, proceed to use RIPEMDecipherFinal.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherDigestUpdate (RIPEMInfo *ripemInfo, unsigned char *partIn, unsigned int partInLen);
Use this to digest the text of a PEM-compliant message which you are enciphering. You should already have called RIPEMEncipherInit. ripemInfo points to the same structure which was initialized in RIPEMEncipherInit. partIn points to the buffer of text to digest, of length partInLen. Textual lines must be delimited by the '\n' character (not <CR><LF>). This can be done, for example, by using fgets or fread to read from a file which has been opened in text mode "r". Make sure there is a '\n' at the end of the final line, which is ensured on most platforms if the file is read using fgets.
You can process text of unlimited length by calling this multiple times by reading in a part of the text and calling RIPEMEncipherDigestUpdate, reading in the next part, calling again, etc. When you are done digesting the text in this manner, proceed to use RIPEMEncipherUpdate.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, RIPEMDatabase *ripemDatabase);
Use this to finalize the processing of a PEM-compliant message which you are enciphering and to produce the final part of the output. You should already have called RIPEMEncipherUpdate one or more times to process the text. (If neither RIPEMEncipherDigestUpdate or RIPEMEncipherUpdate has been called, this will still output a message with empty text.) ripemInfo points to the same structure which was initialized in RIPEMEncipherInit. ripemDatabase must already be initialized using InitRIPEMDatabase and is used for selecting certificates to find issuer names and serial numbers of recipients when using MESSAGE_FORMAT_PEM. The output is returned in partOut and partOutLen as in RIPEMEncipherUpdate. RIPEMEncipherFinal should only be called once per message.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherInit (RIPEMInfo *ripemInfo, enum enhance_mode enhanceMode, int messageFormat, int digestAlgorithm, int encryptionAlgorithm, RecipientKeyInfo *recipientKeys, unsigned int recipientKeyCount);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This initializes ripemInfo for enciphering a PEM-compliant message. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. enhanceMode must be MODE_ENCRYPTED, MODE_MIC_ONLY, or MODE_MIC_CLEAR. digestAlgorithm must be DA_MD2, DA_MD5 or DA_SHA1.
If enhanceMode is MODE_ENCRYPTED, then encryptionAlgorithm must be EA_DES_CBC or EA_DES_EDE2_CBC. ripemInfo->randomStruct must be seeded (see Appendix A for RSAREF documentation on R_RandomUpdate). Also, recipientKeys must point to an array of RecipientKeyInfo structures of length recipientKeyCount (one for each message recipient). RecipientKeyInfo is defined as:
typedef struct { R_RSA_PUBLIC_KEY publicKey; char *username; } RecipientKeyInfo;The publicKey holds the public key of the message recipient. You should obtain the recipient's public key by using SelectCertChain and using DERToCertificate to decode the user's certificate which contains the public key. The username in the RecipientKeyInfo points to a string containing the recipient's username. The username is used for backward compatibility with RIPEM 1.1 (under MESSAGE_FORMAT_RIPEM1 mode) and for looking up the recipient's issuer name and serial number (under MESSAGE_FORMAT_PEM mode). The array pointed to by recipientKeys (and the strings pointed to by each username) must remain valid until the first call to RIPEMEncipherUpdate.
Also, for MODE_ENCRYPTED, note that you should add an entry in recipientKeys for the user logged in to ripemInfo (the -T m option in the RIPEM command line application). This is so that after creating an encrypted message, the user can decrypt it later if necessary. To make a recipientKey entry for the logged-in user, set the publicKey to ripemInfo->publicKey and set the username to GetDNSmartNameValue(&ripemInfo->userDN).
messageFormat must be MESSAGE_FORMAT_RIPEM1 or MESSAGE_FORMAT_PEM. MESSAGE_FORMAT_RIPEM1 is compatible with all versions of RIPEM including those before 2.0. It has a Proc-Type version of 2001 and includes the Originator-Name field and uses Recipient-Key-Asymmetric (containing the public key) for the recipients of encrypted messages. MESSAGE_FORMAT_PEM is for compatibility with the RFC 1421 standards suite. It has a Proc-Type version of 4 and omits RIPEM-specific fields (such as Originator-Name) and uses only Recipient-ID-Asymmetric (containing issuer name and serial number) for the recipients of encrypted messages. (See the section "Specifying Message Format" in the RIPEM user manual and the document RIPEMFMT.TXT for more details.)
After calling RIPEMEncipherInit, you should call RIPEMEncipherDigestUpdate to digest the text by parts, RIPEMEncipherUpdate to enhance the text by parts, and RIPEMEncipherFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherPKCSFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, RIPEMAttributes *authenticatedAttributes);
Use this to finalize the processing of a PKCS-compliant message which you are enciphering and to produce the final part of the output. You should already have called RIPEMEncipherPKCSUpdate one or more times to process the text. (If RIPEMEncipherPKCSUpdate has not been called, this will still output a message with empty text.) ripemInfo points to the same structure which was initialized in RIPEMEncipherPKCSInit. You should pass (RIPEMAttributes *)NULL for authenticatedAttributes, which is provided for future compatibility.
The output is returned in partOut and partOutLen as in RIPEMEncipherPKCSUpdate. RIPEMEncipherPKCSFinal should only be called once per message.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherPKCSInit (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, int pkcsMode, int digestAlgorithm, int encryptionAlgorithm, RecipientKeyInfo *recipientKeys, unsigned int recipientKeyCount, RIPEMDatabase *ripemDatabase);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This initializes ripemInfo for enciphering a PKCS-compliant message. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. pkcsMode must be PKCS_SIGNED for a signed message, PKCS_ENVELOPED for an enveloped message, or PKCS_SIGNED | PKCS_ENVELOPED for a signed and enveloped message. ripemDatabase must already be initialized using InitRIPEMDatabase and is used for selecting certificates to find issuer names and serial numbers of recipients. digestAlgorithm must be DA_MD2, DA_MD5 or DA_SHA1.
If pkcsMode is PKCS_ENVELOPED or PKCS_SIGNED | PKCS_ENVELOPED, then encryptionAlgorithm must be EA_DES_CBC, EA_DES_EDE3_CBC or EA_RX2_CBC(effectiveBits). These are defined in rsaref.h. ripemInfo->randomStruct must be seeded (see Appendix A for RSAREF documentation on R_RandomUpdate). Also, recipientKeys must point to an array of RecipientKeyInfo structures of length recipientKeyCount (one for each message recipient). RecipientKeyInfo is defined as:
typedef struct { R_RSA_PUBLIC_KEY publicKey; char *username; } RecipientKeyInfo;The publicKey holds the public key of the message recipient. You should obtain the recipient's public key by using SelectCertChain and using DERToCertificate to decode the user's certificate which contains the public key. The username in the RecipientKeyInfo points to a string containing the recipient's username. The username is used for looking up the recipient's issuer name and serial number.
Also, for PKCS_ENVELOPED or PKCS_SIGNED | PKCS_ENVELOPED, note that you should add an entry in recipientKeys for the user logged in to ripemInfo (the -T m option in the RIPEM command line application). This is so that after creating an encrypted message, the user can decrypt it later if necessary. To make a recipientKey entry for the logged-in user, set the publicKey to ripemInfo->publicKey and set the username to GetDNSmartNameValue(&ripemInfo->userDN).
To obtain the output from RIPEMEncipherPKCSInit, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEMEncipherPKCSInit can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
After calling RIPEMEncipherPKCSInit, you should call RIPEMEncipherPKCSUpdate to enhance the text by parts, and RIPEMEncipherPKCSFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned int partInLen);
Use this to process the text of a PKCS-compliant message which you are enciphering and to produce the output which is signed and encrypted as necessary. ripemInfo points to the same structure which was initialized in RIPEMEncipherPKCSInit. partIn points to the buffer of text to process, of length partInLen. The output is returned in partOut and partOutLen as in RIPEMEncipherPKCSInit.
The input and output are "as is." No translation of '\n' to <CR><LF>is done because the PKCS message format can support binary data. The calling routine must do end-of-line character translation on the input if necessary before passing it to RIPEMEncipherPKCSUpdate.
You can process text of unlimited length by calling this multiple times by reading in a part of the text and calling RIPEMEncipherPKCSUpdate, reading in the next part, calling again, etc. When you are done digesting the text in this manner, proceed to use RIPEMEncipherPKCSFinal.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMEncipherUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned int partInLen, RIPEMDatabase *ripemDatabase);
Use this to process the text of a PEM-compliant message which you are enciphering and to produce the output which is encoded and encrypted as necessary. You should already have called RIPEMEncipherDigestUpdate one or more times. ripemInfo points to the same structure which was initialized in RIPEMEncipherInit. ripemDatabase must already be initialized using InitRIPEMDatabase and is used for selecting certificates to find issuer names and serial numbers of recipients when using MESSAGE_FORMAT_PEM. partIn points to the buffer of text to process, of length partInLen. Textual lines must be delimited by the '\n' character as in RIPEMEncipherDigestUpdate.
To obtain the output from each call to RIPEMEncipherUpdate, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEMEncipherUpdate can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
Textual lines in the output are delimited by the character '\n'. You must convert these to your platform's local line delimiter, which is done automatically if you use fwrite to write the output to a file which has been opened in text mode "w".
You can process text of unlimited length by calling this multiple times just as you did for RIPEMEncipherDigestUpdate. On the first call to RIPEMEncipherUpdate, the output contains the message header. And of course the data that you pass to RIPEMEncipherUpdate should be the exact same data you passed to RIPEMEncipherDigestUpdate. This means that if the input is coming from a file, you must remember to rewind the file between the final call to RIPEMEncipherDigestUpdate and the first call to RIPEMEncipherUpdate. When you are done processing the text in this manner, proceed to use RIPEMEncipherFinal.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMGenerateKeys (RIPEMInfo *ripemInfo, unsigned int bits, unsigned int validityMonths, int digestAlgorithm, unsigned char *password, unsigned int passwordLen, RIPEMDatabase *ripemDatabase);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This generates a public/private keypair with a modulus of the given number of bits and creates a self-signed certificate and initial CRL, using digestAlgorithm to sign the certificate and CRL. You must construct ripemInfo using RIPEMInfoConstructor before calling this. ripemInfo->randomStruct must be seeded (see Appendix A for RSAREF documentation on R_RandomUpdate). ripemInfo->userDN should be set to the new user's distinguished name. (See "Distinguished Names " in the previous chapter for details on constructing a distinguished name.) ripemDatabase must already be initialized by InitRIPEMDatabase.
When the private key or self-signed certificate is written to the database files, the smart name of ripemInfo->userDN is used in a User: field to identify it. Also, if ripemInfo->z.usernameAliases is not NULL, then it points to a TypList where each entry is a null-terminated string containing an alias for the username and each is also written in a User: field for the private key. The username aliases are only useful for RIPEM version 1.1 and earlier when the recipient of a message was identified by an email address instead of the public key. If you do not require compatibility with version 1.1 and earlier, then you may leave ripemInfo->z.usernameAliases NULL as it is initialized by RIPEMInfoConstructor.
This also writes the user's initial default preferences. Therefore, this will return an error if ripemDatabase->preferencesFilename cannot be opened.
Returns: (char *)NULL for success, otherwise an error string.
void RIPEMInfoConstructor (RIPEMInfo *ripemInfo);
This initializes the RIPEMInfo structure pointed to by ripemInfo. You should call this on any RIPEMInfo structure you create before using it. (If the RIPEM API were C++, this would be the constructor for this type.) You must also call RIPEMInfoDestructor when you are done using the RIPEMInfo.
RIPEMInfoConstructor sets ripemInfo->debug to zero by default. If you want a higher level of debug messages, you must set debug to the level and set ripemInfo->debugStream to the output stream for the debug messages.
Note that some of the data members of the RIPEMInfo structure are within a struct named z for "zeroized". This is done so that RIPEMInfoConstructor can efficiently pre-zeroize these data members simply by zeroizing the z structure.
void RIPEMInfoDestructor (RIPEMInfo *ripemInfo);
This finalizes the RIPEMInfo structure pointed to by ripemInfo by zeroizing sensitive data such as the private key and freeing buffers and lists which were allocated during the use of the RIPEMInfo. Note that this does not close the file pointed to by ripemInfo->debugStream since this is opened by the calling application. Nor does this free the TypList pointed to by ripemInfo->userList (if the application sets it) since this is created by the calling application. RIPEMInfoDestructor is the complement to RIPEMInfoConstructor. You must call this when you are done with the RIPEMInfo structure. (If the RIPEM API were C++, this would be the destructor for this type.)
char *RIPEMLoginUser (RIPEMInfo *ripemInfo, char *username, RIPEMDatabase *ripemDatabase, unsigned char *password, unsigned int passwordLen);
This initializes ripemInfo with a user's public key, private key and other information. You must construct ripemInfo using RIPEMInfoConstructor and initialize ripemDatabase using InitRIPEMDatabase before calling this. username should be the smart name of the user's distinguished name. Typically, this is the common name attribute of the distinguished name. (See the description of GetDNSmartNameIndex for a definition of "smart name".)
RIPEM locates the first record in ripemDatabase's private key files where the User: field matches the supplied username. RIPEM then uses the supplied password, of length passwordLen, to decrypt the private key and places it in ripemInfo->privateKey. RIPEM then constructs the user's public key from the private key and places it in ripemInfo->publicKey. To find the user's self-signed certificate, RIPEM locates the first record in ripemDatabase's public key files where the User: field matches the username, the associated certificate's public key is the user's public key, and the certificate is a valid self-signed certificate (with the same issuer and subject name). The user's distinguished name is placed in ripemInfo->userDN and the encoded self-signed certificate is placed in ripemInfo->z.userCertDER of length ripemInfo->z.userCertDERLen. RIPEM also loads the user's preferences from ripemDatabase's preference file.
This returns (char *)NULL for success, otherwise an error string. There are some special error strings which RIPEMLoginUser may return: ERR_PREFERENCES_NOT_FOUND, ERR_PREFERENCES_CORRUPT and ERR_SELF_SIGNED_CERT_NOT_FOUND. (You should use strcmp to compare the error return string to the value such as ERR_PREFERENCES_NOT_FOUND.) You may wish to "catch" any of these special error cases and give it special treatment as follows:
If this returns ERR_PREFERENCES_NOT_FOUND then the user has been successfully logged in, but the entry in the preferences file for the user doesn't exist. In this case, you should alert the application user that default preferences will be used. The preferences will be saved in the preferences file by the next call to RIPEMSavePreferences or other functions like SetChainLenAllowed which save the preferences. If the user believes that the preferences should have been there, the user may wish to abort since this may mean the preferences file has been tampered with. However, if the user is upgrading from RIPEM 1.2, ERR_PREFERENCES_NOT_FOUND is a normal condition since RIPEM 1.2 did not use preferences.
If this returns ERR_PREFERENCES_CORRUPT, then the user has been successfully logged in, but the preferences information decodes badly or the signature doesn't verify. In this case, you should alert the application user that default preferences will be used and any previous preferences must be set again. New preferences will be saved by the next call to RIPEMSavePreferences or other functions like SetChainLenAllowed which save the preferences.
If this returns ERR_SELF_SIGNED_CERT_NOT_FOUND, ripemInfo->publicKey and ripemInfo->privateKey have already been set in ripemInfo, but RIPEM could not find the user's self-signed certificate. This is an error condition unless you are trying to upgrade the user from RIPEM 1.1 which did not have self-signed certificates. In this case, you may set ripemInfo->userDN and call WriteSelfSignedCert. (See the description of WriteSelfSignedCert.) If your application does not support upgrading from RIPEM 1.1, you should treat ERR_SELF_SIGNED_CERT_NOT_FOUND as a fatal error.
char *RIPEMPublishCRL (RIPEMInfo *ripemInfo, unsigned char **output, unsigned int *outputLen, int messageFormat, RIPEMDatabase *ripemDatabase);
This produces a PEM-compliant CRL message containing the CRL for the user logged in to ripemInfo. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase to initialize ripemDatabase.
To obtain the output, you should declare an unsigned char * and pass a pointer to this in output, and also declare an unsigned int and pass a pointer to this in outputLen, so that RIPEMPublishCRL can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in output is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in output. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
See RIPEMPublishCRLInit for the meaning of messageFormat.
RIPEMPublishCRL internally just calls RIPEMPublishCRLInit and RIPEMPublishCRLFinal. It is provided for backward compatibility with earlier versions of the RIPEM API.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMPublishCRLFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen);
Use this to produce the final output of a PEM-compliant CRL message. You should already have called RIPEMPublishCRLInit and called RIPEMPublishCRLUpdate zero or more times. ripemInfo points to the same structure which was initialized in RIPEMPublishCRLInit. The output is returned in partOut and partOutLen as in RIPEMPublishCRLInit. RIPEMPublishCRLFinal should only be called once per message.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMPublishCRLInit (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, int messageFormat, RIPEMDatabase *ripemDatabase);
This initializes a PEM-compliant CRL message containing the CRL for the user logged in to ripemInfo. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase to initialize ripemDatabase.
To obtain the output, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEM can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
This adds Originator-Certificate and Issuer-Certificate fields to the CRL message if possible. If messageFormat is MESSAGE_FORMAT_PEM and there is only one issuer chain, then the Originator-Certificate field in the message will have the certificate from that issuer. Otherwise, messageFormat should be MESSAGE_FORMAT_RIPEM1 and the Originator-Certificate field will have the user's self-signed certificate. (This is the same technique used when enciphering a message.)
This returns an error if the CRL cannot be found or the signature is corrupt. Otherwise, the CRL is used even if it is expired.
Textual lines in the output are delimited by the character '\n'. You must convert these to your platform's local line delimiter, which is done automatically if you use fwrite to write the output to a file which has been opened in text mode "w".
After calling this, you should call RIPEMPublishCRLUpdate zero or more times to add extra certificates, and RIPEMPublishCRLFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMPublishCRLUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, TypList *certs);
Call this zero or more times to add extra certificates to a PEM-compliant CRL message. You should already have called RIPEMPublishCRLInit. ripemInfo points to the same structure which was initialized in RIPEMPublishCRLInit. The output is returned in partOut and partOutLen as in RIPEMPublishCRLInit.
Each entry in the certs list is added as an "Issuer-Certificate" field to the CRL message. This is not explicitly permitted by the PEM RFCs, but it is a way to export certificates for other users to import into their database. Note that this can be called an arbitrarily large number of times without overrunning memory. You can use RIPEMCertCursorUpdate to repeatedly select a certificate from the database and then add it to the CRL message. If you do this, then you can use FreeList to clear the certs list after it has been written to the message, allowing you to select more certificates from the database.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMRequestCRLsFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen);
Use this to produce the final output of a PEM-compliant CRL retrieval request. You should already have called RIPEMRequestCRLsUpdate one or more times. ripemInfo points to the same structure which was initialized in RIPEMRequestCRLsInit. The output is returned in partOut and partOutLen as in RIPEMRequestCRLsInit. RIPEMRequestCRLsFinal should only be called once per message.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMRequestCRLsInit (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen);
This initializes ripemInfo for producing a PEM-compliant CRL retrieval request message. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo.
To obtain the output, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEM can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
Textual lines in the output are delimited by the character '\n'. You must convert these to your platform's local line delimiter, which is done automatically if you use fwrite to write the output to a file which has been opened in text mode "w".
After calling this, you should call RIPEMRequestCRLsUpdate to output the information for each requested CRL, and RIPEMRequestCRLsFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMRequestCRLsUpdate (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, DistinguishedNameStruct *name);
Use this to output the name of the issuer for the CRL you are requesting in a PEM-compliant CRL retrieval request message. The issuer is given by the DistinguishedNameStruct pointed to by name. Typically, this is the issuer in the CertificateStruct returned by DERToCertificate when decoding the certificates in a certificate chain. You should already have called RIPEMRequestCRLsInit. ripemInfo points to the same structure which was initialized in RIPEMRequestCRLsInit. The output is returned in partOut and partOutLen as in RIPEMRequestCRLsInit.
To request multiple CRLs (such as one for the issuer of each certificate in a chain), you can call RIPEMRequestCRLsUpdate multiple times, passing the name of a different issuer each time.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMSavePreferences (RIPEMInfo *ripemInfo, RIPEMDatabase *ripemDatabase);
This saves the preferences in ripemInfo by writing them to ripemDatabase->preferencesFilename. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase to initialize ripemDatabase.
You might want to call this to explicitly save the preferences if RIPEMLoginUser returns ERR_PREFERENCES_NOT_FOUND or ERR_PREFERENCES_CORRUPT. Note that RIPEM functions SetChainLenAllowed, RIPEMChangePassword and RIPEMUpdateCRL which modify the preferences implicitly save the preferences, so you do not need to call RIPEMSavePreferences in this case.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMSignDetachedPKCSFinal (RIPEMInfo *ripemInfo, unsigned char **partOut, unsigned int *partOutLen, RIPEMAttributes *authenticatedAttributes);
Use this to finalize the creating of a PKCS-compliant detached signature and to output the detached signature data. You should already have called RIPEMSignDetachedPKCSDigestUpdate one or more times to digest the text. (If RIPEMSignDetachedPKCSDigestUpdate has not been called, this will still output the signature of zero-length message.) ripemInfo points to the same structure which was initialized in RIPEMSignDetachedPKCSInit. You should pass (RIPEMAttributes *)NULL for authenticatedAttributes, which is provided for future compatibility.
To obtain the output from RIPEMSignDetachedPKCSFinal, you should declare an unsigned char * and pass a pointer to this in partOut, and also declare an unsigned int and pass a pointer to this in partOutLen, so that RIPEMSignDetachedPKCSFinal can return the pointer and length of a working buffer which contains the output. This buffer is allocated by RIPEM and maintained within ripemInfo. The buffer pointer returned in partOut is only valid until the next call to RIPEM, so it must be written out or copied immediately. Also, you do not need to free the memory buffer returned in partOut. This is done by RIPEMInfoDestructor. On error return, the pointer to the output is undefined.
RIPEMSignDetachedPKCSFinal should only be called once per signature.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMSignDetachedPKCSInit (RIPEMInfo *ripemInfo, int digestAlgorithm);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This initializes ripemInfo for creating a PKCS-compliant detached signature. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. digestAlgorithm must be DA_MD2, DA_MD5 or DA_SHA1.
After calling RIPEMSignDetachedPKCSInit, you should call RIPEMSignDetachedPKCSDigestUpdate to digest the message data by parts, and RIPEMSignDetachedPKCSFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMSignDetachedPKCSDigestUpdate (RIPEMInfo *ripemInfo, unsigned char *partIn, unsigned int partInLen);
Use this to digest the message data for a PKCS-compliant detached signature. ripemInfo points to the same structure which was initialized in RIPEMSignDetachedPKCSInit. partIn points to the buffer of message data to digest, of length partInLen.
The input is "as is." No translation of '\n' to <CR><LF>is done because the PKCS message format can support binary data. The calling routine must do end-of-line character translation on the input if necessary before passing it to RIPEMSignDetachedPKCSDigestUpdate.
You can digest a message of unlimited length by calling this multiple times by reading in a part of the text and calling RIPEMSignDetachedPKCSDigestUpdate, reading in the next part, calling again, etc. When you are done digesting the text in this manner, proceed to use RIPEMSignDetachedPKCSFinal. (There is no RIPEMSignDetachedPKCSUpdate because RIPEMSignDetachedPKCSFinal produces the entire output.)
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMUpdateCRL (RIPEMInfo *ripemInfo, UINT4 nextUpdate, unsigned char *serialNumber, unsigned int serialNumberLen, int digestAlgorithm, RIPEMDatabase *ripemDatabase);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This selects the current CRL for the user logged in to ripemInfo, sets the last update time to now and the next update time to nextUpdate, signs the CRL using digestAlgorithm and inserts the updated CRL into the first file listed in ripemDatabase->crlSource. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase to initialize ripemDatabase. The nextUpdate time is represented as the number of seconds since January first, 1970 at midnight Greenwich Mean Time. It is an error if nextUpdate is before now (as returned by R_time). This will create a new CRL if one does not exist in the database. This also sets the current CRL last update time in the RIPEM preferences to the new value and saves the modified preferences. (Note that this updates the CRL even if the CRL last update time in the RIPEM preferences doesn't match the actual latest CRL.)
If serialNumber is not NULL, then serialNumber and serialNumberLen give the serial number of a user to revoke by adding an entry to the CRL. The revocation time for the revoked user is set to now. In this case, serialNumber typically points to the serialNumber buffer in the CertificateStruct returned by DERToCertificate, and serialNumberLen is the sizeof this buffer. However, if serialNumber is NULL, serialNumberLen is ignored and no new revocation entry is added. This is useful for renewing the CRL when it expires by writing a fresh one to the database.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMVerifyDetachedPKCSFinal (RIPEMInfo *ripemInfo, TypList *certChain, ChainStatusInfo *chainStatus, RIPEMAttributes *authenticatedAttributes);
Use this to finalize the verifying of a PKCS-compliant detached signature. You should already have called RIPEMVerifyDetachedPKCSUpdate one or more times to supply the detached signature. ripemInfo points to the same structure which was initialized in RIPEMVerifyDetachedPKCSInit.
certChain, chainStatus and authenticatedAttributes are returned as in RIPEMDecipherPKCSFinal for a PKCS_SIGNED message. pkcsMode is not returned because it can only be PKCS_SIGNED.
RIPEMVerifyDetachedPKCSFinal should only be called once per signature.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMVerifyDetachedPKCSInit (RIPEMInfo *ripemInfo, int digestAlgorithm);
This initializes ripemInfo for verifying a PKCS-compliant detached signature. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. The digestAlgorithm is used by RIPEMVerifyDetachedPKCSDigestUpdate to digest the text and is also compared with the digest algorithm specified in the signature information. digestAlgorithm should be one of the tokens defined by RSAREF such as DA_MD2 or DA_MD5.
After calling RIPEMVerifyDetachedPKCSInit, you should call RIPEMVerifyDetachedPKCSDigestUpdate to digest the message by parts, RIPEMVerifyDetachedPKCSUpdate to supply the detached signature, and RIPEMVerifyDetachedPKCSFinal to finish.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMVerifyDetachedPKCSDigestUpdate (RIPEMInfo *ripemInfo, unsigned char *partIn, unsigned int partInLen);
Use this to digest the message data of a PKCS-compliant detached signature which you are verifying. You should already have called RIPEMVerifyDetachedPKCSInit. ripemInfo points to the same structure which was initialized in RIPEMVerifyDetachedPKCSInit.
partIn points to the buffer of the input message to digest, of length partInLen. partIn may contain any number of bytes.
The input is "as is." No translation of '\n' to <CR><LF>is done because the PKCS message format can support binary data. The calling routine must do end-of-line character translation on the output text if necessary.
You can process a message of unlimited length by calling this multiple times by reading in a part of the message and calling RIPEMVerifyDetachedPKCSDigestUpdate, reading in the next part, calling again, etc. When you are done processing the message in this manner, proceed to use RIPEMVerifyDetachedPKCSUpdate.
Returns: (char *)NULL for success, otherwise an error string.
char *RIPEMVerifyDetachedPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char *partIn, unsigned int partInLen, RIPEMDatabase *ripemDatabase);
Use this to supply the signature data of a PKCS-compliant detached signature which you are verifying. You should already have called RIPEMVerifyDetachedPKCSDigestUpdate to digest the message data. (If RIPEMVerifyDetachedPKCSDigestUpdate has not been called, this will still verify the signature of zero-length message.) ripemInfo points to the same structure which was initialized in RIPEMVerifyDetachedPKCSInit. ripemDatabase must already be initialized using InitRIPEMDatabase and is used to process the certificate information in the message.
partIn points to the buffer of the input signature data to process, of length partInLen. partIn may contain any number of bytes.
You can call this multiple times by reading in a part of the signature data and calling RIPEMVerifyDetachedPKCSUpdate, reading in the next part, calling again, etc. When you are done supplying the signature in this manner, proceed to use RIPEMVerifyDetachedPKCSFinal.
Returns: (char *)NULL for success, otherwise an error string.
void *R_realloc (void *pointer, unsigned int size);
This is just like the normal realloc, except it frees the original pointer if realloc fails, whereas the normal realloc does not. Also, this returns a non-null value if size is zero, whereas some implementations may return (void *)NULL when sizing a block down to zero (which might be mistaken for an error return value). In this way, R_realloc is a little more robust that the normal realloc. It is used throughout the RIPEM library and is made available in the RIPEM API as a general utility function.
If pointer is (void *)NULL, this behaves like malloc. Otherwise, pointer must point to a previously allocated block. On success, this returns the new block, otherwise it returns (void *)NULL.
void R_time (UINT4 *theTime);
This sets theTime to the number of seconds since January first, 1970 at midnight Greenwich Mean Time. The type UINT4 is defined in global.h as unsigned long int. This representation of time is used by the RIPEM API in many places such as the beginning and end of a certificate validity period or the time of a CRL revocation entry (although these times are represented differently inside an encoded certificate or CRL as a "UTC Time"). Having a representation in seconds makes it easy to compare two times to see which is earlier or to compute a time interval by calculating the number of seconds in the interval.
char *SelectCertChain (RIPEMInfo *ripemInfo, TypList *certChain, ChainStatusInfo *chainStatus, DistinguishedNameStruct *name, R_RSA_PUBLIC_KEY *publicKey, BOOL directCertOnly, RIPEMDatabase *ripemDatabase);
This selects the best certificate chain for the distinguished name pointed to by name. Typically, the name is the subject of the CertificateStruct returned by DERToCertificate. The RIPEM User Manual explains what is meant by the "best" certificate chain. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase to initialize ripemDatabase.
The certificate chain is returned in the TypList pointed to by certChain. You must declare this TypList and initialize it using InitList before calling SelectCertChain. SelectCertChain will call FreeList on certChain to make sure it is empty before searching for the chain. You must also declare a ChainStatusInfo structure and pass a pointer to it in chainStatus, which SelectCertChain uses to return the status of the certificates in the chain.
On success, certChain contains the chain where each entry is the certificate DER. The first entry is the certificate for the user given by name (called the "bottom" certificate), and each successive entry is an issuer certificate going up the chain. The caller can use DERToCertificate to get the contents of the certificates in the chain. Note that DERToCertificate will not return an error since the certificate was already successfully decoded, so you can use it without worrying about the return value. The array for chainStatus->individual contains the individual certificate statues and corresponds to the certificates in certChain where chainStatus->individual[0] is the status for the certificate at the "bottom" of the chain, etc. chainStatus->overall contains the overall status, defined as the "worst" of the individual statues. The status (individual or overall) is one the CERT_ statuses, such as CERT_VALID, CERT_REVOCATION_UNKNOWN, etc. Note that an application is usually only interested in the overall chain status. The individual certificate statuses are provided only for extra detail.
If publicKey is not NULL, it points to the public key for the user given by name, and limits the search to include only certificates for the user where the public key matches the supplied value. This is useful if you already have a certificate such as one returned by GetCertsBySmartName. To find out whether it is validated, you can call SelectCertChain, passing the public key in the certificate as well as the subject name. This is important because you don't want a certificate chain returned for the same name but a different public key.
Also, if directCertOnly is FALSE, chain lengths up to MAX_CERT_CHAIN_LEN will be allowed. This is the usual case for finding senders and recipients of messages. But if directCertOnly is TRUE, SelectCertChain limits the certificate chain to only one certificate issued directly by the user which is logged in to ripemInfo. This is useful for operations such as revoking or setting chain length allowed which are only meaningful on certificates issued directly by the logged-in user. (You don't want a longer certificate chain where the certificate for the user given by name is issued by someone else.)
If no chain can be found, this sets chainStatus->overall to 0, the certChain is empty, values in chainStatus->individual are undefined, and the function return value is (char *)NULL.
Returns: (char *)NULL for success, otherwise an error string. Note that failure to find a certificate chain does not result in an error string.
char *SetChainLenAllowed (RIPEMInfo *ripemInfo, unsigned char *publicKeyDigest, unsigned int chainLenAllowed, RIPEMDatabase *ripemDatabase);
This sets the chain length allowed for the user with the public key given by publicKeyDigest. This is the way that you specify that you trust a user, such as the Low Assurance Certification Authority, to certify other users. Before calling this, you must call RIPEMLoginUser to properly initialize ripemInfo. The public key in question usually comes from a user's certificate in a certificate chain. To get the public key digest, you should decode the certificate using DERToCertificate and pass the public key from the CertificateStruct to GetPublicKeyDigest.
This modifies the certificate preferences in ripemInfo which were loaded during RIPEMLoginUser. If the public key digest is found, this modifies the entry to the given chainLenAllowed, otherwise it adds a new entry for the public key digest. However, if chainLenAllowed is zero, this removes the entry for the public key digest since zero is the default (and if there was no entry in the first place, this does not add one). This also calls RIPEMSavePreferences so that the stored preferences are updated. Therefore, this will return an error if ripemDatabase->preferencesFilename cannot be opened.
Returns: (char *)NULL for success, otherwise an error string.
char *ValidateAndWriteCert (RIPEMInfo *ripemInfo, CertificateStruct *certStruct, int digestAlgorithm, RIPEMDatabase *ripemDatabase);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This is the function which you should use to validate another user for the first time. This creates a new certificate based on the information in certStruct and signs it with the private key in ripemInfo using digestAlgorithm for the signature, then writes the new certificate to the database. Usually, this is done when RIPEMDecipherFinal returns a self-signed certificate from another user (indicated by a chainStatus->overall of zero). After verifying that the user given by the self-signed certificate should be validated (by making sure the self-signed certificate digest is correct) you can call ValidateAndWriteCert to make a new certificate which validates that user.
You can also call this in other circumstances where you might want to make a new certificate, like making a certificate for the Low Assurance Certification Authority in order to hook into the low assurance certificate hierarchy by setting its chain length allowed to 2.
When you call this, certStruct->subject should contain the distinguished name of the user to validate and certStruct->publicKey should contain that user's public key. certStruct->notBefore and certStruct->notAfter should contain the beginning and end times of the certificate's validity period. These represent the time in number of seconds since January first, 1970 at midnight Greenwich Mean Time. For example, you can get the begin time by calling R_time and the end time by adding the number of seconds in a year. It is an error if certStruct->notAfter is less than certStruct->notBefore.
If the logged-in user in ripemInfo has already issued a certificate for the same user (even if it has been revoked) and if the validity period in certStruct does not begin after the end of the already-issued certificate's validity period, then this returns the error string ERR_CERT_ALREADY_VALIDATED. This prevents making a new certificate with an overlapping or earlier validity period. (You should compare the error return string to ERR_CERT_ALREADY_VALIDATED using strcmp.)
This sets the certificate issuer to the logged-in user's distinguished name in ripemInfo and generates a unique serial number for this certificate by digesting the certificate data. This also sets the certificate version and digest algorithm to default values. This then signs the certificate with the private key in ripemInfo and adds the certificate to the first file listed in ripemDatabase->pubKeySource. The certificate is identified in the public key file by a User: field which is the smart name from certStruct->subject.
Returns: (char *)NULL for success, otherwise an error string.
char *WriteSelfSignedCert (RIPEMInfo *ripemInfo, unsigned int validityMonths, int digestAlgorithm, RIPEMDatabase *ripemDatabase);
NOTE: digestAlgorithm is a new parameter added in release 3.0 Beta 4.
This creates a self-signed certificate using the ripemInfo->publicKey and ripemInfo->userDN with a validity->period starting from the current time to validityMonths in the future. The self-signed certificate is signed with ripemInfo->privateKey using digestAlgorithm for the signature and written to the first file listed in ripemDatabase->pubKeySource.
This function is provided only for applications which must upgrade a user from RIPEM 1.1 or earlier since these versions did not use self-signed certificates. And you should call this only in response to RIPEMLoginUser returning ERR_SELF_SIGNED_CERT_NOT_FOUND. If you do not need to convert RIPEM 1.1 users, you do not need to call WriteSelfSignedCert. (RIPEMGenerateKeys writes the new self-signed certificate itself, so you do not need to call WriteSelfSignedCert when creating a new user.)
When RIPEMLoginUser returns ERR_SELF_SIGNED_CERT_NOT_FOUND, ripemInfo->publicKey and ripemInfo->privateKey are already set. You must set ripemInfo->userDN and then call WriteSelfSignedCert. After that, the self-signed certificate is available for normal RIPEM operation.
Returns: (char *)NULL for success, otherwise an error string.
A random structure contains a seed from which a pseudorandom sequence of bytes is derived. RSAREF generates keys and pads RSA encryption blocks with bytes derived from a random structure. Random structures are used by both message-processing and key-generation applications. RSAREF sets up a random structure with the procedure R_RandomInit. A typical application calls R_RandomInit on entry. A new random structure is not ready for use until it is seeded by mixing in some random bytes. RSAREF seeds a random structure with the procedure R_RandomUpdate and R_GetRandomBytesNeeded. A random structure is considered seeded when the number of bytes still needed reaches zero. More bytes can be mixed in after the random structure is seeded. A typical application calls R_GetRandomBytesNeeded and R_RandomUpdate immediately after calling R_RandomInit. RSAREF zeroizes a random structure with the procedure R_RandomFinal. A typical application calls R_RandomFinal on exit. R_RandomInit int R_RandomInit ( R_RANDOM_STRUCT *randomStruct /* new random structure */ ); R_RandomInit sets up a new random structure. Return value: 0 success nonzero reserved for future compatibility R_RandomUpdate int R_RandomUpdate ( R_RANDOM_STRUCT *randomStruct, /* random structure */ unsigned char *block, /* block of values to mix in */ unsigned int blockLen /* length of block */ ); R_RandomUpdate mixes blockLen bytes from block into randomStruct. Return value: 0 success nonzero reserved for future compatibility R_GetRandomBytesNeeded int R_GetRandomBytesNeeded ( unsigned int *bytesNeeded,/* number of mix-in bytes needed */ R_RANDOM_STRUCT *randomStruct /* random structure */ ); R_GetRandomBytesNeeded computes the number of mix-in bytes still needed to seed randomStruct, storing the result in bytesNeeded. Return value: 0 success nonzero reserved for future compatibility R_RandomFinal void R_RandomFinal ( R_RANDOM_STRUCT *randomStruct /* random structure */ ); R_RandomFinal zeroizes randomStruct. No return value.
int R_DigestBlock ( unsigned char *digest, /* message digest */ unsigned int *digestLen, /* length of message digest */ unsigned char *content, /* content */ unsigned int contentLen, /* length of content */ int digestAlgorithm /* message-digest algorithm */ ); R_DigestBlock computes the message digest of content, storing the resulting message digest in digest and its length in bytes in digestLen. digestAlgorithm is the algorithm with which the content is digested, and must be one DA_MD2 or DA_MD5. digestLen will not be greater than MAX_DIGEST_LEN. Return value: 0 success RE_DIGEST_ALGORITHM digestAlgorithm is invalid