2 * Copyright (C) 2010 Stefan Walter
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 #include "gcr-certificate.h"
23 #include "gcr-certificate-exporter.h"
24 #include "gcr-certificate-renderer.h"
25 #include "gcr-display-view.h"
26 #include "gcr-icons.h"
27 #include "gcr-simple-certificate.h"
28 #include "gcr-renderer.h"
30 #include "egg/egg-asn1x.h"
31 #include "egg/egg-asn1-defs.h"
32 #include "egg/egg-dn.h"
33 #include "egg/egg-oid.h"
34 #include "egg/egg-hex.h"
39 #include <glib/gi18n-lib.h>
48 struct _GcrCertificateRendererPrivate {
49 GcrCertificate *opt_cert;
50 GckAttributes *opt_attrs;
55 static void gcr_renderer_iface_init (GcrRendererIface *iface);
56 static void gcr_renderer_certificate_iface_init (GcrCertificateIface *iface);
58 G_DEFINE_TYPE_WITH_CODE (GcrCertificateRenderer, gcr_certificate_renderer, G_TYPE_OBJECT,
59 G_IMPLEMENT_INTERFACE (GCR_TYPE_RENDERER, gcr_renderer_iface_init);
60 GCR_CERTIFICATE_MIXIN_IMPLEMENT_COMPARABLE ();
61 G_IMPLEMENT_INTERFACE (GCR_TYPE_CERTIFICATE, gcr_renderer_certificate_iface_init);
64 /* -----------------------------------------------------------------------------
69 calculate_label (GcrCertificateRenderer *self)
74 return g_strdup (self->pv->label);
76 if (self->pv->opt_attrs) {
77 if (gck_attributes_find_string (self->pv->opt_attrs, CKA_LABEL, &label))
81 label = gcr_certificate_get_subject_cn (GCR_CERTIFICATE (self));
85 return g_strdup (_("Certificate"));
89 append_extension (GcrCertificateRenderer *self, GcrDisplayView *view,
90 GNode *asn, const guchar *data, gsize n_data, gint index)
92 GcrRenderer *renderer = GCR_RENDERER (self);
101 /* Make sure it is present */
102 node = egg_asn1x_node (asn, "tbsCertificate", "extensions", index, NULL);
106 /* Dig out the OID */
107 oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "extnID", NULL));
108 g_return_val_if_fail (oid, FALSE);
110 _gcr_display_view_append_heading (view, renderer, _("Extension"));
114 text = egg_oid_get_description (oid);
115 _gcr_display_view_append_value (view, renderer, _("Identifier"), text, FALSE);
118 /* Extension value */
119 value = egg_asn1x_get_raw_value (egg_asn1x_node (node, "extnValue", NULL), &n_value);
121 /* TODO: Parsing of extensions that we understand */
122 display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
123 _gcr_display_view_append_value (view, renderer, _("Value"), display, TRUE);
128 if (egg_asn1x_get_boolean (egg_asn1x_node (node, "critical", NULL), &critical))
129 _gcr_display_view_append_value (view, renderer, _("Critical"), critical ? _("Yes") : _("No"), FALSE);
134 typedef struct _on_parsed_dn_args {
135 GcrCertificateRenderer *renderer;
136 GcrDisplayView *view;
140 on_parsed_dn_part (guint index, GQuark oid, const guchar *value,
141 gsize n_value, gpointer user_data)
143 GcrCertificateRenderer *self = ((on_parsed_dn_args*)user_data)->renderer;
144 GcrDisplayView *view = ((on_parsed_dn_args*)user_data)->view;
150 g_return_if_fail (GCR_IS_CERTIFICATE_RENDERER (self));
152 attr = egg_oid_get_name (oid);
153 desc = egg_oid_get_description (oid);
155 /* Combine them into something sane */
157 if (strcmp (attr, desc) == 0)
158 field = g_strdup (attr);
160 field = g_strdup_printf ("%s (%s)", attr, desc);
161 } else if (!attr && !desc) {
162 field = g_strdup ("");
164 field = g_strdup (attr);
166 field = g_strdup (desc);
168 g_assert_not_reached ();
171 display = egg_dn_print_value (oid, value, n_value);
173 display = g_strdup ("");
175 _gcr_display_view_append_value (view, GCR_RENDERER (self), field, display, FALSE);
181 on_delete_unref_dialog (GtkWidget *widget, GdkEvent *event, gpointer data)
183 g_object_unref (widget);
188 on_export_completed (GObject *source, GAsyncResult *result, gpointer user_data)
190 GtkWindow *parent = GTK_WINDOW (user_data);
191 GcrCertificateExporter *exporter = GCR_CERTIFICATE_EXPORTER (source);
192 GError *error = NULL;
195 if (!_gcr_certificate_exporter_export_finish (exporter, result, &error)) {
196 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
197 dialog = gtk_message_dialog_new_with_markup (parent,
198 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
199 GTK_BUTTONS_OK, "<big>%s</big>\n\n%s",
200 _("Couldn't export the certificate."),
202 gtk_widget_show (dialog);
203 g_signal_connect (dialog, "delete-event",
204 G_CALLBACK (on_delete_unref_dialog), NULL);
208 /* Matches ref in on_certificate_export */
210 g_object_unref (parent);
214 on_certificate_export (GtkMenuItem *menuitem, gpointer user_data)
216 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (user_data);
217 GcrCertificateExporter *exporter;
221 label = calculate_label (self);
223 parent = gtk_widget_get_toplevel (GTK_WIDGET (menuitem));
224 if (parent && !GTK_IS_WINDOW (parent))
227 exporter = _gcr_certificate_exporter_new (GCR_CERTIFICATE (self), label,
228 GTK_WINDOW (parent));
232 _gcr_certificate_exporter_export_async (exporter, NULL, on_export_completed,
233 parent ? g_object_ref (parent) : NULL);
236 /* -----------------------------------------------------------------------------
241 gcr_certificate_renderer_init (GcrCertificateRenderer *self)
243 self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_RENDERER, GcrCertificateRendererPrivate));
247 gcr_certificate_renderer_dispose (GObject *obj)
249 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj);
251 if (self->pv->opt_cert)
252 g_object_unref (self->pv->opt_cert);
253 self->pv->opt_cert = NULL;
255 G_OBJECT_CLASS (gcr_certificate_renderer_parent_class)->dispose (obj);
259 gcr_certificate_renderer_finalize (GObject *obj)
261 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj);
263 g_assert (!self->pv->opt_cert);
265 if (self->pv->opt_attrs)
266 gck_attributes_unref (self->pv->opt_attrs);
267 self->pv->opt_attrs = NULL;
269 g_free (self->pv->label);
270 self->pv->label = NULL;
272 G_OBJECT_CLASS (gcr_certificate_renderer_parent_class)->finalize (obj);
276 gcr_certificate_renderer_set_property (GObject *obj, guint prop_id, const GValue *value,
279 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj);
282 case PROP_CERTIFICATE:
283 gcr_certificate_renderer_set_certificate (self, g_value_get_object (value));
286 g_free (self->pv->label);
287 self->pv->label = g_value_dup_string (value);
288 g_object_notify (obj, "label");
289 gcr_renderer_emit_data_changed (GCR_RENDERER (self));
291 case PROP_ATTRIBUTES:
292 gcr_certificate_renderer_set_attributes (self, g_value_get_boxed (value));
295 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
301 gcr_certificate_renderer_get_property (GObject *obj, guint prop_id, GValue *value,
304 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj);
307 case PROP_CERTIFICATE:
308 g_value_set_object (value, self->pv->opt_cert);
311 g_value_take_string (value, calculate_label (self));
313 case PROP_ATTRIBUTES:
314 g_value_set_boxed (value, self->pv->opt_attrs);
317 gcr_certificate_mixin_get_property (obj, prop_id, value, pspec);
323 gcr_certificate_renderer_class_init (GcrCertificateRendererClass *klass)
325 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
326 GckAttributes *registered;
328 gcr_certificate_renderer_parent_class = g_type_class_peek_parent (klass);
329 g_type_class_add_private (klass, sizeof (GcrCertificateRendererPrivate));
331 gobject_class->dispose = gcr_certificate_renderer_dispose;
332 gobject_class->finalize = gcr_certificate_renderer_finalize;
333 gobject_class->set_property = gcr_certificate_renderer_set_property;
334 gobject_class->get_property = gcr_certificate_renderer_get_property;
336 g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
337 g_param_spec_object ("certificate", "Certificate", "Certificate to display.",
338 GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE));
340 g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
341 g_param_spec_boxed ("attributes", "Attributes", "Certificate pkcs11 attributes",
342 GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE));
344 g_object_class_install_property (gobject_class, PROP_LABEL,
345 g_param_spec_string ("label", "Label", "Certificate Label",
346 "", G_PARAM_READWRITE));
348 _gcr_icons_register ();
349 gcr_certificate_mixin_class_init (gobject_class);
351 /* Register this as a renderer which can be loaded */
352 registered = gck_attributes_new ();
353 gck_attributes_add_ulong (registered, CKA_CLASS, CKO_CERTIFICATE);
354 gcr_renderer_register (GCR_TYPE_CERTIFICATE_RENDERER, registered);
355 gck_attributes_unref (registered);
359 gcr_certificate_renderer_render (GcrRenderer *renderer, GcrViewer *viewer)
361 GcrCertificateRenderer *self;
362 gconstpointer data, value;
363 gsize n_data, n_value, n_raw;
364 GcrDisplayView *view;
365 on_parsed_dn_args args;
367 GcrCertificate *cert;
377 self = GCR_CERTIFICATE_RENDERER (renderer);
379 if (GCR_IS_DISPLAY_VIEW (viewer)) {
380 view = GCR_DISPLAY_VIEW (viewer);
383 g_warning ("GcrCertificateRenderer only works with internal specific "
384 "GcrViewer returned by gcr_viewer_new().");
388 _gcr_display_view_clear (view, renderer);
389 cert = GCR_CERTIFICATE (self);
391 data = gcr_certificate_get_der_data (cert, &n_data);
395 icon = gcr_certificate_get_icon (cert);
396 _gcr_display_view_set_icon (view, GCR_RENDERER (self), icon);
397 g_object_unref (icon);
399 asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", data, n_data);
400 g_return_if_fail (asn);
402 display = calculate_label (self);
403 _gcr_display_view_append_title (view, renderer, display);
406 display = egg_dn_read_part (egg_asn1x_node (asn, "tbsCertificate", "subject", "rdnSequence", NULL), "CN");
407 _gcr_display_view_append_content (view, renderer, _("Identity"), display);
410 display = egg_dn_read_part (egg_asn1x_node (asn, "tbsCertificate", "issuer", "rdnSequence", NULL), "CN");
411 _gcr_display_view_append_content (view, renderer, _("Verified by"), display);
414 if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notAfter", NULL), &date)) {
415 display = g_malloc0 (128);
416 if (!g_date_strftime (display, 128, "%x", &date))
417 g_return_if_reached ();
418 _gcr_display_view_append_content (view, renderer, _("Expires"), display);
422 _gcr_display_view_start_details (view, renderer);
424 args.renderer = self;
428 _gcr_display_view_append_heading (view, renderer, _("Subject Name"));
429 egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "subject", "rdnSequence", NULL), on_parsed_dn_part, &args);
432 _gcr_display_view_append_heading (view, renderer, _("Issuer Name"));
433 egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "issuer", "rdnSequence", NULL), on_parsed_dn_part, &args);
435 /* The Issued Parameters */
436 _gcr_display_view_append_heading (view, renderer, _("Issued Certificate"));
438 if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "tbsCertificate", "version", NULL), &version))
439 g_return_if_reached ();
440 display = g_strdup_printf ("%lu", version + 1);
441 _gcr_display_view_append_value (view, renderer, _("Version"), display, FALSE);
444 raw = egg_asn1x_get_integer_as_raw (egg_asn1x_node (asn, "tbsCertificate", "serialNumber", NULL), NULL, &n_raw);
445 g_return_if_fail (raw);
446 display = egg_hex_encode_full (raw, n_raw, TRUE, ' ', 1);
447 _gcr_display_view_append_value (view, renderer, _("Serial Number"), display, TRUE);
451 display = g_malloc0 (128);
452 if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notBefore", NULL), &date)) {
453 if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
454 g_return_if_reached ();
455 _gcr_display_view_append_value (view, renderer, _("Not Valid Before"), display, FALSE);
457 if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notAfter", NULL), &date)) {
458 if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
459 g_return_if_reached ();
460 _gcr_display_view_append_value (view, renderer, _("Not Valid After"), display, FALSE);
465 _gcr_display_view_append_heading (view, renderer, _("Signature"));
467 oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "signatureAlgorithm", "algorithm", NULL));
468 text = egg_oid_get_description (oid);
469 _gcr_display_view_append_value (view, renderer, _("Signature Algorithm"), text, FALSE);
471 value = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "signatureAlgorithm", "parameters", NULL), &n_value);
472 if (value && n_value) {
473 display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
474 _gcr_display_view_append_value (view, renderer, _("Signature Parameters"), display, TRUE);
478 raw = egg_asn1x_get_bits_as_raw (egg_asn1x_node (asn, "signature", NULL), NULL, &bits);
479 g_return_if_fail (raw);
480 display = egg_hex_encode_full (raw, bits / 8, TRUE, ' ', 1);
481 _gcr_display_view_append_value (view, renderer, _("Signature"), display, TRUE);
485 /* Public Key Info */
486 _gcr_display_view_append_heading (view, renderer, _("Public Key Info"));
488 oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo",
489 "algorithm", "algorithm", NULL));
490 text = egg_oid_get_description (oid);
491 _gcr_display_view_append_value (view, renderer, _("Key Algorithm"), text, FALSE);
493 value = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo",
494 "algorithm", "parameters", NULL), &n_value);
495 if (value && n_value) {
496 display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
497 _gcr_display_view_append_value (view, renderer, _("Key Parameters"), display, TRUE);
501 bits = gcr_certificate_get_key_size (cert);
503 display = g_strdup_printf ("%u", bits);
504 _gcr_display_view_append_value (view, renderer, _("Key Size"), display, FALSE);
508 raw = egg_asn1x_get_bits_as_raw (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo",
509 "subjectPublicKey", NULL), NULL, &bits);
510 g_return_if_fail (raw);
511 display = egg_hex_encode_full (raw, bits / 8, TRUE, ' ', 1);
512 _gcr_display_view_append_value (view, renderer, _("Public Key"), display, TRUE);
517 _gcr_display_view_append_heading (view, renderer, _("Fingerprints"));
519 _gcr_display_view_append_fingerprint (view, renderer, data, n_data, "SHA1", G_CHECKSUM_SHA1);
520 _gcr_display_view_append_fingerprint (view, renderer, data, n_data, "MD5", G_CHECKSUM_MD5);
523 for (index = 1; TRUE; ++index) {
524 if (!append_extension (self, view, asn, data, n_data, index))
528 egg_asn1x_destroy (asn);
532 gcr_certificate_renderer_populate_popup (GcrRenderer *self, GcrViewer *viewer,
537 item = gtk_separator_menu_item_new ();
538 gtk_widget_show (item);
539 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
541 item = gtk_menu_item_new_with_label ("Export Certificate...");
542 gtk_widget_show (item);
543 g_signal_connect_data (item, "activate", G_CALLBACK (on_certificate_export),
544 g_object_ref (self), (GClosureNotify)g_object_unref, 0);
545 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
549 gcr_renderer_iface_init (GcrRendererIface *iface)
551 iface->populate_popup = gcr_certificate_renderer_populate_popup;
552 iface->render_view = gcr_certificate_renderer_render;
556 gcr_certificate_renderer_get_der_data (GcrCertificate *cert, gsize *n_data)
558 GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (cert);
563 if (self->pv->opt_cert)
564 return gcr_certificate_get_der_data (self->pv->opt_cert, n_data);
566 if (self->pv->opt_attrs) {
567 attr = gck_attributes_find (self->pv->opt_attrs, CKA_VALUE);
568 g_return_val_if_fail (attr, NULL);
569 *n_data = attr->length;
577 gcr_renderer_certificate_iface_init (GcrCertificateIface *iface)
579 iface->get_der_data = gcr_certificate_renderer_get_der_data;
582 /* -----------------------------------------------------------------------------
586 GcrCertificateRenderer*
587 gcr_certificate_renderer_new (GcrCertificate *certificate)
589 return g_object_new (GCR_TYPE_CERTIFICATE_RENDERER, "certificate", certificate, NULL);
592 GcrCertificateRenderer*
593 gcr_certificate_renderer_new_for_attributes (const gchar *label, struct _GckAttributes *attrs)
595 return g_object_new (GCR_TYPE_CERTIFICATE_RENDERER, "label", label, "attributes", attrs, NULL);
599 gcr_certificate_renderer_get_certificate (GcrCertificateRenderer *self)
601 g_return_val_if_fail (GCR_IS_CERTIFICATE_RENDERER (self), NULL);
602 if (self->pv->opt_cert)
603 return self->pv->opt_cert;
604 return GCR_CERTIFICATE (self);
608 gcr_certificate_renderer_set_certificate (GcrCertificateRenderer *self, GcrCertificate *cert)
610 g_return_if_fail (GCR_IS_CERTIFICATE_RENDERER (self));
612 if (self->pv->opt_cert)
613 g_object_unref (self->pv->opt_cert);
614 self->pv->opt_cert = cert;
615 if (self->pv->opt_cert)
616 g_object_ref (self->pv->opt_cert);
618 if (self->pv->opt_attrs) {
619 gck_attributes_unref (self->pv->opt_attrs);
620 self->pv->opt_attrs = NULL;
623 gcr_renderer_emit_data_changed (GCR_RENDERER (self));
624 g_object_notify (G_OBJECT (self), "certificate");
628 gcr_certificate_renderer_get_attributes (GcrCertificateRenderer *self)
630 g_return_val_if_fail (GCR_IS_CERTIFICATE_RENDERER (self), NULL);
631 return self->pv->opt_attrs;
635 gcr_certificate_renderer_set_attributes (GcrCertificateRenderer *self, GckAttributes *attrs)
637 g_return_if_fail (GCR_IS_CERTIFICATE_RENDERER (self));
639 gck_attributes_unref (self->pv->opt_attrs);
640 self->pv->opt_attrs = attrs;
642 if (self->pv->opt_attrs)
643 gck_attributes_ref (self->pv->opt_attrs);
645 if (self->pv->opt_cert) {
646 g_object_unref (self->pv->opt_cert);
647 g_object_notify (G_OBJECT (self), "certificate");
648 self->pv->opt_cert = NULL;
651 gcr_renderer_emit_data_changed (GCR_RENDERER (self));
652 g_object_notify (G_OBJECT (self), "attributes");