From 999ef98d7640005b86cd958ed062ee1213008c49 Mon Sep 17 00:00:00 2001 From: Chris Toshok Date: Fri, 5 Dec 2003 01:56:08 +0000 Subject: add prototype for e_cert_get_asn1_struct. 2003-12-04 Chris Toshok * lib/e-cert.h: add prototype for e_cert_get_asn1_struct. * lib/e-cert.c (e_cert_dispose): unref the asn1 object. (get_int_value): copy and massage from mozilla source. (process_version): same. (process_serial_number_der): same. (get_default_oid_format): same. (get_oid_text): same. (process_raw_bytes): same. (process_sec_algorithm_id): same. (process_subject_public_key_info): same. (process_ns_cert_type_extensions): same. (process_key_usage_extensions): same. (process_extension_data): same. (process_single_extension): same. (process_extensions): same. (process_name): same. (create_tbs_certificate_asn1_struct): same. (create_asn1_struct): same. (e_cert_get_asn1_struct): new function. * lib/e-asn1-object.c (e_asn1_object_dispose): free the display name, value, and children. (e_asn1_object_init): assume it's a valid container unless we hear otherwise. (e_asn1_object_new_from_cert): nuke. (e_asn1_object_set_valid_container): implement. (e_asn1_object_append_child): same. (e_asn1_object_set_display_name): same. (e_asn1_object_set_display_value): same. * lib/e-asn1-object.h: add prototypes for e_asn1_object_set_valid_container, e_asn1_object_set_display_name, e_asn1_object_set_display_value, and e_asn1_object_append_child. * gui/certificate-viewer.c (populate_fields_tree): populate the tree from the asn structure. (hierarchy_selection_changed): blow away the old fields_tree content and populate it again. (fields_selection_changed): implement, set the text view's contents to the asn1 object's display_value. (fill_in_details): expand all nodes in the hierarchy tree. svn path=/trunk/; revision=23640 --- smime/lib/e-cert.c | 741 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 741 insertions(+) (limited to 'smime/lib/e-cert.c') diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c index 2389cc29c8..173f445c22 100644 --- a/smime/lib/e-cert.c +++ b/smime/lib/e-cert.c @@ -90,6 +90,8 @@ struct _ECertPrivate { char *sha1_fingerprint; char *md5_fingerprint; + EASN1Object *asn1; + gboolean delete; }; @@ -130,6 +132,9 @@ e_cert_dispose (GObject *object) if (ec->priv->md5_fingerprint) PORT_Free (ec->priv->md5_fingerprint); + if (ec->priv->asn1) + g_object_unref (ec->priv->asn1); + if (ec->priv->delete) { printf ("attempting to delete cert marked for deletion\n"); if (e_cert_get_cert_type (ec) == E_CERT_USER) { @@ -434,6 +439,742 @@ e_cert_get_chain (ECert *ecert) return l; } +static gboolean +get_int_value (SECItem *versionItem, + unsigned long *version) +{ + SECStatus srv; + srv = SEC_ASN1DecodeInteger(versionItem,version); + if (srv != SECSuccess) { + g_warning ("could not decode version of cert"); + return FALSE; + } + return TRUE; +} + +static gboolean +process_version (SECItem *versionItem, + EASN1Object **retItem) +{ + EASN1Object *item = e_asn1_object_new (); + unsigned long version; + + e_asn1_object_set_display_name (item, _("Version")); + + /* Now to figure out what version this certificate is. */ + + if (versionItem->data) { + if (!get_int_value (versionItem, &version)) + return FALSE; + } else { + /* If there is no version present in the cert, then rfc2459 + says we default to v1 (0) */ + version = 0; + } + + switch (version){ + case 0: + e_asn1_object_set_display_value (item, _("Version 1")); + break; + case 1: + e_asn1_object_set_display_value (item, _("Version 2")); + break; + case 2: + e_asn1_object_set_display_value (item, _("Version 3")); + break; + default: + g_warning ("Bad value for cert version"); + return FALSE; + } + + *retItem = item; + return TRUE; +} + +static gboolean +process_serial_number_der (SECItem *serialItem, + EASN1Object **retItem) +{ + char *serialNumber; + EASN1Object *item = e_asn1_object_new (); + + e_asn1_object_set_display_name (item, _("Serial Number")); + + serialNumber = CERT_Hexify(serialItem, 1); + + e_asn1_object_set_display_value (item, serialNumber); + PORT_Free (serialNumber); /* XXX the right free to use? */ + + *retItem = item; + return TRUE; +} + +static gboolean +get_default_oid_format (SECItem *oid, + char **text) +{ + char buf[300]; + unsigned int len; + int written; + + unsigned long val = oid->data[0]; + unsigned int i = val % 40; + val /= 40; + written = PR_snprintf(buf, 300, "%lu %u ", val, i); + if (written < 0) + return FALSE; + len = written; + + val = 0; + for (i = 1; i < oid->len; ++i) { + /* In this loop, we have to parse a DER formatted + If the first bit is a 1, then the integer is + represented by more than one byte. If the + first bit is set then we continue on and add + the values of the later bytes until we get + a byte without the first bit set. + */ + unsigned long j; + + j = oid->data[i]; + val = (val << 7) | (j & 0x7f); + if (j & 0x80) + continue; + written = PR_snprintf(&buf[len], sizeof(buf)-len, "%lu ", val); + if (written < 0) + return FALSE; + + len += written; + if (len >= sizeof (buf)) + g_warning ("OID data to big to display in 300 chars."); + val = 0; + } + + *text = g_strdup (buf); + return TRUE; +} + +static gboolean +get_oid_text (SECItem *oid, char **text) +{ + SECOidTag oidTag = SECOID_FindOIDTag(oid); + char *temp; + + switch (oidTag) { + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD2 With RSA Encryption")); + break; + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD5 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-1 With RSA Encryption")); + break; + case SEC_OID_AVA_COUNTRY_NAME: + *text = g_strdup (_("C")); + break; + case SEC_OID_AVA_COMMON_NAME: + *text = g_strdup (_("CN")); + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + *text = g_strdup (_("OU")); + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + *text = g_strdup (_("O")); + break; + case SEC_OID_AVA_LOCALITY: + *text = g_strdup (_("L")); + break; + case SEC_OID_AVA_DN_QUALIFIER: + *text = g_strdup (_("DN")); + break; + case SEC_OID_AVA_DC: + *text = g_strdup (_("DC")); + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + *text = g_strdup (_("ST")); + break; + case SEC_OID_PKCS1_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 RSA Encryption")); + break; + case SEC_OID_X509_KEY_USAGE: + *text = g_strdup (_("Certificate Key Usage")); + break; + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + *text = g_strdup (_("Netscape Certificate Type")); + break; + case SEC_OID_X509_AUTH_KEY_ID: + *text = g_strdup (_("Certificate Authority Key Identifier")); + break; + case SEC_OID_RFC1274_UID: + *text = g_strdup (_("UID")); + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + *text = g_strdup (_("E")); + break; + default: + if (!get_default_oid_format (oid, &temp)) + return FALSE; + + *text = g_strdup_printf (_("Object Identifier (%s)"), temp); + g_free (temp); + + break; + } + return TRUE; +} + + +static gboolean +process_raw_bytes (SECItem *data, char **text) +{ + /* This function is used to display some DER bytes + that we have not added support for decoding. + It prints the value of the byte out into a + string that can later be displayed as a byte + string. We place a new line after 24 bytes + to break up extermaly long sequence of bytes. + */ + GString *str = g_string_new (""); + PRUint32 i; + char buffer[5]; + for (i=0; ilen; i++) { + PR_snprintf(buffer, 5, "%02x ", data->data[i]); + g_string_append (str, buffer); + if ((i+1)%16 == 0) { + g_string_append (str, "\n"); + } + } + *text = g_string_free (str, FALSE); + return TRUE; +} + +static gboolean +process_sec_algorithm_id (SECAlgorithmID *algID, + EASN1Object **retSequence) +{ + EASN1Object *sequence = e_asn1_object_new (); + char *text; + + *retSequence = NULL; + + get_oid_text (&algID->algorithm, &text); + + if (!algID->parameters.len || algID->parameters.data[0] == E_ASN1_OBJECT_TYPE_NULL) { + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_set_valid_container (sequence, FALSE); + } else { + EASN1Object *subitem; + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Identifier")); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + g_free (text); + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Parameters")); + process_raw_bytes (&algID->parameters, &text); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + } + + g_free (text); + *retSequence = sequence; + return TRUE; +} + +static gboolean +process_subject_public_key_info (CERTSubjectPublicKeyInfo *spki, + EASN1Object *parentSequence) +{ + EASN1Object *spkiSequence = e_asn1_object_new(); + EASN1Object *sequenceItem; + EASN1Object *printableItem; + char *text; + + e_asn1_object_set_display_name (spkiSequence, _("Subject Public Key Info")); + + if (!process_sec_algorithm_id (&spki->algorithm, &sequenceItem)) + return FALSE; + + e_asn1_object_set_display_name (sequenceItem, _("Subject Public Key Algorithm")); + + e_asn1_object_append_child (spkiSequence, sequenceItem); + + /* The subjectPublicKey field is encoded as a bit string. + ProcessRawBytes expects the lenght to be in bytes, so + let's convert the lenght into a temporary SECItem. + */ + SECItem data; + data.data = spki->subjectPublicKey.data; + data.len = spki->subjectPublicKey.len / 8; + + process_raw_bytes (&data, &text); + printableItem = e_asn1_object_new (); + + e_asn1_object_set_display_value (printableItem, text); + e_asn1_object_set_display_name (printableItem, _("Subject's Public Key")); + e_asn1_object_append_child (spkiSequence, printableItem); + g_object_unref (printableItem); + + e_asn1_object_append_child (parentSequence, spkiSequence); + g_object_unref (spkiSequence); + + return TRUE; +} + +static gboolean +process_ns_cert_type_extensions (SECItem *extData, + GString *text) +{ + SECItem decoded; + unsigned char nsCertType; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded, + SEC_ASN1_GET(SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + nsCertType = decoded.data[0]; + + PORT_Free (decoded.data); /* XXX right free? */ + + if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) { + g_string_append (text, _("SSL Client Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_SERVER) { + g_string_append (text, _("SSL Server Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL) { + g_string_append (text, _("Email")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_CA) { + g_string_append (text, _("SSL Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL_CA) { + g_string_append (text, _("Email Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + return TRUE; +} + +static gboolean +process_key_usage_extensions (SECItem *extData, GString *text) +{ + SECItem decoded; + unsigned char keyUsage; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded, + SEC_ASN1_GET(SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + keyUsage = decoded.data[0]; + PORT_Free (decoded.data); /* XXX right free? */ + + if (keyUsage & KU_DIGITAL_SIGNATURE) { + g_string_append (text, _("Signing")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_NON_REPUDIATION) { + g_string_append (text, _("Non-repudiation")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_ENCIPHERMENT) { + g_string_append (text, _("Key Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_DATA_ENCIPHERMENT) { + g_string_append (text, _("Data Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_AGREEMENT) { + g_string_append (text, _("Key Agreement")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_CERT_SIGN) { + g_string_append (text, _("Certificate Signer")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_CRL_SIGN) { + g_string_append (text, _("CRL Signer")); + g_string_append (text, "\n"); + } + + return TRUE; +} + +static gboolean +process_extension_data (SECOidTag oidTag, SECItem *extData, + GString *str) +{ + gboolean rv; + switch (oidTag) { + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + rv = process_ns_cert_type_extensions (extData, str); + break; + case SEC_OID_X509_KEY_USAGE: + rv = process_key_usage_extensions (extData, str); + break; + default: { + char *text; + rv = process_raw_bytes (extData, &text); + g_string_append (str, text); + g_free (text); + break; + } + } + return rv; +} + +static gboolean +process_single_extension (CERTCertExtension *extension, + EASN1Object **retExtension) +{ + GString *str = g_string_new (""); + char *text; + EASN1Object *extensionItem; + SECOidTag oidTag = SECOID_FindOIDTag(&extension->id); + + get_oid_text (&extension->id, &text); + + extensionItem = e_asn1_object_new (); + + e_asn1_object_set_display_name (extensionItem, text); + g_free (text); + + if (extension->critical.data != NULL) { + if (extension->critical.data[0]) { + g_string_append (str, _("Critical")); + } else { + g_string_append (str, _("Not Critical")); + } + } else { + g_string_append (str, _("Not Critical")); + } + g_string_append (str, "\n"); + if (!process_extension_data (oidTag, &extension->value, str)) { + g_string_free (str, TRUE); + return FALSE; + } + + e_asn1_object_set_display_value (extensionItem, str->str); + g_string_free (str, TRUE); + *retExtension = extensionItem; + return TRUE; +} + +static gboolean +process_extensions (CERTCertExtension **extensions, + EASN1Object *parentSequence) +{ + EASN1Object *extensionSequence = e_asn1_object_new (); + PRInt32 i; + + e_asn1_object_set_display_name (extensionSequence, _("Extensions")); + + for (i=0; extensions[i] != NULL; i++) { + EASN1Object *newExtension; + + if (!process_single_extension (extensions[i], + &newExtension)) + return FALSE; + + e_asn1_object_append_child (extensionSequence, newExtension); + } + e_asn1_object_append_child (parentSequence, extensionSequence); + return TRUE; +} + +static gboolean +process_name (CERTName *name, char **value) +{ + CERTRDN** rdns; + CERTRDN** rdn; + CERTAVA** avas; + CERTAVA* ava; + SECItem *decodeItem = NULL; + GString *final_string = g_string_new (""); + + char *type; + GString *avavalue; + char *temp; + CERTRDN **lastRdn; + + rdns = name->rdns; + + lastRdn = rdns; + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) lastRdn++; + + /* The above whille loop will put us at the last member + * of the array which is a NULL pointer. So let's back + * up one spot so that we have the last non-NULL entry in + * the array in preparation for traversing the + * RDN's (Relative Distinguished Name) in reverse order. + */ + lastRdn--; + + /* + * Loop over name contents in _reverse_ RDN order appending to string + * When building the Ascii string, NSS loops over these entries in + * reverse order, so I will as well. The difference is that NSS + * will always place them in a one line string separated by commas, + * where I want each entry on a single line. I can't just use a comma + * as my delimitter because it is a valid character to have in the + * value portion of the AVA and could cause trouble when parsing. + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + avas = (*rdn)->avas; + while ((ava = *avas++) != 0) { + if (!get_oid_text (&ava->type, &type)) + return FALSE; + + /* This function returns a string in UTF8 format. */ + decodeItem = CERT_DecodeAVAValue(&ava->value); + if(!decodeItem) { + return FALSE; + } + + avavalue = g_string_new_len ((char*)decodeItem->data, decodeItem->len); + + SECITEM_FreeItem(decodeItem, PR_TRUE); + + temp = g_strdup_printf (_("%s = %s"), type, avavalue->str); + + g_string_append (final_string, temp); + g_string_append (final_string, "\n"); + g_string_free (avavalue, TRUE); + g_free (temp); + } + } + *value = g_string_free (final_string, FALSE); + return TRUE; +} + +static gboolean +create_tbs_certificate_asn1_struct (ECert *cert, EASN1Object **seq) +{ + /* + ** TBSCertificate ::= SEQUENCE { + ** version [0] EXPLICIT Version DEFAULT v1, + ** serialNumber CertificateSerialNumber, + ** signature AlgorithmIdentifier, + ** issuer Name, + ** validity Validity, + ** subject Name, + ** subjectPublicKeyInfo SubjectPublicKeyInfo, + ** issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** extensions [3] EXPLICIT Extensions OPTIONAL + ** -- If present, version shall be v3 + ** } + ** + ** This is the ASN1 structure we should be dealing with at this point. + ** The code in this method will assert this is the structure we're dealing + ** and then add more user friendly text for that field. + */ + EASN1Object *sequence = e_asn1_object_new (); + char *text; + EASN1Object *subitem; + SECItem data; + + e_asn1_object_set_display_name (sequence, _("Certificate")); + + if (!process_version (&cert->priv->cert->version, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_serial_number_der (&cert->priv->cert->serialNumber, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + + if (!process_sec_algorithm_id (&cert->priv->cert->signature, &subitem)) + return FALSE; + e_asn1_object_set_display_name (subitem, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + process_name (&cert->priv->cert->issuer, &text); + subitem = e_asn1_object_new (); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_set_display_name (subitem, _("Issuer")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + +#if notyet + nsCOMPtr validitySequence = new nsNSSASN1Sequence(); + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpValidity").get(), + text); + validitySequence->SetDisplayName(text); + asn1Objects->AppendElement(validitySequence, PR_FALSE); + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotBefore").get(), + text); + nsCOMPtr validityData; + GetValidity(getter_AddRefs(validityData)); + PRTime notBefore, notAfter; + + validityData->GetNotBefore(¬Before); + validityData->GetNotAfter(¬After); + validityData = 0; + rv = ProcessTime(notBefore, text.get(), validitySequence); + if (NS_FAILED(rv)) + return rv; + + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotAfter").get(), + text); + rv = ProcessTime(notAfter, text.get(), validitySequence); + if (NS_FAILED(rv)) + return rv; +#endif + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Subject")); + + process_name (&cert->priv->cert->subject, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_subject_public_key_info (&cert->priv->cert->subjectPublicKeyInfo, sequence)) + return FALSE; + + /* Is there an issuerUniqueID? */ + if (cert->priv->cert->issuerID.data) { + /* The issuerID is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem + */ + data.data = cert->priv->cert->issuerID.data; + data.len = cert->priv->cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Issuer Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + + if (cert->priv->cert->subjectID.data) { + /* The subjectID is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem + */ + data.data = cert->priv->cert->issuerID.data; + data.len = cert->priv->cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Subject Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + if (cert->priv->cert->extensions) { + if (!process_extensions (cert->priv->cert->extensions, sequence)) + return FALSE; + } + + *seq = sequence; + + return TRUE; +} + +static gboolean +create_asn1_struct (ECert *cert) +{ + EASN1Object *sequence; + SECItem temp; + char *text; + + cert->priv->asn1 = e_asn1_object_new (); + + if (cert->priv->cert->nickname) + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->nickname); + else if (cert->priv->cn) + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cn); + else + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->subjectName); + + /* This sequence will be contain the tbsCertificate, signatureAlgorithm, + and signatureValue. */ + + if (!create_tbs_certificate_asn1_struct (cert, &sequence)) + return FALSE; + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_object_unref (sequence); + + if (!process_sec_algorithm_id (&cert->priv->cert->signatureWrap.signatureAlgorithm, &sequence)) + return FALSE; + e_asn1_object_set_display_name (sequence, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_object_unref (sequence); + + sequence = e_asn1_object_new (); + e_asn1_object_set_display_name (sequence, _("Certificate Signature Value")); + + /* The signatureWrap is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem */ + temp.data = cert->priv->cert->signatureWrap.signature.data; + temp.len = cert->priv->cert->signatureWrap.signature.len / 8; + process_raw_bytes (&temp, &text); + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_free (text); + + return TRUE; +} + +EASN1Object* +e_cert_get_asn1_struct (ECert *cert) +{ + if (!cert->priv->asn1) + create_asn1_struct (cert); + + return g_object_ref (cert->priv->asn1); +} + gboolean e_cert_mark_for_deletion (ECert *cert) { -- cgit