cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
imp_exp.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate Import/Export Routines *
4 * Copyright Peter Gutmann 1997-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "asn1.h"
11 #else
12  #include "cert/cert.h"
13  #include "enc_dec/asn1.h"
14 #endif /* Compiler-specific includes */
15 
16 #ifdef USE_CERTIFICATES
17 
18 /****************************************************************************
19 * *
20 * base64/PEM/SMIME Processing Functions *
21 * *
22 ****************************************************************************/
23 
24 /* Decode a base64/PEM/SMIME-encoded certificate into a temporary buffer */
25 
26 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
27 static int decodeCertificate( IN_BUFFER( certObjectLength ) const void *certObject,
28  IN_LENGTH const int certObjectLength,
29  OUT_OPT_PTR void **newObject,
30  OUT_LENGTH_Z int *newObjectLength )
31  {
32  void *decodedObjectPtr;
33  int decodedLength, status;
34 
35  assert( isReadPtr( certObject, certObjectLength ) );
36  assert( isWritePtr( newObject, sizeof( void * ) ) );
37  assert( isWritePtr( newObjectLength, sizeof( int ) ) );
38 
39  REQUIRES( certObjectLength > 0 && certObjectLength < MAX_INTLENGTH );
40 
41  /* Clear return values */
42  *newObject = NULL;
43  *newObjectLength = 0;
44 
45  /* Allocate a temporary buffer to decode the certificate object data
46  into */
47  status = base64decodeLen( certObject, certObjectLength,
48  &decodedLength );
49  if( cryptStatusError( status ) )
50  return( status );
51  if( decodedLength < 64 || decodedLength >= MAX_INTLENGTH_SHORT )
52  return( CRYPT_ERROR_UNDERFLOW );
53  if( ( decodedObjectPtr = clAlloc( "decodeCertificate", \
54  decodedLength + 8 ) ) == NULL )
55  return( CRYPT_ERROR_MEMORY );
56 
57  /* Decode the base64/PEM/SMIME-encoded certificate data into the
58  temporary buffer */
59  status = base64decode( decodedObjectPtr, decodedLength, &decodedLength,
60  certObject, certObjectLength,
62  if( cryptStatusOK( status ) )
63  {
64  /* Make sure that the decoded data length is still valid. We don't
65  allow long lengths here because we shouldn't be getting things
66  like mega-CRLs as email messages */
67  if( decodedLength < 64 || decodedLength >= MAX_INTLENGTH_SHORT )
68  status = CRYPT_ERROR_BADDATA;
69  }
70  if( cryptStatusError( status ) )
71  {
72  clFree( "decodeCertificate", decodedObjectPtr );
73  return( status );
74  }
75  *newObject = decodedObjectPtr;
76  *newObjectLength = decodedLength;
77 
78  return( CRYPT_OK );
79  }
80 
81 /* Detect the encoded form of certificate data, either raw binary, raw
82  binary with a MIME header, or some form of text encoding. Autodetection
83  in the presence of EBCDIC gets a bit more complicated because the text
84  content can potentially be either EBCDIC or ASCII so we have to add an
85  extra layer of checking for EBCDIC */
86 
87 #ifdef EBCDIC_CHARS
88 
89 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
90 static int checkTextEncoding( IN_BUFFER( certObjectLength ) const void *certObject,
91  IN_LENGTH const int certObjectLength,
92  OUT_PTR void **newObject,
93  OUT_LENGTH int *newObjectLength )
94  {
95  CRYPT_CERTFORMAT_TYPE format;
96  void *asciiObject = NULL;
97  int offset, status;
98 
99  assert( isReadPtr( certObject, certObjectLength ) );
100  assert( isWritePtr( newObject, sizeof( void * ) ) );
101  assert( isWritePtr( newObjectLength, sizeof( int ) ) );
102 
103  REQUIRES( certObjectLength > 0 && certObjectLength < MAX_INTLENGTH );
104 
105  /* Initialise the return values to the default settings, the identity
106  transformation */
107  *newObject = ( void * ) certObject;
108  *newObjectLength = certObjectLength;
109 
110  /* Check for a header that identifies some form of encoded object */
111  status = base64checkHeader( certObject, certObjectLength, &format,
112  &offset );
113  if( cryptStatusError( status ) )
114  {
115  int status;
116 
117  /* If we get a decoding error (i.e. it's not either an unencoded
118  object or some form of ASCII encoded object) try again assuming
119  that the source is EBCDIC */
120  if( ( asciiObject = clAlloc( "checkTextEncoding",
121  certObjectLength + 8 ) ) == NULL )
122  return( CRYPT_ERROR_MEMORY );
123  status = ebcdicToAscii( asciiObject, certObject, certObjectLength );
124  if( cryptStatusError( status ) )
125  return( status );
126  certObject = asciiObject;
127  status = base64checkHeader( certObject, certObjectLength, &format,
128  &offset );
129  if( cryptStatusOK( status ) && \
130  ( format != CRYPT_ICERTFORMAT_SMIME_CERTIFICATE && \
132  {
133  clFree( "checkTextEncoding", asciiObject );
134  asciiObject = NULL;
135  }
136  if( cryptStatusError( status ) )
137  return( status );
138  }
139 
140  /* If it's not encoded in any way, we're done */
141  if( format == CRYPT_CERTFORMAT_NONE )
142  return( CRYPT_OK );
143 
144  /* Make sure that the length after (potentially) skipping the header is
145  still valid, since this will now be different from the length that
146  was validated by the kernel */
147  if( certObjectLength - offset < 64 || \
148  certObjectLength - offset > MAX_INTLENGTH )
149  {
150  if( asciiObject != NULL )
151  clFree( "checkTextEncoding", asciiObject );
152  return( CRYPT_ERROR_UNDERFLOW );
153  }
154 
155  if( format == CRYPT_ICERTFORMAT_SMIME_CERTIFICATE || \
157  {
158  status = decodeCertificate( ( const char * ) certObject + offset,
159  certObjectLength - offset, newObject,
160  newObjectLength );
161  if( asciiObject != NULL )
162  clFree( "checkTextEncoding", asciiObject );
163  if( cryptStatusError( status ) )
164  return( status );
165 
166  /* Let the caller know that they have to free the temporary decoding
167  buffer before they exit. This is somewhat ugly but necessary for
168  the EBCDIC case because of the extra level of conversion that's
169  required before we can base64-decode it */
170  return( OK_SPECIAL );
171  }
172 
173  /* If it's binary-encoded MIME data we don't need to decode it but still
174  need to skip the MIME header */
175  if( format == CRYPT_CERTFORMAT_CERTIFICATE || \
176  format == CRYPT_CERTFORMAT_CERTCHAIN )
177  {
178  ENSURES( offset > 0 && offset < MAX_INTLENGTH );
179 
180  *newObject = ( BYTE * ) certObject + offset;
181  *newObjectLength = certObjectLength - offset;
182  }
183 
184  return( CRYPT_OK );
185  }
186 #else
187 
188 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
189 static int checkTextEncoding( IN_BUFFER( certObjectLength ) const void *certObject,
190  IN_LENGTH const int certObjectLength,
191  OUT_PTR void **objectData,
193  {
194  CRYPT_CERTFORMAT_TYPE format;
195  int offset, status;
196 
197  assert( isReadPtr( certObject, certObjectLength ) );
198  assert( isWritePtr( objectData, sizeof( void * ) ) );
199  assert( isWritePtr( objectDataLength, sizeof( int ) ) );
200 
201  REQUIRES( certObjectLength > 0 && certObjectLength < MAX_INTLENGTH );
202 
203  /* Initialise the return values to the default settings, the identity
204  transformation */
205  *objectData = ( void * ) certObject;
206  *objectDataLength = certObjectLength;
207 
208  /* Check for a header that identifies some form of encoded object */
209  status = base64checkHeader( certObject, certObjectLength, &format,
210  &offset );
211  if( cryptStatusError( status ) )
212  return( status );
213 
214  /* If it's not encoded in any way, we're done */
215  if( format == CRYPT_CERTFORMAT_NONE )
216  return( CRYPT_OK );
217 
218  /* Make sure that the length after (potentially) skipping the header is
219  still valid, since this will now be different from the length that
220  was validated by the kernel */
221  if( certObjectLength - offset < 64 || \
222  certObjectLength - offset > MAX_INTLENGTH )
223  return( CRYPT_ERROR_UNDERFLOW );
224 
225  /* Remember the position of the payload, which may be either binary
226  (with a MIME header) or base64-encoded (with or without a header) */
227  *objectData = ( BYTE * ) certObject + offset;
228  *objectDataLength = certObjectLength - offset;
229  ENSURES( rangeCheckZ( offset, *objectDataLength, certObjectLength ) );
230 
231  /* It's base64-encoded let the caller know that it needs decoding */
232  return( ( format == CRYPT_ICERTFORMAT_SMIME_CERTIFICATE || \
233  format == CRYPT_CERTFORMAT_TEXT_CERTIFICATE ) ? \
234  OK_SPECIAL : CRYPT_OK );
235  }
236 #endif /* EBCDIC_CHARS */
237 
238 /****************************************************************************
239 * *
240 * Import a Certificate *
241 * *
242 ****************************************************************************/
243 
244 /* Import a certificate object. If the import type is set to create a data-
245  only certificate then its publicKeyInfo pointer is set to the start of
246  the encoded public key to allow it to be decoded later */
247 
248 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
249 int importCert( IN_BUFFER( certObjectLength ) const void *certObject,
250  IN_LENGTH const int certObjectLength,
254  IN_BUFFER_OPT( keyIDlength ) const void *keyID,
255  IN_LENGTH_KEYID_Z const int keyIDlength,
256  IN_ENUM_OPT( CRYPT_CERTTYPE ) \
257  const CRYPT_CERTTYPE_TYPE formatHint )
258  {
261  STREAM stream;
262  READCERT_FUNCTION readCertFunction;
263  BOOLEAN isDecodedObject = FALSE;
264  void *certObjectPtr = ( void * ) certObject, *certBuffer;
265  int objectLength = certObjectLength, length, offset = 0;
266  int complianceLevel, initStatus = CRYPT_OK, status;
267 
268  assert( isReadPtr( certObject, certObjectLength ) );
269  assert( isWritePtr( certificate, sizeof( CRYPT_CERTIFICATE ) ) );
270  assert( ( keyIDtype == CRYPT_KEYID_NONE && \
271  keyID == NULL && keyIDlength == 0 ) || \
272  ( keyIDtype != CRYPT_KEYID_NONE && \
273  isReadPtr( keyID, keyIDlength ) ) );
274 
275  REQUIRES( certObjectLength > 0 && certObjectLength < MAX_INTLENGTH );
276  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
277  isHandleRangeValid( iCryptOwner ) );
278  REQUIRES( ( keyIDtype == CRYPT_KEYID_NONE && \
279  keyID == NULL && keyIDlength == 0 ) || \
280  ( ( keyIDtype > CRYPT_KEYID_NONE && \
281  keyIDtype < CRYPT_KEYID_LAST ) && \
282  keyID != NULL && \
283  keyIDlength >= MIN_NAME_LENGTH && \
284  keyIDlength < MAX_ATTRIBUTE_SIZE ) );
285  REQUIRES( formatHint >= CRYPT_CERTTYPE_NONE && \
286  formatHint < CRYPT_CERTTYPE_LAST );
287 
288  /* Clear return value */
289  *certificate = CRYPT_ERROR;
290 
291  /* Determine how much checking we need to perform */
292  status = krnlSendMessage( iCryptOwner, IMESSAGE_GETATTRIBUTE,
293  &complianceLevel,
295  if( cryptStatusError( status ) )
296  return( status );
297 
298  /* If it's not a pre-specified or special-case format, check whether
299  it's some form of encoded certificate object, and decode it if
300  required */
301  if( formatHint == CRYPT_CERTTYPE_NONE )
302  {
303  status = checkTextEncoding( certObject, certObjectLength,
304  &certObjectPtr, &objectLength );
305  if( cryptStatusError( status ) )
306  {
307  if( status != OK_SPECIAL )
308  return( status );
309 #ifndef EBCDIC_CHARS
310  status = decodeCertificate( certObjectPtr, objectLength,
311  &certObjectPtr, &objectLength );
312  if( cryptStatusError( status ) )
313  return( status );
314 #endif /* EBCDIC_CHARS */
315 
316  /* The certificate object has been decoded into a temporary
317  buffer, remember that we have to free it before we exit */
318  isDecodedObject = TRUE;
319  }
320  }
321 
322  /* Determine the object's type and length and check the encoding unless
323  we're running in oblivious mode (qui omnes insidias timet in nullas
324  incidit - Syrus). In addition we can't perform the check for SSL
325  certificate chains because there's SSL length data between each
326  certificate, so it has to be performed later when each certificate
327  in the chain is read */
328  sMemConnect( &stream, certObjectPtr, objectLength );
329  status = getCertObjectInfo( &stream, &offset, &length, &type,
330  formatHint );
331  sMemDisconnect( &stream );
332  if( cryptStatusOK( status ) && \
333  complianceLevel > CRYPT_COMPLIANCELEVEL_OBLIVIOUS && \
334  formatHint != CRYPT_ICERTTYPE_SSL_CERTCHAIN )
335  {
336  ENSURES( rangeCheckZ( offset, length, objectLength ) );
337  if( cryptStatusError( \
338  checkObjectEncoding( ( BYTE * ) certObjectPtr + offset,
339  length ) ) )
340  status = CRYPT_ERROR_BADDATA;
341  }
342  if( cryptStatusError( status ) )
343  {
344  if( isDecodedObject )
345  clFree( "importCert", certObjectPtr );
346  return( status );
347  }
348 
349  /* If it's a certificate chain this is handled specially since we need
350  to import a plurality of certificates at once */
351  if( type == CRYPT_CERTTYPE_CERTCHAIN || \
352  type == CRYPT_ICERTTYPE_CMS_CERTSET || \
353  type == CRYPT_ICERTTYPE_SSL_CERTCHAIN )
354  {
355  /* Read the certificate chain into a collection of internal
356  certificate objects. This returns a handle to the leaf
357  certificate in the chain with the remaining certificates being
358  accessible within it via the certificate cursor functions.
359  Because the different chain types are used only to distinguish
360  the chain wrapper type on import, the final object type which is
361  created is always a CRYPT_CERTTYPE_CERTCHAIN no matter what the
362  import format was */
363  ENSURES( rangeCheckZ( offset, length, objectLength ) );
364  sMemConnect( &stream, ( BYTE * ) certObjectPtr + offset, length );
365  if( type == CRYPT_CERTTYPE_CERTCHAIN )
366  readSequence( &stream, NULL ); /* Skip the outer wrapper */
367  status = readCertChain( &stream, certificate, iCryptOwner, type,
368  keyIDtype, keyID, keyIDlength,
369  ( formatHint == CRYPT_ICERTTYPE_DATAONLY ||
370  formatHint == CRYPT_ICERTTYPE_CTL ) ? \
371  TRUE : FALSE );
372  sMemDisconnect( &stream );
373  if( isDecodedObject )
374  clFree( "importCert", certObjectPtr );
375  return( status );
376  }
377 
378  ENSURES( keyIDtype == CRYPT_KEYID_NONE && keyID == NULL && \
379  keyIDlength == 0 );
380 
381  /* Select the function to use to read the certificate object */
382  readCertFunction = getCertReadFunction( type );
383  if( readCertFunction == NULL )
384  {
385  /* In theory this should be an internal error if there's no decode
386  function corresponding to an identified certificate object type
387  present, however with the ability to selectively disable some
388  capabilities it may just correspond to a disabled capability so
389  we return a not-available error instead */
390  if( isDecodedObject )
391  clFree( "importCert", certObjectPtr );
392  return( CRYPT_ERROR_NOTAVAIL );
393  }
394 
395  /* Allocate a buffer to store a copy of the object so that we can
396  preserve the original for when it's needed again later, and try and
397  create the certificate object. All of the objects (including the CMS
398  attributes, which in theory aren't needed for anything further) need
399  to be kept around in their encoded form, which is often incorrect and
400  therefore can't be reconstructed from the decoded information. The
401  readXXX() functions also record pointers to the various required
402  encoded fields such as DNs and key data so that they can be recovered
403  later in their (possibly incorrect) form and these pointers need to
404  be to a persistent copy of the encoded object. In addition the
405  certificate objects need to be kept around anyway for signature
406  checks and possible re-export */
407  if( ( certBuffer = clAlloc( "importCert", length ) ) == NULL )
408  {
409  if( isDecodedObject )
410  clFree( "importCert", certObjectPtr );
411  return( CRYPT_ERROR_MEMORY );
412  }
413 
414  /* Create the certificate object */
415  status = createCertificateInfo( &certInfoPtr, iCryptOwner, type );
416  if( cryptStatusError( status ) )
417  {
418  if( isDecodedObject )
419  clFree( "importCert", certObjectPtr );
420  clFree( "importCert", certBuffer );
421  return( status );
422  }
423  *certificate = status;
424 
425  /* If we're doing a deferred read of the public key components (they'll
426  be decoded later when we know whether we need them) set the data-only
427  flag to ensure that we don't try to decode them */
428  if( formatHint == CRYPT_ICERTTYPE_DATAONLY || \
429  formatHint == CRYPT_ICERTTYPE_CTL )
430  certInfoPtr->flags |= CERT_FLAG_DATAONLY;
431 
432  /* If we're reading a single entry from a CRL, indicate that the
433  resulting object is a standalone single CRL entry rather than a proper
434  CRL */
435  if( formatHint == CRYPT_ICERTTYPE_REVINFO )
436  certInfoPtr->flags |= CERT_FLAG_CRLENTRY;
437 
438  /* Copy in the certificate object for later use */
439  ENSURES( rangeCheckZ( offset, length, objectLength ) );
440  memcpy( certBuffer, ( BYTE * ) certObjectPtr + offset, length );
441  certInfoPtr->certificate = certBuffer;
442  certInfoPtr->certificateSize = length;
443 
444  /* Parse the data into the certificate object. Note that we have to use
445  the copy in the certBuffer rather than the original since the
446  readXXX() functions will record pointers to various required encoded
447  fields */
448  sMemConnect( &stream, certBuffer, length );
449  if( type != CRYPT_CERTTYPE_CMS_ATTRIBUTES && \
450  type != CRYPT_CERTTYPE_RTCS_REQUEST && \
452  {
453  /* Skip the outer wrapper */
454  readLongSequence( &stream, NULL );
455  }
456  status = readCertFunction( &stream, certInfoPtr );
457  sMemDisconnect( &stream );
458  if( isDecodedObject )
459  clFree( "importCert", certObjectPtr );
460  if( cryptStatusError( status ) )
461  {
462  /* The import failed, make sure that the object gets destroyed when
463  we notify the kernel that the setup process is complete. We also
464  have to explicitly destroy the attached context since at this
465  point it hasn't been associated with the certificate yet so it
466  won't be automatically destroyed by the kernel when the
467  certificate is destroyed */
468  krnlSendNotifier( *certificate, IMESSAGE_DESTROY );
469  if( certInfoPtr->iPubkeyContext != CRYPT_ERROR )
470  {
471  krnlSendNotifier( certInfoPtr->iPubkeyContext,
473  certInfoPtr->iPubkeyContext = CRYPT_ERROR;
474  }
475  initStatus = status;
476  }
477 
478  /* We've finished setting up the object-type-specific information, tell
479  the kernel that the object is ready for use */
480  status = krnlSendMessage( *certificate, IMESSAGE_SETATTRIBUTE,
481  MESSAGE_VALUE_OK, CRYPT_IATTRIBUTE_STATUS );
482  if( cryptStatusError( initStatus ) || cryptStatusError( status ) )
483  {
484  *certificate = CRYPT_ERROR;
485  return( cryptStatusError( initStatus ) ? initStatus : status );
486  }
487 
488  /* If this is a type of object that has a public key associated with it,
489  notify the kernel that the given context is attached to the
490  certificate. Note that we can only do this at this point because the
491  certificate object can't receive general messages until its status is
492  set to OK. In addition since this is an internal object used only by
493  the certificate we tell the kernel not to increment its reference
494  count when it attaches it to the certificate object. Finally, we're
495  ready to go so we mark the object as initialised (we can't do this
496  before the initialisation is complete because the kernel won't
497  forward the message to a not-ready-for-use object)*/
498  if( certInfoPtr->iPubkeyContext != CRYPT_ERROR )
499  {
500  krnlSendMessage( *certificate, IMESSAGE_SETDEPENDENT,
501  &certInfoPtr->iPubkeyContext,
503  }
504  return( krnlSendMessage( *certificate, IMESSAGE_SETATTRIBUTE,
506  CRYPT_IATTRIBUTE_INITIALISED ) );
507  }
508 
509 /****************************************************************************
510 * *
511 * Import a Certificate *
512 * *
513 ****************************************************************************/
514 
515 /* Export a certificate object. This just writes the internal encoded
516  object data to an external buffer. For certificate/certificate chain
517  export the possibilities are as follows:
518 
519  Export
520  Type | Cert Chain
521  ------+--------------------+---------------
522  Cert | Cert | Cert as chain
523  | |
524  Chain | Currently selected | Chain
525  | cert in chain | */
526 
527 CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5 ) ) \
528 int exportCert( OUT_BUFFER_OPT( certObjectMaxLength, *certObjectLength ) \
529  void *certObject,
530  IN_LENGTH const int certObjectMaxLength,
531  OUT_LENGTH_Z int *certObjectLength,
532  IN_ENUM( CRYPT_CERTFORMAT ) \
533  const CRYPT_CERTFORMAT_TYPE certFormatType,
534  const CERT_INFO *certInfoPtr )
535  {
536  const CRYPT_CERTFORMAT_TYPE baseFormatType = \
537  ( certFormatType == CRYPT_CERTFORMAT_TEXT_CERTIFICATE || \
538  certFormatType == CRYPT_CERTFORMAT_XML_CERTIFICATE ) ? \
540  ( certFormatType == CRYPT_CERTFORMAT_TEXT_CERTCHAIN || \
541  certFormatType == CRYPT_CERTFORMAT_XML_CERTCHAIN ) ? \
542  CRYPT_CERTFORMAT_CERTCHAIN : \
543  certFormatType;
544  STREAM stream;
545  void *buffer;
547 
548  assert( ( certObject == NULL && certObjectMaxLength == 0 ) || \
549  isWritePtr( certObject, certObjectMaxLength ) );
550  assert( isWritePtr( certObjectLength, sizeof( int ) ) );
551  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
552 
553  REQUIRES( ( certObject == NULL && certObjectMaxLength == 0 ) || \
554  ( certObject != NULL && \
555  certObjectMaxLength > 0 && \
556  certObjectMaxLength < MAX_INTLENGTH ) );
557  REQUIRES( certFormatType > CRYPT_CERTFORMAT_NONE && \
558  certFormatType < CRYPT_CERTFORMAT_LAST );
559 
560  /* If it's an internal format, write it and exit */
561  if( certFormatType == CRYPT_ICERTFORMAT_CERTSET || \
562  certFormatType == CRYPT_ICERTFORMAT_CERTSEQUENCE || \
563  certFormatType == CRYPT_ICERTFORMAT_SSL_CERTCHAIN )
564  {
565  *certObjectLength = ( int ) \
566  sizeofCertCollection( certInfoPtr, certFormatType );
567  if( certObject == NULL )
568  return( CRYPT_OK );
569  if( *certObjectLength > certObjectMaxLength )
570  return( CRYPT_ERROR_OVERFLOW );
571  sMemOpen( &stream, certObject, *certObjectLength );
572  status = writeCertCollection( &stream, certInfoPtr,
573  certFormatType );
574  sMemDisconnect( &stream );
575  return( status );
576  }
577 
578  /* Determine how big the output object will be */
579  length = encodedLength = certInfoPtr->certificateSize;
580  /* Placed here to get rid of not-inited warnings */
581  if( baseFormatType == CRYPT_CERTFORMAT_CERTCHAIN )
582  {
583  STREAM nullStream;
584 
585  ENSURES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
586  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
587 
588  sMemNullOpen( &nullStream );
589  status = writeCertChain( &nullStream, certInfoPtr );
590  if( cryptStatusOK( status ) )
591  length = encodedLength = stell( &nullStream );
592  sMemClose( &nullStream );
593  if( cryptStatusError( status ) )
594  return( status );
595  }
596  if( baseFormatType != certFormatType )
597  {
598  status = base64encodeLen( length, &encodedLength,
599  certInfoPtr->type );
600  if( cryptStatusError( status ) )
601  return( status );
602  }
603 
604  /* Set up the length information */
605  *certObjectLength = encodedLength;
606  if( certObject == NULL )
607  return( CRYPT_OK );
608  if( encodedLength > certObjectMaxLength )
609  return( CRYPT_ERROR_OVERFLOW );
610  if( !isWritePtr( certObject, encodedLength ) )
611  return( CRYPT_ARGERROR_STR1 );
612 
613  /* If it's a simple format, write either the DER-encoded object data or
614  its base64 / S/MIME-encoded form directly to the output */
615  if( certFormatType == CRYPT_CERTFORMAT_CERTIFICATE || \
616  certFormatType == CRYPT_ICERTFORMAT_DATA )
617  {
618  memcpy( certObject, certInfoPtr->certificate, length );
619  ENSURES( !cryptStatusError( checkObjectEncoding( certObject, \
620  length ) ) );
621 
622  return( CRYPT_OK );
623  }
624  if( certFormatType == CRYPT_CERTFORMAT_TEXT_CERTIFICATE || \
625  certFormatType == CRYPT_CERTFORMAT_XML_CERTIFICATE )
626  {
627  return( base64encode( certObject, certObjectMaxLength,
628  certObjectLength, certInfoPtr->certificate,
629  certInfoPtr->certificateSize,
630  certInfoPtr->type ) );
631  }
632 
633  /* It's a straight certificate chain, write it directly to the output */
634  if( certFormatType == CRYPT_CERTFORMAT_CERTCHAIN )
635  {
636  sMemOpen( &stream, certObject, length );
637  status = writeCertChain( &stream, certInfoPtr );
638  sMemDisconnect( &stream );
639  ENSURES( cryptStatusError( status ) || \
640  !cryptStatusError( checkObjectEncoding( certObject, \
641  length ) ) );
642  return( status );
643  }
644 
645  /* It's a base64 / S/MIME-encoded certificate chain, write it to a
646  temporary buffer and then encode it to the output */
647  ENSURES( certFormatType == CRYPT_CERTFORMAT_TEXT_CERTCHAIN || \
648  certFormatType == CRYPT_CERTFORMAT_XML_CERTCHAIN );
649  if( ( buffer = clAlloc( "exportCert", length ) ) == NULL )
650  return( CRYPT_ERROR_MEMORY );
651  sMemOpen( &stream, buffer, length );
652  status = writeCertChain( &stream, certInfoPtr );
653  if( cryptStatusOK( status ) )
654  {
655  status = base64encode( certObject, certObjectMaxLength,
656  certObjectLength, buffer, length,
658  }
659  sMemClose( &stream );
660  clFree( "exportCert", buffer );
661 
662  return( status );
663  }
664 #endif /* USE_CERTIFICATES */