Add a custom prompt for the CSR generation.
[gnupg.git] / sm / certdump.c
blob71907d1884474751c78acac79ea50bc771d21c56
1 /* certdump.c - Dump a certificate for debugging
2 * Copyright (C) 2001, 2004, 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <assert.h>
28 #ifdef HAVE_LOCALE_H
29 #include <locale.h>
30 #endif
31 #ifdef HAVE_LANGINFO_CODESET
32 #include <langinfo.h>
33 #endif
35 #include "gpgsm.h"
36 #include <gcrypt.h>
37 #include <ksba.h>
39 #include "keydb.h"
40 #include "i18n.h"
43 struct dn_array_s {
44 char *key;
45 char *value;
46 int multivalued;
47 int done;
51 /* Print the first element of an S-Expression. */
52 void
53 gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
55 const char *p = (const char *)sn;
56 unsigned long n;
57 char *endp;
59 if (!p)
60 es_fputs (_("none"), fp);
61 else if (*p != '(')
62 es_fputs ("[Internal error - not an S-expression]", fp);
63 else
65 p++;
66 n = strtoul (p, &endp, 10);
67 p = endp;
68 if (*p++ != ':')
69 es_fputs ("[Internal Error - invalid S-expression]", fp);
70 else
71 es_write_hexstring (fp, p, n, 0, NULL);
76 /* Dump the serial number or any other simple S-expression. */
77 void
78 gpgsm_dump_serial (ksba_const_sexp_t sn)
80 const char *p = (const char *)sn;
81 unsigned long n;
82 char *endp;
84 if (!p)
85 log_printf ("none");
86 else if (*p != '(')
87 log_printf ("ERROR - not an S-expression");
88 else
90 p++;
91 n = strtoul (p, &endp, 10);
92 p = endp;
93 if (*p!=':')
94 log_printf ("ERROR - invalid S-expression");
95 else
97 for (p++; n; n--, p++)
98 log_printf ("%02X", *(const unsigned char *)p);
104 char *
105 gpgsm_format_serial (ksba_const_sexp_t sn)
107 const char *p = (const char *)sn;
108 unsigned long n;
109 char *endp;
110 char *buffer;
111 int i;
113 if (!p)
114 return NULL;
116 if (*p != '(')
117 BUG (); /* Not a valid S-expression. */
119 p++;
120 n = strtoul (p, &endp, 10);
121 p = endp;
122 if (*p!=':')
123 BUG (); /* Not a valid S-expression. */
124 p++;
126 buffer = xtrymalloc (n*2+1);
127 if (buffer)
129 for (i=0; n; n--, p++, i+=2)
130 sprintf (buffer+i, "%02X", *(unsigned char *)p);
131 buffer[i] = 0;
133 return buffer;
139 void
140 gpgsm_print_time (estream_t fp, ksba_isotime_t t)
142 if (!t || !*t)
143 es_fputs (_("none"), fp);
144 else
145 es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
146 t, t+4, t+6, t+9, t+11, t+13);
150 void
151 gpgsm_dump_time (ksba_isotime_t t)
153 if (!t || !*t)
154 log_printf (_("[none]"));
155 else
156 log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
157 t, t+4, t+6, t+9, t+11, t+13);
163 void
164 gpgsm_dump_string (const char *string)
167 if (!string)
168 log_printf ("[error]");
169 else
171 const unsigned char *s;
173 for (s=(const unsigned char*)string; *s; s++)
175 if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
176 break;
178 if (!*s && *string != '[')
179 log_printf ("%s", string);
180 else
182 log_printf ( "[ ");
183 log_printhex (NULL, string, strlen (string));
184 log_printf ( " ]");
190 /* This simple dump function is mainly used for debugging purposes. */
191 void
192 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
194 ksba_sexp_t sexp;
195 char *p;
196 char *dn;
197 ksba_isotime_t t;
199 log_debug ("BEGIN Certificate `%s':\n", text? text:"");
200 if (cert)
202 sexp = ksba_cert_get_serial (cert);
203 log_debug (" serial: ");
204 gpgsm_dump_serial (sexp);
205 ksba_free (sexp);
206 log_printf ("\n");
208 ksba_cert_get_validity (cert, 0, t);
209 log_debug (" notBefore: ");
210 gpgsm_dump_time (t);
211 log_printf ("\n");
212 ksba_cert_get_validity (cert, 1, t);
213 log_debug (" notAfter: ");
214 gpgsm_dump_time (t);
215 log_printf ("\n");
217 dn = ksba_cert_get_issuer (cert, 0);
218 log_debug (" issuer: ");
219 gpgsm_dump_string (dn);
220 ksba_free (dn);
221 log_printf ("\n");
223 dn = ksba_cert_get_subject (cert, 0);
224 log_debug (" subject: ");
225 gpgsm_dump_string (dn);
226 ksba_free (dn);
227 log_printf ("\n");
229 log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
231 p = gpgsm_get_fingerprint_string (cert, 0);
232 log_debug (" SHA1 Fingerprint: %s\n", p);
233 xfree (p);
235 log_debug ("END Certificate\n");
239 /* Return a new string holding the format serial number and issuer
240 ("#SN/issuer"). No filtering on invalid characters is done.
241 Caller must release the string. On memory failure NULL is
242 returned. */
243 char *
244 gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
246 char *p, *p1;
248 if (sn && issuer)
250 p1 = gpgsm_format_serial (sn);
251 if (!p1)
252 p = xtrystrdup ("[invalid SN]");
253 else
255 p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
256 if (p)
258 *p = '#';
259 strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
261 xfree (p1);
264 else
265 p = xtrystrdup ("[invalid SN/issuer]");
266 return p;
270 /* Log the certificate's name in "#SN/ISSUERDN" format along with
271 TEXT. */
272 void
273 gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
275 log_info ("%s", text? text:"certificate" );
276 if (cert)
278 ksba_sexp_t sn;
279 char *p;
281 p = ksba_cert_get_issuer (cert, 0);
282 sn = ksba_cert_get_serial (cert);
283 if (p && sn)
285 log_printf (" #");
286 gpgsm_dump_serial (sn);
287 log_printf ("/");
288 gpgsm_dump_string (p);
290 else
291 log_printf (" [invalid]");
292 ksba_free (sn);
293 xfree (p);
295 log_printf ("\n");
303 /* helper for the rfc2253 string parser */
304 static const unsigned char *
305 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
307 static struct {
308 const char *label;
309 const char *oid;
310 } label_map[] = {
311 /* Warning: When adding new labels, make sure that the buffer
312 below we be allocated large enough. */
313 {"EMail", "1.2.840.113549.1.9.1" },
314 {"T", "2.5.4.12" },
315 {"GN", "2.5.4.42" },
316 {"SN", "2.5.4.4" },
317 {"NameDistinguisher", "0.2.262.1.10.7.20"},
318 {"ADDR", "2.5.4.16" },
319 {"BC", "2.5.4.15" },
320 {"D", "2.5.4.13" },
321 {"PostalCode", "2.5.4.17" },
322 {"Pseudo", "2.5.4.65" },
323 {"SerialNumber", "2.5.4.5" },
324 {NULL, NULL}
326 const unsigned char *s, *s1;
327 size_t n;
328 char *p;
329 int i;
331 /* Parse attributeType */
332 for (s = string+1; *s && *s != '='; s++)
334 if (!*s)
335 return NULL; /* error */
336 n = s - string;
337 if (!n)
338 return NULL; /* empty key */
340 /* We need to allocate a few bytes more due to the possible mapping
341 from the shorter OID to the longer label. */
342 array->key = p = xtrymalloc (n+10);
343 if (!array->key)
344 return NULL;
345 memcpy (p, string, n);
346 p[n] = 0;
347 trim_trailing_spaces (p);
349 if (digitp (p))
351 for (i=0; label_map[i].label; i++ )
352 if ( !strcmp (p, label_map[i].oid) )
354 strcpy (p, label_map[i].label);
355 break;
358 string = s + 1;
360 if (*string == '#')
361 { /* hexstring */
362 string++;
363 for (s=string; hexdigitp (s); s++)
364 s++;
365 n = s - string;
366 if (!n || (n & 1))
367 return NULL; /* Empty or odd number of digits. */
368 n /= 2;
369 array->value = p = xtrymalloc (n+1);
370 if (!p)
371 return NULL;
372 for (s1=string; n; s1 += 2, n--, p++)
374 *(unsigned char *)p = xtoi_2 (s1);
375 if (!*p)
376 *p = 0x01; /* Better print a wrong value than truncating
377 the string. */
379 *p = 0;
381 else
382 { /* regular v3 quoted string */
383 for (n=0, s=string; *s; s++)
385 if (*s == '\\')
386 { /* pair */
387 s++;
388 if (*s == ',' || *s == '=' || *s == '+'
389 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
390 || *s == '\\' || *s == '\"' || *s == ' ')
391 n++;
392 else if (hexdigitp (s) && hexdigitp (s+1))
394 s++;
395 n++;
397 else
398 return NULL; /* invalid escape sequence */
400 else if (*s == '\"')
401 return NULL; /* invalid encoding */
402 else if (*s == ',' || *s == '=' || *s == '+'
403 || *s == '<' || *s == '>' || *s == ';' )
404 break;
405 else
406 n++;
409 array->value = p = xtrymalloc (n+1);
410 if (!p)
411 return NULL;
412 for (s=string; n; s++, n--)
414 if (*s == '\\')
416 s++;
417 if (hexdigitp (s))
419 *(unsigned char *)p++ = xtoi_2 (s);
420 s++;
422 else
423 *p++ = *s;
425 else
426 *p++ = *s;
428 *p = 0;
430 return s;
434 /* Parse a DN and return an array-ized one. This is not a validating
435 parser and it does not support any old-stylish syntax; KSBA is
436 expected to return only rfc2253 compatible strings. */
437 static struct dn_array_s *
438 parse_dn (const unsigned char *string)
440 struct dn_array_s *array;
441 size_t arrayidx, arraysize;
442 int i;
444 arraysize = 7; /* C,ST,L,O,OU,CN,email */
445 arrayidx = 0;
446 array = xtrymalloc ((arraysize+1) * sizeof *array);
447 if (!array)
448 return NULL;
449 while (*string)
451 while (*string == ' ')
452 string++;
453 if (!*string)
454 break; /* ready */
455 if (arrayidx >= arraysize)
457 struct dn_array_s *a2;
459 arraysize += 5;
460 a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
461 if (!a2)
462 goto failure;
463 array = a2;
465 array[arrayidx].key = NULL;
466 array[arrayidx].value = NULL;
467 string = parse_dn_part (array+arrayidx, string);
468 if (!string)
469 goto failure;
470 while (*string == ' ')
471 string++;
472 array[arrayidx].multivalued = (*string == '+');
473 array[arrayidx].done = 0;
474 arrayidx++;
475 if (*string && *string != ',' && *string != ';' && *string != '+')
476 goto failure; /* invalid delimiter */
477 if (*string)
478 string++;
480 array[arrayidx].key = NULL;
481 array[arrayidx].value = NULL;
482 return array;
484 failure:
485 for (i=0; i < arrayidx; i++)
487 xfree (array[i].key);
488 xfree (array[i].value);
490 xfree (array);
491 return NULL;
495 /* Print a DN part to STREAM or if STREAM is NULL to FP. */
496 static void
497 print_dn_part (FILE *fp, estream_t stream,
498 struct dn_array_s *dn, const char *key, int translate)
500 struct dn_array_s *first_dn = dn;
502 for (; dn->key; dn++)
504 if (!dn->done && !strcmp (dn->key, key))
506 /* Forward to the last multi-valued RDN, so that we can
507 print them all in reverse in the correct order. Note
508 that this overrides the the standard sequence but that
509 seems to a reasonable thing to do with multi-valued
510 RDNs. */
511 while (dn->multivalued && dn[1].key)
512 dn++;
513 next:
514 if (!dn->done && dn->value && *dn->value)
516 if (stream)
518 es_fprintf (stream, "/%s=", dn->key);
519 if (translate)
520 es_write_sanitized_utf8_buffer (stream, dn->value,
521 strlen (dn->value),
522 "/", NULL);
523 else
524 es_write_sanitized (stream, dn->value, strlen (dn->value),
525 "/", NULL);
527 else
529 fprintf (fp, "/%s=", dn->key);
530 if (translate)
531 print_sanitized_utf8_string (fp, dn->value, '/');
532 else
533 print_sanitized_string (fp, dn->value, '/');
536 dn->done = 1;
537 if (dn > first_dn && dn[-1].multivalued)
539 dn--;
540 goto next;
546 /* Print all parts of a DN in a "standard" sequence. We first print
547 all the known parts, followed by the uncommon ones */
548 static void
549 print_dn_parts (FILE *fp, estream_t stream,
550 struct dn_array_s *dn, int translate)
552 const char *stdpart[] = {
553 "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
555 int i;
557 for (i=0; stdpart[i]; i++)
558 print_dn_part (fp, stream, dn, stdpart[i], translate);
560 /* Now print the rest without any specific ordering */
561 for (; dn->key; dn++)
562 print_dn_part (fp, stream, dn, dn->key, translate);
566 /* Print the S-Expression in BUF, which has a valid length of BUFLEN,
567 as a human readable string in one line to FP. */
568 static void
569 pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
571 size_t len;
572 gcry_sexp_t sexp;
573 char *result, *p;
575 if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
577 fputs (_("[Error - invalid encoding]"), fp);
578 return;
580 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
581 assert (len);
582 result = xtrymalloc (len);
583 if (!result)
585 fputs (_("[Error - out of core]"), fp);
586 gcry_sexp_release (sexp);
587 return;
589 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
590 assert (len);
591 for (p = result; len; len--, p++)
593 if (*p == '\n')
595 if (len > 1) /* Avoid printing the trailing LF. */
596 fputs ("\\n", fp);
598 else if (*p == '\r')
599 fputs ("\\r", fp);
600 else if (*p == '\v')
601 fputs ("\\v", fp);
602 else if (*p == '\t')
603 fputs ("\\t", fp);
604 else
605 putc (*p, fp);
607 xfree (result);
608 gcry_sexp_release (sexp);
611 /* Print the S-Expression in BUF to extended STREAM, which has a valid
612 length of BUFLEN, as a human readable string in one line to FP. */
613 static void
614 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
616 size_t len;
617 gcry_sexp_t sexp;
618 char *result, *p;
620 if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
622 es_fputs (_("[Error - invalid encoding]"), fp);
623 return;
625 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
626 assert (len);
627 result = xtrymalloc (len);
628 if (!result)
630 es_fputs (_("[Error - out of core]"), fp);
631 gcry_sexp_release (sexp);
632 return;
634 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
635 assert (len);
636 for (p = result; len; len--, p++)
638 if (*p == '\n')
640 if (len > 1) /* Avoid printing the trailing LF. */
641 es_fputs ("\\n", fp);
643 else if (*p == '\r')
644 es_fputs ("\\r", fp);
645 else if (*p == '\v')
646 es_fputs ("\\v", fp);
647 else if (*p == '\t')
648 es_fputs ("\\t", fp);
649 else
650 es_putc (*p, fp);
652 xfree (result);
653 gcry_sexp_release (sexp);
659 void
660 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
662 const unsigned char *s = (const unsigned char *)name;
663 int i;
665 if (!s)
667 fputs (_("[Error - No name]"), fp);
669 else if (*s == '<')
671 const char *s2 = strchr ( (char*)s+1, '>');
672 if (s2)
674 if (translate)
675 print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
676 else
677 print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
680 else if (*s == '(')
682 pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
684 else if (!((*s >= '0' && *s < '9')
685 || (*s >= 'A' && *s <= 'Z')
686 || (*s >= 'a' && *s <= 'z')))
687 fputs (_("[Error - invalid encoding]"), fp);
688 else
690 struct dn_array_s *dn = parse_dn (s);
691 if (!dn)
692 fputs (_("[Error - invalid DN]"), fp);
693 else
695 print_dn_parts (fp, NULL, dn, translate);
696 for (i=0; dn[i].key; i++)
698 xfree (dn[i].key);
699 xfree (dn[i].value);
701 xfree (dn);
707 void
708 gpgsm_print_name (FILE *fp, const char *name)
710 gpgsm_print_name2 (fp, name, 1);
714 /* This is a variant of gpgsm_print_name sending it output to an estream. */
715 void
716 gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
718 const unsigned char *s = (const unsigned char *)name;
719 int i;
721 if (!s)
723 es_fputs (_("[Error - No name]"), fp);
725 else if (*s == '<')
727 const char *s2 = strchr ( (char*)s+1, '>');
729 if (s2)
731 if (translate)
732 es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
733 NULL, NULL);
734 else
735 es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
738 else if (*s == '(')
740 pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
742 else if (!((*s >= '0' && *s < '9')
743 || (*s >= 'A' && *s <= 'Z')
744 || (*s >= 'a' && *s <= 'z')))
745 es_fputs (_("[Error - invalid encoding]"), fp);
746 else
748 struct dn_array_s *dn = parse_dn (s);
750 if (!dn)
751 es_fputs (_("[Error - invalid DN]"), fp);
752 else
754 print_dn_parts (NULL, fp, dn, translate);
755 for (i=0; dn[i].key; i++)
757 xfree (dn[i].key);
758 xfree (dn[i].value);
760 xfree (dn);
766 void
767 gpgsm_es_print_name (estream_t fp, const char *name)
769 gpgsm_es_print_name2 (fp, name, 1);
773 /* A cookie structure used for the memory stream. */
774 struct format_name_cookie
776 char *buffer; /* Malloced buffer with the data to deliver. */
777 size_t size; /* Allocated size of this buffer. */
778 size_t len; /* strlen (buffer). */
779 int error; /* system error code if any. */
782 /* The writer function for the memory stream. */
783 static ssize_t
784 format_name_writer (void *cookie, const void *buffer, size_t size)
786 struct format_name_cookie *c = cookie;
787 char *p;
789 if (!c->buffer)
791 p = xtrymalloc (size + 1 + 1);
792 if (p)
794 c->size = size + 1;
795 c->buffer = p;
796 c->len = 0;
799 else if (c->len + size < c->len)
801 p = NULL;
802 errno = ENOMEM;
804 else if (c->size < c->len + size)
806 p = xtryrealloc (c->buffer, c->len + size + 1);
807 if (p)
809 c->size = c->len + size;
810 c->buffer = p;
813 else
814 p = c->buffer;
815 if (!p)
817 c->error = errno;
818 xfree (c->buffer);
819 c->buffer = NULL;
820 errno = c->error;
821 return -1;
823 memcpy (p + c->len, buffer, size);
824 c->len += size;
825 p[c->len] = 0; /* Terminate string. */
827 return (ssize_t)size;
831 /* Format NAME which is expected to be in rfc2253 format into a better
832 human readable format. Caller must free the returned string. NULL
833 is returned in case of an error. With TRANSLATE set to true the
834 name will be translated to the native encoding. Note that NAME is
835 internally always UTF-8 encoded. */
836 char *
837 gpgsm_format_name2 (const char *name, int translate)
839 estream_t fp;
840 struct format_name_cookie cookie;
841 es_cookie_io_functions_t io = { NULL };
843 memset (&cookie, 0, sizeof cookie);
845 io.func_write = format_name_writer;
846 fp = es_fopencookie (&cookie, "w", io);
847 if (!fp)
849 int save_errno = errno;
850 log_error ("error creating memory stream: %s\n", strerror (errno));
851 errno = save_errno;
852 return NULL;
854 gpgsm_es_print_name2 (fp, name, translate);
855 es_fclose (fp);
856 if (cookie.error || !cookie.buffer)
858 xfree (cookie.buffer);
859 errno = cookie.error;
860 return NULL;
862 return cookie.buffer;
866 char *
867 gpgsm_format_name (const char *name)
869 return gpgsm_format_name2 (name, 1);
873 /* Return fingerprint and a percent escaped name in a human readable
874 format suitable for status messages like GOODSIG. May return NULL
875 on error (out of core). */
876 char *
877 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
879 char *fpr, *name, *p;
880 char *buffer;
882 fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
883 if (!fpr)
884 return NULL;
886 name = ksba_cert_get_subject (cert, 0);
887 if (!name)
889 xfree (fpr);
890 return NULL;
893 p = gpgsm_format_name2 (name, 0);
894 ksba_free (name);
895 name = p;
896 if (!name)
898 xfree (fpr);
899 return NULL;
902 buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
903 if (buffer)
905 const char *s;
907 p = stpcpy (stpcpy (buffer, fpr), " ");
908 for (s = name; *s; s++)
910 if (*s < ' ')
912 sprintf (p, "%%%02X", *(const unsigned char*)s);
913 p += 3;
915 else
916 *p++ = *s;
918 *p = 0;
920 xfree (fpr);
921 xfree (name);
922 return buffer;
926 /* Create a key description for the CERT, this may be passed to the
927 pinentry. The caller must free the returned string. NULL may be
928 returned on error. */
929 char *
930 gpgsm_format_keydesc (ksba_cert_t cert)
932 char *name, *subject, *buffer;
933 ksba_isotime_t t;
934 char created[20];
935 char expires[20];
936 char *sn;
937 ksba_sexp_t sexp;
938 char *orig_codeset;
940 name = ksba_cert_get_subject (cert, 0);
941 subject = name? gpgsm_format_name2 (name, 0) : NULL;
942 ksba_free (name); name = NULL;
944 sexp = ksba_cert_get_serial (cert);
945 sn = sexp? gpgsm_format_serial (sexp) : NULL;
946 ksba_free (sexp);
948 ksba_cert_get_validity (cert, 0, t);
949 if (*t)
950 sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
951 else
952 *created = 0;
953 ksba_cert_get_validity (cert, 1, t);
954 if (*t)
955 sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
956 else
957 *expires = 0;
959 orig_codeset = i18n_switchto_utf8 ();
961 name = xtryasprintf (_("Please enter the passphrase to unlock the"
962 " secret key for the X.509 certificate:\n"
963 "\"%s\"\n"
964 "S/N %s, ID 0x%08lX,\n"
965 "created %s, expires %s.\n" ),
966 subject? subject:"?",
967 sn? sn: "?",
968 gpgsm_get_short_fingerprint (cert),
969 created, expires);
971 i18n_switchback (orig_codeset);
973 if (!name)
975 xfree (subject);
976 xfree (sn);
977 return NULL;
980 xfree (subject);
981 xfree (sn);
983 buffer = percent_plus_escape (name);
984 xfree (name);
985 return buffer;