cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
certproc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Certificate Handling Test Routines *
4 * Copyright Peter Gutmann 1997-2009 *
5 * *
6 ****************************************************************************/
7 
8 #include "cryptlib.h"
9 #include "test/test.h"
10 
11 #if defined( __MVS__ ) || defined( __VMCMS__ )
12  /* Suspend conversion of literals to ASCII. */
13  #pragma convlit( suspend )
14 #endif /* IBM big iron */
15 #if defined( __ILEC400__ )
16  #pragma convert( 0 )
17 #endif /* IBM medium iron */
18 
20 
21 /****************************************************************************
22 * *
23 * Certificate Processing Test *
24 * *
25 ****************************************************************************/
26 
27 static const CERT_DATA FAR_BSS certRequestData[] = {
28  /* Identification information */
29  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
30  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
31  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
32  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Dave Smith" ) },
33 
34  /* Subject altName */
36  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
37 
39  };
40 
41 static const CERT_DATA FAR_BSS certProcessData[] = {
42  /* Identification information */
43  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
44  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
45  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
46  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Dave Smith" ) },
47 
48  /* Subject altName */
50  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
51 
52  /* Re-select the subject name after poking around in the altName */
54 
56  };
57 
58 /* Create a certification request */
59 
60 static int createCertRequest( void *certRequest,
62  const BOOLEAN useCRMF )
63  {
64  CRYPT_CERTIFICATE cryptCert;
66  int length = DUMMY_INIT, status;
67 
68  /* Create a new key */
69  status = cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo );
70  if( cryptStatusError( status ) )
71  return( status );
73  TEXT( "Private key" ),
74  paramStrlen( TEXT( "Private key" ) ) );
75  status = cryptGenerateKey( cryptContext );
76  if( cryptStatusError( status ) )
77  return( status );
78 
79  /* Create the certification request */
80  status = cryptCreateCert( &cryptCert, CRYPT_UNUSED, useCRMF ? \
82  if( cryptStatusError( status ) )
83  return( status );
84  status = cryptSetAttribute( cryptCert,
86  if( cryptStatusError( status ) )
87  return( status );
88  if( !addCertFields( cryptCert, certRequestData, __LINE__ ) )
89  return( -1 );
90 #ifndef _WIN32_WCE
91  if( useCRMF )
92  {
93  const time_t startTime = time( NULL ) - 1000;
94  const time_t endTime = time( NULL ) + 86400;
95 
96  /* Since we're using a CRMF request, set some fields that can't
97  be specified in the standard certificate request */
98  status = cryptSetAttributeString( cryptCert,
99  CRYPT_CERTINFO_VALIDFROM, &startTime, sizeof( time_t ) );
100  if( cryptStatusOK( status ) )
101  status = cryptSetAttributeString( cryptCert,
102  CRYPT_CERTINFO_VALIDTO, &endTime, sizeof( time_t ) );
103  }
104 #endif /* _WIN32_WCE */
105  if( cryptStatusOK( status ) )
106  status = cryptSignCert( cryptCert, cryptContext );
107  if( cryptStatusOK( status ) )
108  status = cryptExportCert( certRequest, BUFFER_SIZE, &length,
109  CRYPT_CERTFORMAT_CERTIFICATE, cryptCert );
110  if( cryptStatusOK( status ) )
111  status = cryptDestroyCert( cryptCert );
112  if( cryptStatusError( status ) )
113  return( status );
114 
115  /* Clean up */
116  cryptDestroyContext( cryptContext );
117  return( length );
118  }
119 
120 /* Create a certificate from a certificate request */
121 
122 static int createCertificate( void *certificate, const void *certRequest,
123  const int certReqLength,
124  const CRYPT_CONTEXT caKeyContext )
125  {
126  CRYPT_CERTIFICATE cryptCert, cryptCertRequest;
127  int length, status;
128 
129  /* Import and verify the certification request */
130  status = cryptImportCert( certRequest, certReqLength, CRYPT_UNUSED,
131  &cryptCertRequest );
132  if( cryptStatusOK( status ) )
133  status = cryptCheckCert( cryptCertRequest, CRYPT_UNUSED );
134  if( cryptStatusError( status ) )
135  return( status );
136 
137  /* Create the certificate */
138  status = cryptCreateCert( &cryptCert, CRYPT_UNUSED,
140  if( cryptStatusError( status ) )
141  return( status );
142  status = cryptSetAttribute( cryptCert,
143  CRYPT_CERTINFO_CERTREQUEST, cryptCertRequest );
144  if( cryptStatusOK( status ) )
145  status = cryptSignCert( cryptCert, caKeyContext );
146  if( cryptStatusOK( status ) )
147  status = cryptExportCert( certificate, BUFFER_SIZE, &length,
148  CRYPT_CERTFORMAT_CERTIFICATE, cryptCert );
149  if( cryptStatusOK( status ) )
150  status = cryptDestroyCert( cryptCert );
151 
152  /* Clean up */
153  cryptDestroyCert( cryptCertRequest );
154  return( ( cryptStatusOK( status ) ) ? length : status );
155  }
156 
157 /* Create a certificate directly, used for algorithms that don't support
158  self-signed certificate requests */
159 
160 static int createCertDirect( void *certificate,
161  const CRYPT_ALGO_TYPE cryptAlgo,
162  const CRYPT_CONTEXT caKeyContext )
163  {
164  CRYPT_CERTIFICATE cryptCert;
166  int length, status;
167 
168  /* Create a new key */
169  status = cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo );
170  if( cryptStatusError( status ) )
171  return( status );
173  TEXT( "Private key" ),
174  paramStrlen( TEXT( "Private key" ) ) );
175  status = cryptGenerateKey( cryptContext );
176  if( cryptStatusError( status ) )
177  return( status );
178 
179  /* Create the certification */
180  status = cryptCreateCert( &cryptCert, CRYPT_UNUSED,
182  if( cryptStatusError( status ) )
183  return( status );
184  status = cryptSetAttribute( cryptCert,
185  CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, cryptContext );
186  if( cryptStatusError( status ) )
187  return( status );
188  if( !addCertFields( cryptCert, certProcessData, __LINE__ ) )
189  return( FALSE );
190  status = cryptSignCert( cryptCert, caKeyContext );
191  if( cryptStatusOK( status ) )
192  status = cryptExportCert( certificate, BUFFER_SIZE, &length,
193  CRYPT_CERTFORMAT_CERTIFICATE, cryptCert );
194  if( cryptStatusOK( status ) )
195  status = cryptDestroyCert( cryptCert );
196 
197  /* Clean up */
198  cryptDestroyContext( cryptContext );
199  return( ( cryptStatusOK( status ) ) ? length : status );
200  }
201 
202 /* Test the full certification process */
203 
204 static int certProcess( const CRYPT_ALGO_TYPE cryptAlgo,
205  const char *algoName,
206  const CRYPT_CONTEXT cryptCAKey,
207  const BOOLEAN useCRMF )
208  {
209  CRYPT_CERTIFICATE cryptCert;
210  const char *certName = \
211  ( cryptAlgo == CRYPT_ALGO_RSA ) ? \
212  ( useCRMF ? "prcrtrsa_c" : "prcrtrsa" ) : \
213  ( cryptAlgo == CRYPT_ALGO_DSA ) ? "prcrtdsa" : \
214  ( cryptAlgo == CRYPT_ALGO_DH ) ? "prcrtdh" : \
215  ( cryptAlgo == CRYPT_ALGO_ELGAMAL ) ? "prcrtelg" : \
216  ( cryptAlgo == CRYPT_ALGO_ECDSA ) ? "prcrtecdsa" : \
217  ( cryptAlgo == CRYPT_ALGO_ECDH ) ? "prcrtecdh" : "prcrtxxx";
218  int length, status;
219 
220  printf( "Testing %s certificate processing%s...\n", algoName,
221  useCRMF ? " from CRMF request" : "" );
222 
223  /* Some algorithms can't create self-signed certificate requests so we
224  have to create the certificate directly */
225  if( cryptAlgo != CRYPT_ALGO_ELGAMAL && cryptAlgo != CRYPT_ALGO_DH && \
226  cryptAlgo != CRYPT_ALGO_ECDH )
227  {
228  const char *reqName = \
229  ( cryptAlgo == CRYPT_ALGO_RSA ) ? \
230  ( useCRMF ? "prreqrsa_c" : "prreqrsa" ) : \
231  ( cryptAlgo == CRYPT_ALGO_DSA ) ? "prreqdsa" : \
232  ( cryptAlgo == CRYPT_ALGO_ECDSA ) ? "prreqecdsa" : "prreqxxx";
233 
234  /* Create the certification request */
235  status = length = createCertRequest( certBuffer, cryptAlgo, useCRMF );
236  if( cryptStatusError( status ) )
237  {
238  printf( "Certification request creation failed with error code "
239  "%d, line %d.\n", status, __LINE__ );
240  return( FALSE );
241  }
242  debugDump( reqName, certBuffer, length );
243 
244  /* Create a certificate from the certification request */
245  status = createCertificate( certBuffer, certBuffer, length,
246  cryptCAKey );
247  }
248  else
249  status = createCertDirect( certBuffer, cryptAlgo, cryptCAKey );
250  if( cryptStatusError( status ) )
251  {
252  printf( "Certificate creation failed with error code %d, line "
253  "%d.\n", status, __LINE__ );
254  return( FALSE );
255  }
256  length = status;
257  debugDump( certName, certBuffer, length );
258 
259  /* Import the certificate and check its validity using the CA key (we use
260  the private key context since it's handy, in practice we should use
261  the public key certificate */
262  status = cryptImportCert( certBuffer, length, CRYPT_UNUSED,
263  &cryptCert );
264  if( cryptStatusOK( status ) )
265  status = cryptCheckCert( cryptCert, cryptCAKey );
266  if( cryptStatusError( status ) )
267  {
268  printf( "Certificate validation failed with error code %d, line %d.\n",
269  status, __LINE__ );
270  return( FALSE );
271  }
272 
273  /* Clean up */
274  cryptDestroyCert( cryptCert );
275  printf( "%s certificate processing succeeded.\n\n", algoName );
276  return( TRUE );
277  }
278 
279 int testCertProcess( void )
280  {
281  CRYPT_CONTEXT cryptCAKey;
282  int value, status;
283 
284  /* Get the CA's private key */
285  status = getPrivateKey( &cryptCAKey, CA_PRIVKEY_FILE,
287  if( cryptStatusError( status ) )
288  {
289  printf( "CA private key read failed with error code %d, line %d.\n",
290  status, __LINE__ );
291  return( FALSE );
292  }
293 
294  /* Test each PKC algorithm */
295  if( !certProcess( CRYPT_ALGO_RSA, "RSA", cryptCAKey, FALSE ) )
296  return( FALSE );
297  if( !certProcess( CRYPT_ALGO_DSA, "DSA", cryptCAKey, FALSE ) )
298  return( FALSE );
300  !certProcess( CRYPT_ALGO_ELGAMAL, "Elgamal", cryptCAKey, FALSE ) )
301  return( FALSE );
302  if( !certProcess( CRYPT_ALGO_DH, "Diffie-Hellman", cryptCAKey, FALSE ) )
303  return( FALSE );
305  !certProcess( CRYPT_ALGO_ECDSA, "ECDSA", cryptCAKey, FALSE ) )
306  return( FALSE );
307 #if 0
308  /* We can't perform the test for ECDH because there's only one algorithm
309  type available for public keys, "ECC", and that doesn't distinguish
310  between an ECDH ECC key and an ECDSA ECC key. Because of this the
311  code assumes that any ECC key is an ECDSA key, so that while we can
312  create an ECDH certificate, we can't read it back because it'll be
313  read as an ECDSA certificate */
314  if( !certProcess( CRYPT_ALGO_ECDH, "ECDH", cryptCAKey, FALSE ) )
315  return( FALSE );
316 #endif /* 0 */
317 
318  /* Run the test again with a CRMF instead of PKCS #10 request */
319  if( !certProcess( CRYPT_ALGO_RSA, "RSA", cryptCAKey, TRUE ) )
320  return( FALSE );
321 
322  /* Now try a different hash algorithm */
325  CRYPT_ALGO_SHA2 );
326  status = certProcess( CRYPT_ALGO_RSA, "RSA with SHA-256", cryptCAKey,
327  FALSE );
329  if( !status )
330  return( FALSE );
331 
332  /* Clean up */
333  cryptDestroyContext( cryptCAKey );
334  return( TRUE );
335  }
336 
337 /****************************************************************************
338 * *
339 * CA Certificate Management Test *
340 * *
341 ****************************************************************************/
342 
343 /* Since opening the certificate store for update creates a log entry each
344  time, we open it once at the start and then call a series of sub-tests
345  with the store open throughout the tests. This also allows us to keep the
346  CA key active througout */
347 
348 static const CERT_DATA FAR_BSS cert1Data[] = {
349  /* Identification information */
350  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
351  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
352  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
353  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Test user 1" ) },
354 
355  /* Subject altName */
357  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
358 
359  /* Re-select the subject name after poking around in the altName */
361 
363  };
364 static const CERT_DATA FAR_BSS revokableCert1Data[] = {
365  /* Identification information */
366  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
367  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
368  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
369  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Revoked cert user 1" ) },
370 
371  /* Subject altName */
373  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
374 
375  /* Re-select the subject name after poking around in the altName */
377 
379  };
380 static const CERT_DATA FAR_BSS revokableCert2Data[] = {
381  /* Identification information */
382  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
383  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
384  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
385  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Revoked cert user 2" ) },
386 
387  /* Subject altName */
389  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
390 
391  /* Re-select the subject name after poking around in the altName */
393 
395  };
396 static const CERT_DATA FAR_BSS expiredCert1Data[] = {
397  /* Identification information */
398  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
399  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
400  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
401  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Expired cert user 1" ) },
402 
403  /* Subject altName */
405  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
406 
407  /* Re-select the subject name after poking around in the altName */
409 
411  };
412 static const CERT_DATA FAR_BSS expiredCert2Data[] = {
413  /* Identification information */
414  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
415  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
416  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
417  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Expired cert user 2" ) },
418 
419  /* Subject altName */
421  { CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, IS_STRING, 0, TEXT( "http://www.wetas-r-us.com" ) },
422 
423  /* Re-select the subject name after poking around in the altName */
425 
427  };
428 
429 /* Certificate requests for which the issue should fail, for various
430  reasons */
431 
432 static const CERT_DATA FAR_BSS certCA1Data[] = {
433  /* Identification information */
434  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
435  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
436  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
437  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Test CA user 1" ) },
438 
439  /* CA extensions. These should be rejected/stripped by the certificate
440  management code, since new CAs can only be created by the issuing CA
441  specifying it in the PKI user info */
445 
447  };
448 static const CERT_DATA FAR_BSS certCA2Data[] = {
449  /* Identification information */
450  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
451  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "Dave's Wetaburgers" ) },
452  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "Procurement" ) },
453  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "Test CA user 2" ) },
454 
455  /* CA extensions. A variant of the above with no basicConstraints but a
456  CA-only key usage */
459  CRYPT_KEYUSAGE_CRLSIGN },
460 
462  };
463 #ifdef USE_CERT_DNSTRING
464 static const CERT_DATA FAR_BSS certDodgyDNData[] = {
465  /* Identification information. The multi-AVA RDN should be rejected */
467  TEXT( "cn=www.example.com + cn=www.wetaburgers.com, ou=Procurement, o=Wetaburgers, c=NZ" ) },
468 
470  };
471 #endif /* USE_CERT_DNSTRING */
472 
473 /* Add a certification request to the certificate store */
474 
475 static int addCertRequest( const CRYPT_KEYSET cryptCertStore,
476  const CERT_DATA *certReqData,
477  const BOOLEAN isExpired )
478  {
480  CRYPT_CERTIFICATE cryptCertRequest;
481  int length, status;
482 
483  /* Generate a (short) key for the request */
484  status = cryptCreateContext( &cryptContext, CRYPT_UNUSED,
485  CRYPT_ALGO_RSA );
486  if( cryptStatusError( status ) )
487  return( status );
489  TEXT( "Private key" ),
490  paramStrlen( TEXT( "Private key" ) ) );
491  status = cryptGenerateKey( cryptContext );
492  if( cryptStatusError( status ) )
493  {
494  printf( "Creation of private key for certificate failed with error "
495  "code %d, line %d.\n", status, __LINE__ );
496  return( FALSE );
497  }
498 
499  /* Create the certification request. If we're adding an expiry time
500  we have to make it a CRMF request since a standard request can't
501  handle this */
502  status = cryptCreateCert( &cryptCertRequest, CRYPT_UNUSED, isExpired ? \
504  if( cryptStatusError( status ) )
505  {
506  printf( "cryptCreateCert() failed with error code %d, line %d.\n",
507  status, __LINE__ );
508  return( FALSE );
509  }
510  status = cryptSetAttribute( cryptCertRequest,
511  CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, cryptContext );
512 #ifndef _WIN32_WCE
513  if( cryptStatusOK( status ) && isExpired )
514  {
515  const time_t theTime = time( NULL ) + 5;
516 
517  /* Set the expiry time to a few seconds after the current time to
518  ensure that the certificate has expired by the time we need it.
519  This is a tiny bit risky since it requires that the interval
520  between setting this attribute and the creation of the certificate
521  below is less than five seconds, however there's no easy way to
522  guarantee the creation of a pre-expired certificate since if we
523  set the time too far back it won't be created */
524  status = cryptSetAttributeString( cryptCertRequest,
525  CRYPT_CERTINFO_VALIDTO, &theTime, sizeof( time_t ) );
526  }
527 #endif /* _WIN32_WCE */
528  if( cryptStatusError( status ) )
529  return( attrErrorExit( cryptCertRequest, "cryptSetAttribute()",
530  status, __LINE__ ) );
531  if( !addCertFields( cryptCertRequest, certReqData, __LINE__ ) )
532  {
533  /* We have to make sure that we exit cleanly here since some of the
534  tests verify the rejection of invalid request data, so a failure
535  at this point isn't a true error for these tests */
536  cryptDestroyCert( cryptCertRequest );
537  cryptDestroyContext( cryptContext );
538  return( FALSE );
539  }
540  status = cryptSignCert( cryptCertRequest, cryptContext );
541  cryptDestroyContext( cryptContext );
542  if( cryptStatusError( status ) )
543  return( attrErrorExit( cryptCertRequest, "cryptSignCert()",
544  status, __LINE__ ) );
545 
546  /* Export the request, destroy it, and recreate it by importing it again.
547  This is just a pedantic check to make sure that we emulate exactly a
548  real-world scenario of an externally-obtained request */
549  status = cryptExportCert( certBuffer, BUFFER_SIZE, &length,
551  cryptCertRequest );
552  cryptDestroyCert( cryptCertRequest );
553  if( cryptStatusOK( status ) )
554  status = cryptImportCert( certBuffer, length, CRYPT_UNUSED,
555  &cryptCertRequest );
556  if( cryptStatusError( status ) )
557  {
558  printf( "Couldn't export/re-import certificate request, status = "
559  "%d, line %d.\n", status, __LINE__ );
560  return( FALSE );
561  }
562 
563  /* Add the request to the certificate store */
564  status = cryptCAAddItem( cryptCertStore, cryptCertRequest );
565  if( cryptStatusError( status ) )
566  return( extErrorExit( cryptCertStore, "cryptCAAddItem()", status,
567  __LINE__ ) );
568 
569  return( cryptCertRequest );
570  }
571 
572 /* Add a revocation request to the certificate store. This code isn't
573  currently used because CMP doesn't allow revocation requests to be
574  signed so we can't create a signed object to add directly but have to
575  come in via CMP */
576 
577 #if 0
578 
579 static int addRevRequest( const CRYPT_KEYSET cryptCertStore,
580  const CERT_DATA *certReqData )
581  {
582  CRYPT_CERTIFICATE cryptCert, cryptCertRequest;
583  int i, status;
584 
585  /* Find the CN of the certificate we're revoking and use it to fetch the
586  certificate */
587  for( i = 0; certReqData[ i ].componentType != CRYPT_ATTRIBUTE_NONE; i++ )
588  if( certReqData[ i ].type == CRYPT_CERTINFO_COMMONNAME )
589  printf( "Revoking certificate for '%s'.\n",
590  ( char * ) certReqData[ i ].stringValue );
591  status = cryptGetPublicKey( cryptCertStore, &cryptCert, CRYPT_KEYID_NAME,
592  certReqData[ i ].stringValue );
593  if( cryptStatusError( status ) )
594  return( extErrorExit( cryptCertStore, "cryptGetPublicKey()", status,
595  __LINE__ ) );
596 
597  /* Create the revocation request */
598  status = cryptCreateCert( &cryptCertRequest, CRYPT_UNUSED,
600  if( cryptStatusError( status ) )
601  {
602  printf( "cryptCreateCert() failed with error code %d, line %d.\n",
603  status, __LINE__ );
604  return( FALSE );
605  }
606  status = cryptSetAttribute( cryptCertRequest, CRYPT_CERTINFO_CERTIFICATE,
607  cryptCert );
608  if( cryptStatusError( status ) )
609  return( attrErrorExit( cryptCertRequest, "cryptSetAttribute()",
610  status, __LINE__ ) );
611  if( !addCertFields( cryptCertRequest, revRequestData, __LINE__ ) )
612  return( FALSE );
613 
614  /* Add the request to the certificate store */
615  status = cryptCAAddItem( cryptCertStore, cryptCertRequest );
616  if( cryptStatusError( status ) )
617  return( extErrorExit( cryptCertStore, "cryptCAAddItem()", status,
618  __LINE__ ) );
619 
620  return( cryptCertRequest );
621  }
622 #endif /* 0 */
623 
624 /* Issue a certificate from a certificate request, and try and issue an
625  invalid certificate from a request, for which the issue process should
626  fail */
627 
628 static int issueCert( const CRYPT_KEYSET cryptCertStore,
629  const CRYPT_CONTEXT cryptCAKey,
630  const CERT_DATA *certReqData,
631  const BOOLEAN isExpired )
632  {
633  CRYPT_CERTIFICATE cryptCertRequest;
634  int i, status;
635 
636  /* Provide some feedback on what we're doing */
637  for( i = 0; certReqData[ i ].componentType != CRYPT_ATTRIBUTE_NONE; i++ )
638  {
639  if( certReqData[ i ].type == CRYPT_CERTINFO_COMMONNAME )
640  {
641  printf( "Issuing certificate for '%s'.\n",
642  ( char * ) certReqData[ i ].stringValue );
643  break;
644  }
645  }
646 
647  /* Issue the certificate via the certificate store */
648  cryptCertRequest = addCertRequest( cryptCertStore, certReqData, isExpired );
649  if( !cryptCertRequest )
650  return( FALSE );
652  cryptCertStore, cryptCAKey,
653  cryptCertRequest );
654  cryptDestroyCert( cryptCertRequest );
655  if( cryptStatusError( status ) )
656  {
657  if( isExpired && status == CRYPT_ERROR_INVALID )
658  {
659  puts( "The short-expiry-time certificate has already expired at "
660  "the time of issue.\nThis happened because there was a "
661  "delay of more than 5s between adding the\nrequest and "
662  "issuing the certificate for it. Try re-running the test "
663  "on a\nless-heavily-loaded system, or increase the expiry "
664  "delay to more than 5s." );
665  return( FALSE );
666  }
667  return( extErrorExit( cryptCertStore, "cryptCACertManagement()",
668  status, __LINE__ ) );
669  }
670 
671  return( TRUE );
672  }
673 
674 static int checkInvalidIssueRejected( const CRYPT_KEYSET cryptCertStore,
675  const CRYPT_CONTEXT cryptCAKey,
676  const CERT_DATA *certReqData,
677  const BOOLEAN requestCreationShouldFail )
678  {
679  CRYPT_CERTIFICATE cryptCertRequest;
680  int i, status;
681 
682  /* Provide some feedback on what we're doing */
683  for( i = 0; certReqData[ i ].componentType != CRYPT_ATTRIBUTE_NONE; i++ )
684  {
685  if( certReqData[ i ].type == CRYPT_CERTINFO_COMMONNAME )
686  {
687  printf( "Issuing certificate for '%s'.\n",
688  ( char * ) certReqData[ i ].stringValue );
689  break;
690  }
691  }
692  if( certReqData[ i ].componentType == CRYPT_ATTRIBUTE_NONE )
693  {
694  /* If the certificate doesn't have a CN attribute then it's one with
695  a synthetic DN created to test the DN-validity checks */
696  puts( "Issuing certificate for synthetic invalid DN." );
697  }
698 
699  /* Issue the certificate via the certificate store */
700  cryptCertRequest = addCertRequest( cryptCertStore, certReqData, FALSE );
701  if( !cryptCertRequest )
702  {
703  /* In some cases the invalid request will be caught as soon as it's
704  created (this is a bit unfortunate because we really want to
705  check whether an import of someone else's invalid request will
706  fail, however this is checked indirectly because it demonstrates
707  that the certificate code won't allow the use of such a value in
708  a certificate */
709  if( requestCreationShouldFail )
710  {
711  puts( " (This is an expected result since this test verifies "
712  "handling of\n invalid request data)." );
713  return( TRUE );
714  }
715  return( FALSE );
716  }
718  cryptCertStore, cryptCAKey,
719  cryptCertRequest );
720  cryptDestroyCert( cryptCertRequest );
721  if( cryptStatusError( status ) )
722  {
723  /* This is a check of the request validity-checking system so the
724  issue is supposed to fail */
725  return( TRUE );
726  }
727 
728  return( FALSE );
729  }
730 
731 /* Issue a CRL. Although we can't do this directly (see the comment above
732  for the revocation request code) we can at least test the ability to
733  create an empty CRL (and if the CMP code has been run there will probably
734  be a few revocation entries present to fill the CRL) */
735 
736 static int issueCRL( const CRYPT_KEYSET cryptCertStore,
737  const CRYPT_CONTEXT cryptCAKey )
738  {
739  CRYPT_CERTIFICATE cryptCRL;
740  int noEntries = 0, status;
741 
742  /* Issue the CRL via the certificate store */
744  cryptCertStore, cryptCAKey,
745  CRYPT_UNUSED );
746  if( cryptStatusError( status ) )
747  return( extErrorExit( cryptCertStore, "cryptCACertManagement()",
748  status, __LINE__ ) );
749 
750  /* Print information on the CRL */
751  if( cryptStatusOK( cryptSetAttribute( cryptCRL,
753  CRYPT_CURSOR_FIRST ) ) )
754  do
755  noEntries++;
756  while( cryptSetAttribute( cryptCRL,
759  printf( "CRL has %d entr%s.\n", noEntries,
760  ( noEntries == 1 ) ? "y" : "ies" );
761  if( !noEntries )
762  puts( " (This is probably because there haven't been any revocation "
763  "entries added\n via the CMP test yet)." );
764 
765  /* Clean up */
766  cryptDestroyCert( cryptCRL );
767  return( TRUE );
768  }
769 
770 /* Fetch the issued certificate that was created from a given cert template */
771 
772 static CRYPT_CERTIFICATE getCertFromTemplate( const CRYPT_KEYSET cryptCertStore,
773  const CERT_DATA *certReqData )
774  {
775  CRYPT_CERTIFICATE cryptCert;
776  int i, status;
777 
778  for( i = 0; certReqData[ i ].componentType != CRYPT_ATTRIBUTE_NONE; i++ )
779  if( certReqData[ i ].type == CRYPT_CERTINFO_COMMONNAME )
780  break;
781  status = cryptGetPublicKey( cryptCertStore, &cryptCert, CRYPT_KEYID_NAME,
782  certReqData[ i ].stringValue );
783  return( cryptStatusOK( status ) ? cryptCert : status );
784  }
785 
787  {
788  CRYPT_CERTIFICATE cryptCert, cryptCertRequest;
789  CRYPT_CONTEXT cryptCAKey;
790  CRYPT_KEYSET cryptCertStore;
791  time_t certTime;
792  int dummy, status;
793 
794  puts( "Testing certificate management using certificate store..." );
795 
796  /* Get the CA's private key */
797  status = getPrivateKey( &cryptCAKey, CA_PRIVKEY_FILE,
799  if( cryptStatusError( status ) )
800  {
801  printf( "CA private key read failed with error code %d, line %d.\n",
802  status, __LINE__ );
803  return( FALSE );
804  }
805 
806  /* Create the certificate store keyset with a check to make sure that
807  this access method exists so we can return an appropriate error
808  message. If the database table already exists, this will return a
809  duplicate data error so we retry the open with no flags to open the
810  existing database keyset for write access */
811  status = cryptKeysetOpen( &cryptCertStore, CRYPT_UNUSED,
814  if( cryptStatusOK( status ) )
815  puts( "Created new certificate store '" CERTSTORE_KEYSET_NAME_ASCII
816  "'." );
817  if( status == CRYPT_ERROR_PARAM3 )
818  {
819  /* This type of keyset access isn't available, return a special error
820  code to indicate that the test wasn't performed, but that this
821  isn't a reason to abort processing */
822  cryptDestroyContext( cryptCAKey );
823  return( CRYPT_ERROR_NOTAVAIL );
824  }
825  if( status == CRYPT_ERROR_DUPLICATE )
826  status = cryptKeysetOpen( &cryptCertStore, CRYPT_UNUSED,
829  if( cryptStatusError( status ) )
830  {
831  printf( "cryptKeysetOpen() failed with error code %d, line %d.\n",
832  status, __LINE__ );
833  if( status == CRYPT_ERROR_OPEN )
834  {
835  cryptDestroyContext( cryptCAKey );
836  return( CRYPT_ERROR_FAILED );
837  }
838  return( FALSE );
839  }
840 
841  /* Create a certificate request, add it to the store, and destroy it,
842  simulating a delayed certificate issue in which the request can't
843  immediately be converted into a certificate. Then read the request
844  back from the store and issue a certificate based on it */
845  puts( "Issuing certificate for 'Test user 1'..." );
846  cryptCertRequest = addCertRequest( cryptCertStore, cert1Data, FALSE );
847  if( !cryptCertRequest )
848  return( FALSE );
849  cryptDestroyCert( cryptCertRequest );
850  status = cryptCAGetItem( cryptCertStore, &cryptCertRequest,
852  TEXT( "Test user 1" ) );
853  if( cryptStatusError( status ) )
854  return( extErrorExit( cryptCertStore, "cryptCAGetItem()", status,
855  __LINE__ ) );
857  cryptCertStore, cryptCAKey,
858  cryptCertRequest );
859  cryptDestroyCert( cryptCertRequest );
860  if( cryptStatusError( status ) )
861  return( extErrorExit( cryptCertStore, "cryptCACertManagement()",
862  status, __LINE__ ) );
863  cryptDestroyCert( cryptCert );
864 
865  /* Issue some more certs, this time directly from the request and without
866  bothering to obtain the resulting certificate. The first two have a
867  validity time that expires in a few seconds so that we can use them
868  to test certificate expiry processing, we issue these first to ensure
869  that as much time as possible passes due to other operations occurring
870  before we run the expiry. The second two are for revocation and CRL
871  testing */
872  if( !issueCert( cryptCertStore, cryptCAKey, expiredCert1Data, TRUE ) )
873  return( FALSE );
874  if( !issueCert( cryptCertStore, cryptCAKey, expiredCert2Data, TRUE ) )
875  return( FALSE );
876  if( !issueCert( cryptCertStore, cryptCAKey, revokableCert1Data, FALSE ) )
877  return( FALSE );
878  if( !issueCert( cryptCertStore, cryptCAKey, revokableCert2Data, FALSE ) )
879  return( FALSE );
880 
881  /* The following tests are specifically inserted at this point (rather
882  than at some other point in the test run) because they'll add some
883  further delay before the expiry operation */
884 
885  /* Try and get a CA certificate issued. This should fail, since new CAs
886  can only be created if the issuing CA specifies it (either directly
887  when it creates the certificate manually or via the PKI user info),
888  but never at the request of the user */
889  if( !checkInvalidIssueRejected( cryptCertStore, cryptCAKey,
890  certCA1Data, TRUE ) || \
891  !checkInvalidIssueRejected( cryptCertStore, cryptCAKey,
892  certCA2Data, FALSE ) )
893  {
894  printf( "Issue of certificate from invalid request succeeded when "
895  "it should have failed,\nline %d.\n", __LINE__ );
896  return( FALSE );
897  }
898 
899 #ifdef USE_CERT_DNSTRING
900  /* Try and get a certificate with a multi-AVA RDN issued. This should
901  fail, since such DN shenanigans can only be performed if the CA
902  creates the required DN directly */
903  if( !checkInvalidIssueRejected( cryptCertStore, cryptCAKey,
904  certDodgyDNData, FALSE ) )
905  {
906  printf( "Issue of certificate from invalid request succeeded when "
907  "it should have failed,\nline %d.\n", __LINE__ );
908  return( FALSE );
909  }
910 #endif /* USE_CERT_DNSTRING */
911 
912  /* Get a certificate and (to-be-)revoked certificate from the store and
913  save them to disk for later tests */
914  status = cryptCert = getCertFromTemplate( cryptCertStore, cert1Data );
915  if( !cryptStatusError( status ) )
916  {
918  FILE *filePtr;
919  int length;
920 
921  /* First save the CA certificate */
923  status = cryptExportCert( certBuffer, BUFFER_SIZE, &length,
925  cryptCAKey );
926  if( cryptStatusOK( status ) && \
927  ( filePtr = fopen( fileName, "wb" ) ) != NULL )
928  {
929  int count;
930 
931  count = fwrite( certBuffer, 1, length, filePtr );
932  fclose( filePtr );
933  if( count < length )
934  {
935  remove( fileName );
936  puts( "Warning: Couldn't save OCSP CA certificate to disk, "
937  "this will cause later\n OCSP server tests to "
938  "fail. Press a key to continue." );
939  getchar();
940  }
941  }
942 
943  /* Then the EE certificate */
945  status = cryptExportCert( certBuffer, BUFFER_SIZE, &length,
947  cryptCert );
948  if( cryptStatusOK( status ) && \
949  ( filePtr = fopen( fileName, "wb" ) ) != NULL )
950  {
951  int count;
952 
953  count = fwrite( certBuffer, 1, length, filePtr );
954  fclose( filePtr );
955  if( count < length )
956  {
957  remove( fileName );
958  puts( "Warning: Couldn't save OCSP non-revoked certificate "
959  "to disk, this will cause later\n OCSP server "
960  "tests to fail. Press a key to continue." );
961  getchar();
962  }
963  }
964  cryptDestroyCert( cryptCert );
965  }
966  if( !cryptStatusError( status ) )
967  status = cryptCert = getCertFromTemplate( cryptCertStore,
968  revokableCert1Data );
969  if( !cryptStatusError( status ) )
970  {
972  FILE *filePtr;
973  int length;
974 
976  status = cryptExportCert( certBuffer, BUFFER_SIZE, &length,
978  cryptCert );
979  if( cryptStatusOK( status ) && \
980  ( filePtr = fopen( fileName, "wb" ) ) != NULL )
981  {
982  int count;
983 
984  count = fwrite( certBuffer, 1, length, filePtr );
985  fclose( filePtr );
986  if( count < length )
987  {
988  remove( fileName );
989  puts( "Warning: Couldn't save OCSP revoked certificate "
990  "to disk, this will cause later\n OCSP server "
991  "tests to fail. Press a key to continue." );
992  getchar();
993  }
994  }
995  cryptDestroyCert( cryptCert );
996  }
997  if( cryptStatusError( status ) )
998  {
999  puts( "Issued certificates couldn't be fetched from the certificate "
1000  "store and written\nto disk, the OCSP server test will abort "
1001  "when it fails to find\nthese certificates." );
1002  }
1003 
1004  /* Issue a CRL. This will probably be a zero-length CRL unless we've run
1005  the CMP tests because we can't directly revoke a certificate. Again,
1006  we perform it before the expiry test because it'll add some further
1007  delay */
1008  if( !issueCRL( cryptCertStore, cryptCAKey ) )
1009  return( FALSE );
1010 
1011  /* Get the most recent of the expired certs and wait for it to expire
1012  if necessary */
1013  status = cryptCert = getCertFromTemplate( cryptCertStore,
1014  expiredCert1Data );
1015  if( !cryptStatusError( status ) )
1016  status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_VALIDTO,
1017  &certTime, &dummy );
1018  if( cryptStatusError( status ) )
1019  {
1020  puts( "Couldn't get expiry information for expired certificate." );
1021  return( FALSE );
1022  }
1023 #ifndef _WIN32_WCE
1024  if( certTime >= time( NULL ) )
1025  {
1026  printf( "Waiting for certificates to expire.." );
1027  while( certTime >= time( NULL ) )
1028  {
1029  delayThread( 1 );
1030  printf( "." );
1031  }
1032  puts( " done." );
1033  }
1034 #endif /* _WIN32_WCE */
1035  cryptDestroyCert( cryptCert );
1036 
1037  /* Expire the certs */
1038  puts( "Expiring certificates..." );
1040  cryptCertStore, CRYPT_UNUSED,
1041  CRYPT_UNUSED );
1042  if( cryptStatusError( status ) )
1043  return( extErrorExit( cryptCertStore, "cryptCACertManagement()",
1044  status, __LINE__ ) );
1045 
1046  /* Clean up */
1047  cryptDestroyContext( cryptCAKey );
1048  cryptKeysetClose( cryptCertStore );
1049  puts( "Certificate management using certificate store succeeded.\n" );
1050  return( TRUE );
1051  }