Better reset the PIN verification stati after changing the key attributes.
[gnupg.git] / sm / certdump.c
blobd3390702df45e0a694532034f778ae4eb35bed05
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_string (const char *string)
154 if (!string)
155 log_printf ("[error]");
156 else
158 const unsigned char *s;
160 for (s=(const unsigned char*)string; *s; s++)
162 if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
163 break;
165 if (!*s && *string != '[')
166 log_printf ("%s", string);
167 else
169 log_printf ( "[ ");
170 log_printhex (NULL, string, strlen (string));
171 log_printf ( " ]");
177 /* This simple dump function is mainly used for debugging purposes. */
178 void
179 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
181 ksba_sexp_t sexp;
182 char *p;
183 char *dn;
184 ksba_isotime_t t;
186 log_debug ("BEGIN Certificate `%s':\n", text? text:"");
187 if (cert)
189 sexp = ksba_cert_get_serial (cert);
190 log_debug (" serial: ");
191 gpgsm_dump_serial (sexp);
192 ksba_free (sexp);
193 log_printf ("\n");
195 ksba_cert_get_validity (cert, 0, t);
196 log_debug (" notBefore: ");
197 dump_isotime (t);
198 log_printf ("\n");
199 ksba_cert_get_validity (cert, 1, t);
200 log_debug (" notAfter: ");
201 dump_isotime (t);
202 log_printf ("\n");
204 dn = ksba_cert_get_issuer (cert, 0);
205 log_debug (" issuer: ");
206 gpgsm_dump_string (dn);
207 ksba_free (dn);
208 log_printf ("\n");
210 dn = ksba_cert_get_subject (cert, 0);
211 log_debug (" subject: ");
212 gpgsm_dump_string (dn);
213 ksba_free (dn);
214 log_printf ("\n");
216 log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert));
218 p = gpgsm_get_fingerprint_string (cert, 0);
219 log_debug (" SHA1 Fingerprint: %s\n", p);
220 xfree (p);
222 log_debug ("END Certificate\n");
226 /* Return a new string holding the format serial number and issuer
227 ("#SN/issuer"). No filtering on invalid characters is done.
228 Caller must release the string. On memory failure NULL is
229 returned. */
230 char *
231 gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
233 char *p, *p1;
235 if (sn && issuer)
237 p1 = gpgsm_format_serial (sn);
238 if (!p1)
239 p = xtrystrdup ("[invalid SN]");
240 else
242 p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
243 if (p)
245 *p = '#';
246 strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
248 xfree (p1);
251 else
252 p = xtrystrdup ("[invalid SN/issuer]");
253 return p;
257 /* Log the certificate's name in "#SN/ISSUERDN" format along with
258 TEXT. */
259 void
260 gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
262 log_info ("%s", text? text:"certificate" );
263 if (cert)
265 ksba_sexp_t sn;
266 char *p;
268 p = ksba_cert_get_issuer (cert, 0);
269 sn = ksba_cert_get_serial (cert);
270 if (p && sn)
272 log_printf (" #");
273 gpgsm_dump_serial (sn);
274 log_printf ("/");
275 gpgsm_dump_string (p);
277 else
278 log_printf (" [invalid]");
279 ksba_free (sn);
280 xfree (p);
282 log_printf ("\n");
290 /* helper for the rfc2253 string parser */
291 static const unsigned char *
292 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
294 static struct {
295 const char *label;
296 const char *oid;
297 } label_map[] = {
298 /* Warning: When adding new labels, make sure that the buffer
299 below we be allocated large enough. */
300 {"EMail", "1.2.840.113549.1.9.1" },
301 {"T", "2.5.4.12" },
302 {"GN", "2.5.4.42" },
303 {"SN", "2.5.4.4" },
304 {"NameDistinguisher", "0.2.262.1.10.7.20"},
305 {"ADDR", "2.5.4.16" },
306 {"BC", "2.5.4.15" },
307 {"D", "2.5.4.13" },
308 {"PostalCode", "2.5.4.17" },
309 {"Pseudo", "2.5.4.65" },
310 {"SerialNumber", "2.5.4.5" },
311 {NULL, NULL}
313 const unsigned char *s, *s1;
314 size_t n;
315 char *p;
316 int i;
318 /* Parse attributeType */
319 for (s = string+1; *s && *s != '='; s++)
321 if (!*s)
322 return NULL; /* error */
323 n = s - string;
324 if (!n)
325 return NULL; /* empty key */
327 /* We need to allocate a few bytes more due to the possible mapping
328 from the shorter OID to the longer label. */
329 array->key = p = xtrymalloc (n+10);
330 if (!array->key)
331 return NULL;
332 memcpy (p, string, n);
333 p[n] = 0;
334 trim_trailing_spaces (p);
336 if (digitp (p))
338 for (i=0; label_map[i].label; i++ )
339 if ( !strcmp (p, label_map[i].oid) )
341 strcpy (p, label_map[i].label);
342 break;
345 string = s + 1;
347 if (*string == '#')
348 { /* hexstring */
349 string++;
350 for (s=string; hexdigitp (s); s++)
351 s++;
352 n = s - string;
353 if (!n || (n & 1))
354 return NULL; /* Empty or odd number of digits. */
355 n /= 2;
356 array->value = p = xtrymalloc (n+1);
357 if (!p)
358 return NULL;
359 for (s1=string; n; s1 += 2, n--, p++)
361 *(unsigned char *)p = xtoi_2 (s1);
362 if (!*p)
363 *p = 0x01; /* Better print a wrong value than truncating
364 the string. */
366 *p = 0;
368 else
369 { /* regular v3 quoted string */
370 for (n=0, s=string; *s; s++)
372 if (*s == '\\')
373 { /* pair */
374 s++;
375 if (*s == ',' || *s == '=' || *s == '+'
376 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
377 || *s == '\\' || *s == '\"' || *s == ' ')
378 n++;
379 else if (hexdigitp (s) && hexdigitp (s+1))
381 s++;
382 n++;
384 else
385 return NULL; /* invalid escape sequence */
387 else if (*s == '\"')
388 return NULL; /* invalid encoding */
389 else if (*s == ',' || *s == '=' || *s == '+'
390 || *s == '<' || *s == '>' || *s == ';' )
391 break;
392 else
393 n++;
396 array->value = p = xtrymalloc (n+1);
397 if (!p)
398 return NULL;
399 for (s=string; n; s++, n--)
401 if (*s == '\\')
403 s++;
404 if (hexdigitp (s))
406 *(unsigned char *)p++ = xtoi_2 (s);
407 s++;
409 else
410 *p++ = *s;
412 else
413 *p++ = *s;
415 *p = 0;
417 return s;
421 /* Parse a DN and return an array-ized one. This is not a validating
422 parser and it does not support any old-stylish syntax; KSBA is
423 expected to return only rfc2253 compatible strings. */
424 static struct dn_array_s *
425 parse_dn (const unsigned char *string)
427 struct dn_array_s *array;
428 size_t arrayidx, arraysize;
429 int i;
431 arraysize = 7; /* C,ST,L,O,OU,CN,email */
432 arrayidx = 0;
433 array = xtrymalloc ((arraysize+1) * sizeof *array);
434 if (!array)
435 return NULL;
436 while (*string)
438 while (*string == ' ')
439 string++;
440 if (!*string)
441 break; /* ready */
442 if (arrayidx >= arraysize)
444 struct dn_array_s *a2;
446 arraysize += 5;
447 a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
448 if (!a2)
449 goto failure;
450 array = a2;
452 array[arrayidx].key = NULL;
453 array[arrayidx].value = NULL;
454 string = parse_dn_part (array+arrayidx, string);
455 if (!string)
456 goto failure;
457 while (*string == ' ')
458 string++;
459 array[arrayidx].multivalued = (*string == '+');
460 array[arrayidx].done = 0;
461 arrayidx++;
462 if (*string && *string != ',' && *string != ';' && *string != '+')
463 goto failure; /* invalid delimiter */
464 if (*string)
465 string++;
467 array[arrayidx].key = NULL;
468 array[arrayidx].value = NULL;
469 return array;
471 failure:
472 for (i=0; i < arrayidx; i++)
474 xfree (array[i].key);
475 xfree (array[i].value);
477 xfree (array);
478 return NULL;
482 /* Print a DN part to STREAM or if STREAM is NULL to FP. */
483 static void
484 print_dn_part (FILE *fp, estream_t stream,
485 struct dn_array_s *dn, const char *key, int translate)
487 struct dn_array_s *first_dn = dn;
489 for (; dn->key; dn++)
491 if (!dn->done && !strcmp (dn->key, key))
493 /* Forward to the last multi-valued RDN, so that we can
494 print them all in reverse in the correct order. Note
495 that this overrides the the standard sequence but that
496 seems to a reasonable thing to do with multi-valued
497 RDNs. */
498 while (dn->multivalued && dn[1].key)
499 dn++;
500 next:
501 if (!dn->done && dn->value && *dn->value)
503 if (stream)
505 es_fprintf (stream, "/%s=", dn->key);
506 if (translate)
507 es_write_sanitized_utf8_buffer (stream, dn->value,
508 strlen (dn->value),
509 "/", NULL);
510 else
511 es_write_sanitized (stream, dn->value, strlen (dn->value),
512 "/", NULL);
514 else
516 fprintf (fp, "/%s=", dn->key);
517 if (translate)
518 print_sanitized_utf8_string (fp, dn->value, '/');
519 else
520 print_sanitized_string (fp, dn->value, '/');
523 dn->done = 1;
524 if (dn > first_dn && dn[-1].multivalued)
526 dn--;
527 goto next;
533 /* Print all parts of a DN in a "standard" sequence. We first print
534 all the known parts, followed by the uncommon ones */
535 static void
536 print_dn_parts (FILE *fp, estream_t stream,
537 struct dn_array_s *dn, int translate)
539 const char *stdpart[] = {
540 "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL
542 int i;
544 for (i=0; stdpart[i]; i++)
545 print_dn_part (fp, stream, dn, stdpart[i], translate);
547 /* Now print the rest without any specific ordering */
548 for (; dn->key; dn++)
549 print_dn_part (fp, stream, dn, dn->key, translate);
553 /* Print the S-Expression in BUF, which has a valid length of BUFLEN,
554 as a human readable string in one line to FP. */
555 static void
556 pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
558 size_t len;
559 gcry_sexp_t sexp;
560 char *result, *p;
562 if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
564 fputs (_("[Error - invalid encoding]"), fp);
565 return;
567 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
568 assert (len);
569 result = xtrymalloc (len);
570 if (!result)
572 fputs (_("[Error - out of core]"), fp);
573 gcry_sexp_release (sexp);
574 return;
576 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
577 assert (len);
578 for (p = result; len; len--, p++)
580 if (*p == '\n')
582 if (len > 1) /* Avoid printing the trailing LF. */
583 fputs ("\\n", fp);
585 else if (*p == '\r')
586 fputs ("\\r", fp);
587 else if (*p == '\v')
588 fputs ("\\v", fp);
589 else if (*p == '\t')
590 fputs ("\\t", fp);
591 else
592 putc (*p, fp);
594 xfree (result);
595 gcry_sexp_release (sexp);
598 /* Print the S-Expression in BUF to extended STREAM, which has a valid
599 length of BUFLEN, as a human readable string in one line to FP. */
600 static void
601 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
603 size_t len;
604 gcry_sexp_t sexp;
605 char *result, *p;
607 if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
609 es_fputs (_("[Error - invalid encoding]"), fp);
610 return;
612 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
613 assert (len);
614 result = xtrymalloc (len);
615 if (!result)
617 es_fputs (_("[Error - out of core]"), fp);
618 gcry_sexp_release (sexp);
619 return;
621 len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
622 assert (len);
623 for (p = result; len; len--, p++)
625 if (*p == '\n')
627 if (len > 1) /* Avoid printing the trailing LF. */
628 es_fputs ("\\n", fp);
630 else if (*p == '\r')
631 es_fputs ("\\r", fp);
632 else if (*p == '\v')
633 es_fputs ("\\v", fp);
634 else if (*p == '\t')
635 es_fputs ("\\t", fp);
636 else
637 es_putc (*p, fp);
639 xfree (result);
640 gcry_sexp_release (sexp);
646 void
647 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
649 const unsigned char *s = (const unsigned char *)name;
650 int i;
652 if (!s)
654 fputs (_("[Error - No name]"), fp);
656 else if (*s == '<')
658 const char *s2 = strchr ( (char*)s+1, '>');
659 if (s2)
661 if (translate)
662 print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
663 else
664 print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
667 else if (*s == '(')
669 pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
671 else if (!((*s >= '0' && *s < '9')
672 || (*s >= 'A' && *s <= 'Z')
673 || (*s >= 'a' && *s <= 'z')))
674 fputs (_("[Error - invalid encoding]"), fp);
675 else
677 struct dn_array_s *dn = parse_dn (s);
678 if (!dn)
679 fputs (_("[Error - invalid DN]"), fp);
680 else
682 print_dn_parts (fp, NULL, dn, translate);
683 for (i=0; dn[i].key; i++)
685 xfree (dn[i].key);
686 xfree (dn[i].value);
688 xfree (dn);
694 void
695 gpgsm_print_name (FILE *fp, const char *name)
697 gpgsm_print_name2 (fp, name, 1);
701 /* This is a variant of gpgsm_print_name sending it output to an estream. */
702 void
703 gpgsm_es_print_name2 (estream_t fp, const char *name, int translate)
705 const unsigned char *s = (const unsigned char *)name;
706 int i;
708 if (!s)
710 es_fputs (_("[Error - No name]"), fp);
712 else if (*s == '<')
714 const char *s2 = strchr ( (char*)s+1, '>');
716 if (s2)
718 if (translate)
719 es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
720 NULL, NULL);
721 else
722 es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL);
725 else if (*s == '(')
727 pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
729 else if (!((*s >= '0' && *s < '9')
730 || (*s >= 'A' && *s <= 'Z')
731 || (*s >= 'a' && *s <= 'z')))
732 es_fputs (_("[Error - invalid encoding]"), fp);
733 else
735 struct dn_array_s *dn = parse_dn (s);
737 if (!dn)
738 es_fputs (_("[Error - invalid DN]"), fp);
739 else
741 print_dn_parts (NULL, fp, dn, translate);
742 for (i=0; dn[i].key; i++)
744 xfree (dn[i].key);
745 xfree (dn[i].value);
747 xfree (dn);
753 void
754 gpgsm_es_print_name (estream_t fp, const char *name)
756 gpgsm_es_print_name2 (fp, name, 1);
760 /* A cookie structure used for the memory stream. */
761 struct format_name_cookie
763 char *buffer; /* Malloced buffer with the data to deliver. */
764 size_t size; /* Allocated size of this buffer. */
765 size_t len; /* strlen (buffer). */
766 int error; /* system error code if any. */
769 /* The writer function for the memory stream. */
770 static ssize_t
771 format_name_writer (void *cookie, const void *buffer, size_t size)
773 struct format_name_cookie *c = cookie;
774 char *p;
776 if (!c->buffer)
778 p = xtrymalloc (size + 1 + 1);
779 if (p)
781 c->size = size + 1;
782 c->buffer = p;
783 c->len = 0;
786 else if (c->len + size < c->len)
788 p = NULL;
789 errno = ENOMEM;
791 else if (c->size < c->len + size)
793 p = xtryrealloc (c->buffer, c->len + size + 1);
794 if (p)
796 c->size = c->len + size;
797 c->buffer = p;
800 else
801 p = c->buffer;
802 if (!p)
804 c->error = errno;
805 xfree (c->buffer);
806 c->buffer = NULL;
807 errno = c->error;
808 return -1;
810 memcpy (p + c->len, buffer, size);
811 c->len += size;
812 p[c->len] = 0; /* Terminate string. */
814 return (ssize_t)size;
818 /* Format NAME which is expected to be in rfc2253 format into a better
819 human readable format. Caller must free the returned string. NULL
820 is returned in case of an error. With TRANSLATE set to true the
821 name will be translated to the native encoding. Note that NAME is
822 internally always UTF-8 encoded. */
823 char *
824 gpgsm_format_name2 (const char *name, int translate)
826 estream_t fp;
827 struct format_name_cookie cookie;
828 es_cookie_io_functions_t io = { NULL };
830 memset (&cookie, 0, sizeof cookie);
832 io.func_write = format_name_writer;
833 fp = es_fopencookie (&cookie, "w", io);
834 if (!fp)
836 int save_errno = errno;
837 log_error ("error creating memory stream: %s\n", strerror (errno));
838 errno = save_errno;
839 return NULL;
841 gpgsm_es_print_name2 (fp, name, translate);
842 es_fclose (fp);
843 if (cookie.error || !cookie.buffer)
845 xfree (cookie.buffer);
846 errno = cookie.error;
847 return NULL;
849 return cookie.buffer;
853 char *
854 gpgsm_format_name (const char *name)
856 return gpgsm_format_name2 (name, 1);
860 /* Return fingerprint and a percent escaped name in a human readable
861 format suitable for status messages like GOODSIG. May return NULL
862 on error (out of core). */
863 char *
864 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
866 char *fpr, *name, *p;
867 char *buffer;
869 fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
870 if (!fpr)
871 return NULL;
873 name = ksba_cert_get_subject (cert, 0);
874 if (!name)
876 xfree (fpr);
877 return NULL;
880 p = gpgsm_format_name2 (name, 0);
881 ksba_free (name);
882 name = p;
883 if (!name)
885 xfree (fpr);
886 return NULL;
889 buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
890 if (buffer)
892 const char *s;
894 p = stpcpy (stpcpy (buffer, fpr), " ");
895 for (s = name; *s; s++)
897 if (*s < ' ')
899 sprintf (p, "%%%02X", *(const unsigned char*)s);
900 p += 3;
902 else
903 *p++ = *s;
905 *p = 0;
907 xfree (fpr);
908 xfree (name);
909 return buffer;
913 /* Create a key description for the CERT, this may be passed to the
914 pinentry. The caller must free the returned string. NULL may be
915 returned on error. */
916 char *
917 gpgsm_format_keydesc (ksba_cert_t cert)
919 char *name, *subject, *buffer;
920 ksba_isotime_t t;
921 char created[20];
922 char expires[20];
923 char *sn;
924 ksba_sexp_t sexp;
925 char *orig_codeset;
927 name = ksba_cert_get_subject (cert, 0);
928 subject = name? gpgsm_format_name2 (name, 0) : NULL;
929 ksba_free (name); name = NULL;
931 sexp = ksba_cert_get_serial (cert);
932 sn = sexp? gpgsm_format_serial (sexp) : NULL;
933 ksba_free (sexp);
935 ksba_cert_get_validity (cert, 0, t);
936 if (*t)
937 sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
938 else
939 *created = 0;
940 ksba_cert_get_validity (cert, 1, t);
941 if (*t)
942 sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
943 else
944 *expires = 0;
946 orig_codeset = i18n_switchto_utf8 ();
948 name = xtryasprintf (_("Please enter the passphrase to unlock the"
949 " secret key for the X.509 certificate:\n"
950 "\"%s\"\n"
951 "S/N %s, ID 0x%08lX,\n"
952 "created %s, expires %s.\n" ),
953 subject? subject:"?",
954 sn? sn: "?",
955 gpgsm_get_short_fingerprint (cert, NULL),
956 created, expires);
958 i18n_switchback (orig_codeset);
960 if (!name)
962 xfree (subject);
963 xfree (sn);
964 return NULL;
967 xfree (subject);
968 xfree (sn);
970 buffer = percent_plus_escape (name);
971 xfree (name);
972 return buffer;