You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
859 lines
21 KiB
859 lines
21 KiB
/*++
|
|
|
|
Copyright (C) 1995-96 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
certify.cxx
|
|
|
|
Abstract:
|
|
|
|
This is the command line tool to manipulate certificates on an executable image.
|
|
|
|
Author: Robert Reichel (robertre) Feb 12, 1996
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
//#define UNICODE 1
|
|
//#define _UNICODE 1
|
|
|
|
|
|
#include <windows.h>
|
|
#include <imagehlp.h>
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
|
|
LPWIN_CERTIFICATE
|
|
GetCertFromImage(
|
|
LPCTSTR ImageName,
|
|
DWORD Index
|
|
);
|
|
|
|
SaveCertificate(
|
|
LPCTSTR OutputFileName,
|
|
LPCTSTR ImageName,
|
|
DWORD CertIndex
|
|
);
|
|
|
|
BOOL
|
|
RemoveCertificateFromImage(
|
|
LPCTSTR ImageName,
|
|
DWORD Index
|
|
);
|
|
|
|
BOOL
|
|
AddCertificateToImage(
|
|
LPCTSTR ImageName,
|
|
LPCTSTR CertificateName,
|
|
WORD CertificateType,
|
|
PDWORD Index
|
|
);
|
|
|
|
VOID
|
|
PrintCertificate(
|
|
LPWIN_CERTIFICATE Certificate,
|
|
DWORD Index
|
|
);
|
|
|
|
BOOL
|
|
GetCertHeaderFromImage(
|
|
IN LPCTSTR ImageName,
|
|
IN DWORD Index,
|
|
OUT LPWIN_CERTIFICATE CertificateHeader
|
|
);
|
|
|
|
#define TYPE_X509 TEXT("X509")
|
|
#define TYPE_PKCS7 TEXT("PKCS7")
|
|
#define TYPE_UNKNOWN TEXT("Unknown")
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
BOOL fVerbose = FALSE;
|
|
|
|
void
|
|
PrintUsage(
|
|
VOID
|
|
)
|
|
{
|
|
fputs("Usage: PESIGMGR [switches] image-name \n"
|
|
" [-?] display this message\n"
|
|
" [-l] list the certificates in an image\n"
|
|
" [-a:<Filename>] add a certificate file to an image\n"
|
|
" [-r:<index>] remove certificate <index> from an image\n"
|
|
" [-s:<Filename>] used with -r to save the removed certificate\n"
|
|
" [-t:<CertType>] used with -a to specify the type of the certificate\n"
|
|
" where CertType may be X509 or PKCS7 [default is X509]\n",
|
|
stderr
|
|
);
|
|
exit(-1);
|
|
}
|
|
|
|
#if 0
|
|
|
|
PWSTR
|
|
GetArgAsUnicode(
|
|
LPSTR s
|
|
)
|
|
{
|
|
ULONG n;
|
|
PWSTR ps;
|
|
|
|
n = strlen( s );
|
|
ps = (PWSTR)HeapAlloc( GetProcessHeap(),
|
|
0,
|
|
(n + 1) * sizeof( WCHAR )
|
|
);
|
|
if (ps == NULL) {
|
|
printf( "Out of memory\n" );
|
|
}
|
|
|
|
if (MultiByteToWideChar( CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
s,
|
|
n,
|
|
ps,
|
|
n
|
|
) != (LONG)n
|
|
) {
|
|
printf( "Unable to convert parameter '%s' to Unicode (%u)", (ULONG)s, GetLastError() );
|
|
}
|
|
|
|
ps[ n ] = UNICODE_NULL;
|
|
return ps;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
int __cdecl
|
|
main(
|
|
int argc,
|
|
char *argv[],
|
|
char *envp[]
|
|
)
|
|
{
|
|
BOOL AFlagSeen = FALSE;
|
|
BOOL LFlagSeen = FALSE;
|
|
BOOL RFlagSeen = FALSE;
|
|
BOOL SFlagSeen = FALSE;
|
|
BOOL TFlagSeen = FALSE;
|
|
BOOL Result;
|
|
|
|
DWORD Index;
|
|
DWORD RFlag_CertIndex;
|
|
WORD CertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA; // default
|
|
|
|
char * CertificateName;
|
|
char * ImageName = NULL;
|
|
char * SFlag_CertificateFile;
|
|
char c, *p;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
PrintUsage();
|
|
}
|
|
|
|
while (--argc) {
|
|
p = *++argv;
|
|
if (*p == '/' || *p == '-') {
|
|
c = *++p;
|
|
switch (toupper( c )) {
|
|
case '?':
|
|
PrintUsage();
|
|
break;
|
|
|
|
case 'A': // Add Certificate to image
|
|
c = *++p;
|
|
if (c != ':') {
|
|
PrintUsage();
|
|
} else {
|
|
|
|
if (AFlagSeen == TRUE) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
AFlagSeen = TRUE;
|
|
CertificateName = ++p;
|
|
}
|
|
break;
|
|
case 'V':
|
|
{
|
|
fVerbose = TRUE;
|
|
break;
|
|
}
|
|
|
|
case 'T':
|
|
{
|
|
//
|
|
// Specify the Type of the certificate
|
|
//
|
|
c = *++p;
|
|
if (c != ':') {
|
|
PrintUsage();
|
|
} else {
|
|
|
|
if (TFlagSeen == TRUE) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
TFlagSeen = TRUE;
|
|
++p;
|
|
|
|
if (_stricmp(p, TYPE_X509) == 0) {
|
|
CertificateType = WIN_CERT_TYPE_X509;
|
|
if (fVerbose) {
|
|
printf("Certificate type = X509\n");
|
|
}
|
|
} else {
|
|
if (_stricmp(p, TYPE_PKCS7) == 0) {
|
|
CertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
|
|
if (fVerbose) {
|
|
printf("Certificate type = PKCS7\n");
|
|
}
|
|
} else {
|
|
if (fVerbose) {
|
|
printf("Unrecognized Certificate type %s\n",p);
|
|
}
|
|
PrintUsage();
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'L': // List the certificates in an image.
|
|
{
|
|
if (LFlagSeen == TRUE) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
LFlagSeen = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
case 'R': // Remove certificate from an image
|
|
{
|
|
c = *++p;
|
|
if (c != ':') {
|
|
PrintUsage();
|
|
} else {
|
|
|
|
if (RFlagSeen == TRUE) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
RFlagSeen = TRUE;
|
|
|
|
//
|
|
// Save the index
|
|
//
|
|
|
|
RFlag_CertIndex = atoi(++p);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 'G':
|
|
c = *++p;
|
|
if (c != ':') {
|
|
PrintUsage();
|
|
} else {
|
|
// Generate a certificate from an image.
|
|
}
|
|
break;
|
|
|
|
case 'S': // Save the certificate
|
|
c = *++p;
|
|
if (c != ':') {
|
|
PrintUsage();
|
|
} else {
|
|
|
|
if (SFlagSeen == TRUE) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
SFlagSeen = TRUE;
|
|
|
|
//
|
|
// Save the name of the file to put the cert into.
|
|
//
|
|
|
|
SFlag_CertificateFile = ++p;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf( stderr, "CERTIFY: Invalid switch - /%c\n", c );
|
|
PrintUsage();
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Should only be a single image name here
|
|
//
|
|
|
|
if (ImageName != NULL) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
ImageName = *argv;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finished processing parameters, let's do the work.
|
|
//
|
|
|
|
if (ImageName == NULL) {
|
|
if (fVerbose) {
|
|
printf("Image name not specified\n");
|
|
}
|
|
PrintUsage();
|
|
return(0);
|
|
}
|
|
|
|
if (LFlagSeen) {
|
|
|
|
int Index = 0;
|
|
WIN_CERTIFICATE Certificate;
|
|
|
|
if (SFlagSeen || RFlagSeen || AFlagSeen) {
|
|
PrintUsage();
|
|
return(0);
|
|
}
|
|
|
|
do {
|
|
|
|
Result = GetCertHeaderFromImage( ImageName, Index, &Certificate );
|
|
|
|
if (Result) {
|
|
PrintCertificate( &Certificate, Index );
|
|
}
|
|
|
|
Index++;
|
|
|
|
} while ( Result );
|
|
return( 1 );
|
|
}
|
|
|
|
if (AFlagSeen) {
|
|
|
|
//
|
|
// 'A' is not used in conjunction with R or S
|
|
//
|
|
|
|
if (SFlagSeen || RFlagSeen) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
Result = AddCertificateToImage(
|
|
ImageName,
|
|
CertificateName,
|
|
CertificateType,
|
|
&Index
|
|
);
|
|
|
|
if (Result) {
|
|
if (fVerbose){
|
|
printf("Certificate %d added\n",Index);
|
|
}
|
|
|
|
return( 1 );
|
|
} else {
|
|
|
|
if (fVerbose) {
|
|
printf("Unable to add Certificate to %s, error = %d\n",ImageName,GetLastError());
|
|
}
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
if (RFlagSeen) {
|
|
|
|
if (!SFlagSeen) {
|
|
if (fVerbose) {
|
|
fputs("-R requires -S\n",stderr);
|
|
}
|
|
PrintUsage();
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Make sure we can save the certificate data before
|
|
// we remove it from the image
|
|
//
|
|
|
|
Result = SaveCertificate(
|
|
SFlag_CertificateFile,
|
|
ImageName,
|
|
RFlag_CertIndex
|
|
);
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("Unable to save certificate to file %s, error = %d\n",SFlag_CertificateFile,GetLastError());
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Now that the certificate is safe, remove it from the image
|
|
//
|
|
|
|
Result = RemoveCertificateFromImage(
|
|
ImageName,
|
|
RFlag_CertIndex
|
|
);
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("Unable to remove certificate, error = %d\n",GetLastError());
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if (SFlagSeen && !RFlagSeen) {
|
|
PrintUsage();
|
|
return( 0 );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
PrintCertificate(
|
|
LPWIN_CERTIFICATE Certificate,
|
|
DWORD Index
|
|
)
|
|
{
|
|
char * CertType;
|
|
|
|
switch (Certificate->wCertificateType) {
|
|
case WIN_CERT_TYPE_X509:
|
|
{
|
|
CertType = TYPE_X509;
|
|
break;
|
|
}
|
|
case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
|
|
{
|
|
|
|
CertType = TYPE_PKCS7;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
CertType = TYPE_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("\nCertificate %3d Revision %1d Type %8s",Index,Certificate->wRevision, CertType);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddCertificateToImage(
|
|
IN LPCTSTR ImageName,
|
|
IN LPCTSTR CertificateName,
|
|
IN WORD CertificateType,
|
|
OUT PDWORD Index
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a certificate to an image, and returns its index.
|
|
|
|
Arguments:
|
|
|
|
ImageName - Provides the full name and path to the image to be
|
|
modified.
|
|
|
|
CertificateName - Provides the full name and path to a file containing
|
|
the certificate to be added to the image.
|
|
|
|
CertificateType - Provides the type of the certificate being added.
|
|
This type will be placed in the dwType field of the resulting
|
|
WIN_CERTIFICATE structure.
|
|
|
|
Index - Returns the index of the certificate after it is placed in the
|
|
image.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. More information is available via
|
|
GetLastError().
|
|
|
|
--*/
|
|
{
|
|
HANDLE CertificateHandle;
|
|
DWORD CertificateFileSize;
|
|
DWORD CertificateSize;
|
|
LPWIN_CERTIFICATE Certificate = NULL;
|
|
BOOL Result = FALSE;
|
|
HANDLE ImageHandle;
|
|
DWORD BytesRead;
|
|
|
|
//
|
|
// Attempt to open the certificate file
|
|
//
|
|
|
|
if ((CertificateHandle = CreateFile(CertificateName,
|
|
GENERIC_READ,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (fVerbose) {
|
|
printf("Unable to open %s, error = %d\n",CertificateName,GetLastError());
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
//
|
|
// Read the certificate data into memory
|
|
//
|
|
|
|
CertificateFileSize = GetFileSize( CertificateHandle, NULL );
|
|
CertificateSize = CertificateFileSize + (sizeof( WIN_CERTIFICATE ) - sizeof( BYTE ));
|
|
|
|
//
|
|
// Hack to make certs 8 byte aligned
|
|
//
|
|
|
|
// CertificateSize += (8 - (CertificateSize % 8));
|
|
|
|
Certificate = (LPWIN_CERTIFICATE)malloc( CertificateSize );
|
|
if (NULL == Certificate) {
|
|
if (fVerbose) {
|
|
printf("malloc failed\n");
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
Certificate->dwLength = CertificateSize;
|
|
Certificate->wRevision = WIN_CERT_REVISION_1_0;
|
|
Certificate->wCertificateType = CertificateType;
|
|
Result = ReadFile( CertificateHandle,
|
|
&Certificate->bCertificate,
|
|
CertificateFileSize,
|
|
&BytesRead,
|
|
NULL // Overlapped
|
|
);
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("Unable to read Certificate file, error = %d\n",GetLastError());
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
ImageHandle = CreateFile( ImageName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (ImageHandle == INVALID_HANDLE_VALUE) {
|
|
if (fVerbose) {
|
|
printf("Unable to open image file %s, error = %d\n",ImageName,GetLastError());
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
Result = ImageAddCertificate( ImageHandle,
|
|
Certificate,
|
|
Index
|
|
);
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("ImageAddCertificate failed, error = %d\n",GetLastError());
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
CommonReturn:
|
|
if (Certificate)
|
|
free(Certificate);
|
|
|
|
return( Result );
|
|
|
|
ErrorReturn:
|
|
Result = FALSE;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
BOOL
|
|
GetCertHeaderFromImage(
|
|
IN LPCTSTR ImageName,
|
|
IN DWORD Index,
|
|
OUT LPWIN_CERTIFICATE CertificateHeader
|
|
)
|
|
{
|
|
HANDLE ImageHandle;
|
|
BOOL Result;
|
|
|
|
ImageHandle = CreateFile( ImageName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (ImageHandle == INVALID_HANDLE_VALUE) {
|
|
if (fVerbose) {
|
|
printf("Unable to open image file %s\n",ImageName);
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
Result = ImageGetCertificateHeader(
|
|
ImageHandle,
|
|
Index,
|
|
CertificateHeader
|
|
);
|
|
|
|
CloseHandle( ImageHandle );
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("\nUnable to retrieve certificate header from %s, index=%d, error = %d\n",ImageName,Index,GetLastError());
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
LPWIN_CERTIFICATE
|
|
GetCertFromImage(
|
|
IN LPCTSTR ImageName,
|
|
IN DWORD Index
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a copy of the specified certificate.
|
|
|
|
Arguments:
|
|
|
|
ImageName - Provides the full path to the image file containing the
|
|
certificate.
|
|
|
|
Index - Provides the index of the desired certificate in the image.
|
|
|
|
Return Value:
|
|
|
|
NULL on failure.
|
|
|
|
On Success, returns a pointer to a filled in WIN_CERTIFICATE
|
|
structure, which may be freed by called free().
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE ImageHandle;
|
|
LPWIN_CERTIFICATE Certificate;
|
|
DWORD RequiredLength = 0;
|
|
BOOL Result;
|
|
|
|
ImageHandle = CreateFile( ImageName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (ImageHandle == INVALID_HANDLE_VALUE) {
|
|
if (fVerbose) {
|
|
printf("Unable to open image file %s\n",ImageName);
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
Result = ImageGetCertificateData(
|
|
ImageHandle,
|
|
Index,
|
|
NULL,
|
|
&RequiredLength
|
|
);
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
if (fVerbose) {
|
|
printf("Unable to retrieve certificate data from %s, error = %d\n",ImageName,GetLastError());
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
Certificate = (LPWIN_CERTIFICATE) malloc( RequiredLength );
|
|
|
|
if (Certificate == NULL) {
|
|
if (fVerbose) {
|
|
printf("Out of memory in GetCertFromImage\n");
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
Result = ImageGetCertificateData(
|
|
ImageHandle,
|
|
Index,
|
|
Certificate,
|
|
&RequiredLength
|
|
);
|
|
|
|
CloseHandle( ImageHandle );
|
|
|
|
if (!Result) {
|
|
if (fVerbose) {
|
|
printf("Unable to retrieve certificate from %s, error = %d\n",ImageName,GetLastError());
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
return( Certificate );
|
|
}
|
|
|
|
|
|
BOOL
|
|
RemoveCertificateFromImage(
|
|
LPCTSTR ImageName,
|
|
DWORD Index
|
|
)
|
|
{
|
|
HANDLE ImageHandle;
|
|
BOOL Result;
|
|
|
|
if (fVerbose) {
|
|
printf("Removing certificate index %d from %s\n",Index,ImageName);
|
|
}
|
|
|
|
ImageHandle = CreateFile( ImageName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (ImageHandle == INVALID_HANDLE_VALUE) {
|
|
printf("Unable to open image file %s\n",ImageName);
|
|
return( FALSE );
|
|
}
|
|
|
|
Result = ImageRemoveCertificate(
|
|
ImageHandle,
|
|
Index
|
|
);
|
|
|
|
if (!Result) {
|
|
printf("Unable to remove certificate from %s, error = %d\n",ImageName,GetLastError());
|
|
return( FALSE );
|
|
}
|
|
|
|
CloseHandle( ImageHandle );
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
BOOL
|
|
SaveCertificate(
|
|
LPCTSTR OutputFileName,
|
|
LPCTSTR ImageName,
|
|
DWORD CertIndex
|
|
)
|
|
{
|
|
DWORD Length;
|
|
BOOL Result;
|
|
DWORD BytesWritten = 0;
|
|
LPWIN_CERTIFICATE Certificate;
|
|
HANDLE CertificateHandle;
|
|
|
|
Certificate = GetCertFromImage( ImageName, CertIndex );
|
|
|
|
if (Certificate == NULL) {
|
|
if (fVerbose) {
|
|
printf("Unable to retrieve certificate from %s, error = %d\n",ImageName,GetLastError());
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
Length = Certificate->dwLength - sizeof( WIN_CERTIFICATE ) + sizeof( BYTE );
|
|
|
|
//
|
|
// Attempt to open the certificate file
|
|
//
|
|
|
|
if ((CertificateHandle = CreateFile(OutputFileName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Unable to create %s, error = %d\n",OutputFileName,GetLastError());
|
|
return( FALSE );
|
|
}
|
|
|
|
Result = WriteFile( CertificateHandle,
|
|
&Certificate->bCertificate,
|
|
Length,
|
|
&BytesWritten,
|
|
NULL // Overlapped
|
|
);
|
|
|
|
if (!Result) {
|
|
printf("Unable to save certificate to file %s, error = %d\n",OutputFileName,GetLastError());
|
|
return( FALSE );
|
|
}
|
|
|
|
CloseHandle( CertificateHandle );
|
|
|
|
free( Certificate );
|
|
|
|
return( TRUE );
|
|
}
|