[gcr] gcr: Add support for parsing PKCS#10 and SPKAC formats



commit c574a0f1118c713ebdfd06da77936d11e8158c04
Author: Stef Walter <stefw collabora co uk>
Date:   Mon Nov 7 15:11:21 2011 +0100

    gcr: Add support for parsing PKCS#10 and SPKAC formats
    
     * Add support for PKCS#10 both DER and PEM encoded
     * Add support for HTML5 SPKAC <keygen> certificate request
       format, both raw DER and encoded like OpenSSL output
    
    https://bugzilla.gnome.org/show_bug.cgi?id=663604

 egg/pkix.asn                            |   14 +++
 gcr/gcr-parser.c                        |  144 ++++++++++++++++++++++++++++++-
 gcr/gcr-types.h                         |   17 +++-
 gcr/tests/files/base64-rsa-2048.spkac   |    1 +
 gcr/tests/files/der-rsa-2048.p10        |  Bin 0 -> 681 bytes
 gcr/tests/files/der-rsa-2048.spkac      |  Bin 0 -> 592 bytes
 gcr/tests/files/pem-rsa-2048.req        |   17 ++++
 testing/ca-example/requests/email.spkac |    1 +
 8 files changed, 190 insertions(+), 4 deletions(-)
---
diff --git a/egg/pkix.asn b/egg/pkix.asn
index c220c8a..6cf966b 100644
--- a/egg/pkix.asn
+++ b/egg/pkix.asn
@@ -1226,4 +1226,18 @@ id-pda-countryOfResidence   AttributeType ::= { id-pda 5 }
 CountryOfResidence ::=      PrintableString (SIZE (2))
                             -- ISO 3166 Country Code
 
+-- spkac: added by gnome-keyring
+-- http://dev.w3.org/html5/spec/Overview.html
+
+PublicKeyAndChallenge ::= SEQUENCE {
+	spki SubjectPublicKeyInfo,
+	challenge IA5String
+}
+
+SignedPublicKeyAndChallenge ::= SEQUENCE {
+	publicKeyAndChallenge PublicKeyAndChallenge,
+	signatureAlgorithm AlgorithmIdentifier,
+	signature BIT STRING
+}
+
 END
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index e7455d3..b43731b 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -174,6 +174,7 @@ static GQuark PEM_ENCRYPTED_PRIVATE_KEY;
 static GQuark PEM_PRIVATE_KEY;
 static GQuark PEM_PKCS7;
 static GQuark PEM_PKCS12;
+static GQuark PEM_CERTIFICATE_REQUEST;
 
 static GQuark ARMOR_PGP_PUBLIC_KEY_BLOCK;
 static GQuark ARMOR_PGP_PRIVATE_KEY_BLOCK;
@@ -198,6 +199,8 @@ init_quarks (void)
 		QUARK (PEM_ENCRYPTED_PRIVATE_KEY, "ENCRYPTED PRIVATE KEY");
 		QUARK (PEM_PKCS7, "PKCS7");
 		QUARK (PEM_PKCS12, "PKCS12");
+		QUARK (PEM_CERTIFICATE_REQUEST, "CERTIFICATE REQUEST");
+
 		QUARK (ARMOR_PGP_PRIVATE_KEY_BLOCK, "PGP PRIVATE KEY BLOCK");
 		QUARK (ARMOR_PGP_PUBLIC_KEY_BLOCK, "PGP PUBLIC KEY BLOCK");
 
@@ -327,6 +330,9 @@ parsed_description (GcrParsed *parsed,
 	case CKO_GCR_GNUPG_RECORDS:
 		parsed->description = _("PGP Key");
 		break;
+	case CKO_GCR_CERTIFICATE_REQUEST:
+		parsed->description = _("Certificate Request");
+		break;
 	default:
 		parsed->description = NULL;
 		break;
@@ -1207,7 +1213,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self,
 		cih = NULL;
 
 		if (gcry != 0) {
-			g_warning ("couldn't decrypt pkcs7 data: %s", gcry_strerror (gcry));
+			g_warning ("couldn't decrypt pkcs12 data: %s", gcry_strerror (gcry));
 			goto done;
 		}
 
@@ -1476,6 +1482,115 @@ done:
 	return ret;
 }
 
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE REQUESTS
+ */
+
+static gint
+parse_der_pkcs10 (GcrParser *self,
+                  EggBytes *data)
+{
+	GNode *asn = NULL;
+	GNode *node;
+	GcrParsed *parsed;
+	gchar *name;
+
+	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-10-CertificationRequest", data);
+	if (!asn)
+		return GCR_ERROR_UNRECOGNIZED;
+
+	parsed = push_parsed (self, FALSE);
+	parsing_block (parsed, GCR_FORMAT_DER_PKCS10, data);
+
+	parsing_object (parsed, CKO_GCR_CERTIFICATE_REQUEST);
+	parsed_ulong_attribute (parsed, CKA_GCR_CERTIFICATE_REQUEST_TYPE, CKQ_GCR_PKCS10);
+
+	node = egg_asn1x_node (asn, "certificationRequestInfo", NULL);
+	g_return_val_if_fail (node != NULL, GCR_ERROR_FAILURE);
+
+	if (gcr_parser_get_parsed_label (self) == NULL)
+		name = egg_dn_read_part (egg_asn1x_node (node, "subject", "rdnSequence", NULL), "CN");
+
+	if (name != NULL) {
+		parsed_label (parsed, name);
+		g_free (name);
+	}
+
+	parsed_attribute_bytes (parsed, CKA_VALUE, data);
+	parsed_asn1_element (parsed, node, "subject", CKA_SUBJECT);
+	parsed_fire (self, parsed);
+
+	egg_asn1x_destroy (asn);
+
+	pop_parsed (self, parsed);
+	return SUCCESS;
+}
+
+static gint
+parse_der_spkac (GcrParser *self,
+                 EggBytes *data)
+{
+	GNode *asn = NULL;
+	GcrParsed *parsed;
+
+	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SignedPublicKeyAndChallenge", data);
+	if (!asn)
+		return GCR_ERROR_UNRECOGNIZED;
+
+	parsed = push_parsed (self, FALSE);
+	parsing_block (parsed, GCR_FORMAT_DER_SPKAC, data);
+
+	parsing_object (parsed, CKO_GCR_CERTIFICATE_REQUEST);
+	parsed_ulong_attribute (parsed, CKA_GCR_CERTIFICATE_REQUEST_TYPE, CKQ_GCR_SPKAC);
+
+	parsed_attribute_bytes (parsed, CKA_VALUE, data);
+	parsed_fire (self, parsed);
+
+	egg_asn1x_destroy (asn);
+
+	pop_parsed (self, parsed);
+	return SUCCESS;
+}
+
+static gint
+parse_base64_spkac (GcrParser *self,
+                    EggBytes *dat)
+{
+	const gchar *PREFIX = "SPKAC=";
+	const gsize PREFIX_LEN = 6;
+
+	GcrParsed *parsed;
+	guchar *spkac;
+	gsize n_spkac;
+	const guchar *data;
+	EggBytes *bytes;
+	gsize n_data;
+	gint ret;
+
+	data = egg_bytes_get_data (dat);
+	n_data = egg_bytes_get_size (dat);
+
+	if (n_data > PREFIX_LEN && memcmp (PREFIX, data, PREFIX_LEN))
+		return GCR_ERROR_UNRECOGNIZED;
+
+	parsed = push_parsed (self, FALSE);
+	parsing_block (parsed, GCR_FORMAT_DER_SPKAC, dat);
+
+	data += PREFIX_LEN;
+	n_data -= PREFIX_LEN;
+
+	spkac = g_base64_decode ((const gchar *)data, &n_spkac);
+	if (spkac != NULL) {
+		bytes = egg_bytes_new_take (spkac, n_spkac);
+		ret = parse_der_spkac (self, bytes);
+		egg_bytes_unref (bytes);
+	} else {
+		ret = GCR_ERROR_FAILURE;
+	}
+
+	pop_parsed (self, parsed);
+	return ret;
+}
 
 /* -----------------------------------------------------------------------------
  * OPENPGP
@@ -1563,6 +1678,9 @@ formats_for_armor_type (GQuark armor_type,
 	} else if (armor_type == PEM_PKCS7) {
 		*inner_format = GCR_FORMAT_DER_PKCS7;
 		*outer_format = GCR_FORMAT_PEM_PKCS7;
+	} else if (armor_type == PEM_CERTIFICATE_REQUEST) {
+		*inner_format = GCR_FORMAT_DER_PKCS10;
+		*outer_format = GCR_FORMAT_PEM_PKCS10;
 	} else if (armor_type == PEM_PKCS12) {
 		*inner_format = GCR_FORMAT_DER_PKCS12;
 		*outer_format = GCR_FORMAT_PEM_PKCS12;
@@ -1784,6 +1902,13 @@ parse_pem_pkcs7 (GcrParser *self,
 }
 
 static gint
+parse_pem_pkcs10 (GcrParser *self,
+                  EggBytes *data)
+{
+	return handle_pem_format (self, GCR_FORMAT_DER_PKCS10, data);
+}
+
+static gint
 parse_pem_pkcs12 (GcrParser *self,
                   EggBytes *data)
 {
@@ -1848,6 +1973,7 @@ parse_openssh_public (GcrParser *self,
  * @GCR_FORMAT_DER_PKCS8: DER encoded PKCS\#8 file which can contain a key
  * @GCR_FORMAT_DER_PKCS8_PLAIN: Unencrypted DER encoded PKCS\#8 file which can contain a key
  * @GCR_FORMAT_DER_PKCS8_ENCRYPTED: Encrypted DER encoded PKCS\#8 file which can contain a key
+ * @GCR_FORMAT_DER_PKCS10: DER encoded PKCS\#10 certificate request file
  * @GCR_FORMAT_DER_PKCS12: DER encoded PKCS\#12 file which can contain certificates and/or keys
  * @GCR_FORMAT_OPENSSH_PUBLIC: OpenSSH v1 or v2 public key
  * @GCR_FORMAT_OPENPGP_PACKET: OpenPGP key packet(s)
@@ -1860,14 +1986,22 @@ parse_openssh_public (GcrParser *self,
  * @GCR_FORMAT_PEM_PKCS7: An OpenSSL style PEM file containing PKCS\#7
  * @GCR_FORMAT_PEM_PKCS8_PLAIN: Unencrypted OpenSSL style PEM file containing PKCS\#8
  * @GCR_FORMAT_PEM_PKCS8_ENCRYPTED: Encrypted OpenSSL style PEM file containing PKCS\#8
+ * @GCR_FORMAT_PEM_PKCS10: An OpenSSL style PEM file containing PKCS\#10
  * @GCR_FORMAT_PEM_PKCS12: An OpenSSL style PEM file containing PKCS\#12
+ * @GCR_FORMAT_DER_SPKAC: DER encoded SPKAC as generated by HTML5 keygen element
+ * @GCR_FORMAT_BASE64_SPKAC: OpenSSL style SPKAC data
  *
  * The various format identifiers.
  */
 
-/* In order of parsing when no formats specified */
+/*
+ * In order of parsing when no formats specified. We put formats earlier
+ * if the parser can quickly detect whether GCR_ERROR_UNRECOGNIZED or not
+ */
+
 static const ParserFormat parser_normal[] = {
 	{ GCR_FORMAT_PEM, parse_pem },
+	{ GCR_FORMAT_BASE64_SPKAC, parse_base64_spkac },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
 	{ GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
@@ -1878,6 +2012,8 @@ static const ParserFormat parser_normal[] = {
 	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
 	{ GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
 	{ GCR_FORMAT_OPENPGP_ARMOR, parse_openpgp_armor },
+	{ GCR_FORMAT_DER_PKCS10, parse_der_pkcs10 },
+	{ GCR_FORMAT_DER_SPKAC, parse_der_spkac },
 };
 
 /* Must be in format_id numeric order */
@@ -1890,6 +2026,9 @@ static const ParserFormat parser_formats[] = {
 	{ GCR_FORMAT_DER_PKCS8, parse_der_pkcs8 },
 	{ GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
 	{ GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
+	{ GCR_FORMAT_DER_PKCS10, parse_der_pkcs10 },
+	{ GCR_FORMAT_DER_SPKAC, parse_der_spkac },
+	{ GCR_FORMAT_BASE64_SPKAC, parse_base64_spkac },
 	{ GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
 	{ GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
 	{ GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
@@ -1902,6 +2041,7 @@ static const ParserFormat parser_formats[] = {
 	{ GCR_FORMAT_PEM_PKCS8_PLAIN, parse_pem_pkcs8_plain },
 	{ GCR_FORMAT_PEM_PKCS8_ENCRYPTED, parse_pem_pkcs8_encrypted },
 	{ GCR_FORMAT_PEM_PKCS12, parse_pem_pkcs12 },
+	{ GCR_FORMAT_PEM_PKCS10, parse_pem_pkcs10 },
 };
 
 static int
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index cffed10..e43e7ef 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -83,6 +83,10 @@ typedef enum {
 	GCR_FORMAT_DER_PKCS8_PLAIN,
 	GCR_FORMAT_DER_PKCS8_ENCRYPTED,
 
+	GCR_FORMAT_DER_PKCS10 = 450,
+	GCR_FORMAT_DER_SPKAC = 455,
+	GCR_FORMAT_BASE64_SPKAC,
+
 	GCR_FORMAT_DER_PKCS12 = 500,
 
 	GCR_FORMAT_OPENSSH_PUBLIC = 600,
@@ -98,7 +102,8 @@ typedef enum {
 	GCR_FORMAT_PEM_PKCS8_PLAIN,
 	GCR_FORMAT_PEM_PKCS8_ENCRYPTED,
 	GCR_FORMAT_PEM_PKCS12,
-	GCR_FORMAT_PEM_PRIVATE_KEY
+	GCR_FORMAT_PEM_PRIVATE_KEY,
+	GCR_FORMAT_PEM_PKCS10,
 } GcrDataFormat;
 
 /*
@@ -108,7 +113,15 @@ typedef enum {
 
 enum {
 	/* An object class representing GcrRecord/gnupg-colons style data */
-	CKO_GCR_GNUPG_RECORDS = (CKO_VENDOR_DEFINED | 0x47435200UL /* GCR0 */)
+	CKO_GCR_GNUPG_RECORDS = (CKO_VENDOR_DEFINED | 0x47435200UL /* GCR0 */),
+
+	CKO_GCR_CERTIFICATE_REQUEST,
+	CKA_GCR_CERTIFICATE_REQUEST_TYPE,
+};
+
+enum {
+	CKQ_GCR_PKCS10,
+	CKQ_GCR_SPKAC
 };
 
 G_END_DECLS
diff --git a/gcr/tests/files/base64-rsa-2048.spkac b/gcr/tests/files/base64-rsa-2048.spkac
new file mode 100644
index 0000000..e1a3286
--- /dev/null
+++ b/gcr/tests/files/base64-rsa-2048.spkac
@@ -0,0 +1 @@
+SPKAC=MIICTDCCATQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCximUa27OhNYvidD/G6Y9iNZ8Zf4Q5Hs04CLNFfUhhhBXcC8BtkKrBrvXg+fCggPKSeubShlyOV7iKM5csNfEn4BC/z+AllnRvfZPCIcn/VMhT+iRNBtDP/P2A6wNcF2sNuGnTh/gpK2TBYEtA8zd/YD7vJIIGKcr9a3uzuC7Pg9s2xC7tfY+Lg1jgM1GPZ0T7iF1EA/4CqUDJQYvKFR/rbO6y43NsokwU6lDFNYqClu/vixQ/wp2S0sIPEg01LA39x2K/BXowGRhIKQYstNAjjiM8YzIg6+V4CR5uIgpof67eBdiLJqQhZQUSZuIQkyhkg8kwkZg2Ob9uxekoLG3JAgMBAAEWDE15IGNoYWxsZW5nZTANBgkqhkiG9w0BAQQFAAOCAQEAaovLKwXqvpz0M3QvLisqadPJ7zKHc267waOjvuOhgzFCa1VmlbaS5FgBf06EBluPHu23wy66NVSz/JGgm2HGwCAkD0JDziZUGtSDfQdEkIKgynJXaGQA3upBwCwJ8KT24sLF3Sv1OFKrMLWjWg3LJualnP/GBsPDd4P2uPyaVgilJCGBqxAMcPwB1SzFMD+LnRALY0o+MdvwR35X160biP5vGxUL6c9MQs9obQNQEv3wZHb+u+rMr9szoRUHga5Mt1ncG3o1bzP81U5cUGQ+GQoBAH3kImmyUX7xLBqMxq+BFjC4cqU1/dAtqeUeKdNLYs4Qt7D4PvVrYOLBhvb70Q==
diff --git a/gcr/tests/files/der-rsa-2048.p10 b/gcr/tests/files/der-rsa-2048.p10
new file mode 100644
index 0000000..6355622
Binary files /dev/null and b/gcr/tests/files/der-rsa-2048.p10 differ
diff --git a/gcr/tests/files/der-rsa-2048.spkac b/gcr/tests/files/der-rsa-2048.spkac
new file mode 100644
index 0000000..f3b3781
Binary files /dev/null and b/gcr/tests/files/der-rsa-2048.spkac differ
diff --git a/gcr/tests/files/pem-rsa-2048.req b/gcr/tests/files/pem-rsa-2048.req
new file mode 100644
index 0000000..71bd130
--- /dev/null
+++ b/gcr/tests/files/pem-rsa-2048.req
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICpTCCAY0CAQAwYDETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixk
+ARkWB0VYQU1QTEUxDjAMBgNVBAMTBWVtYWlsMSAwHgYJKoZIhvcNAQkBFhFlbWFp
+bEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGK
+ZRrbs6E1i+J0P8bpj2I1nxl/hDkezTgIs0V9SGGEFdwLwG2QqsGu9eD58KCA8pJ6
+5tKGXI5XuIozlyw18SfgEL/P4CWWdG99k8Ihyf9UyFP6JE0G0M/8/YDrA1wXaw24
+adOH+CkrZMFgS0DzN39gPu8kggYpyv1re7O4Ls+D2zbELu19j4uDWOAzUY9nRPuI
+XUQD/gKpQMlBi8oVH+ts7rLjc2yiTBTqUMU1ioKW7++LFD/CnZLSwg8SDTUsDf3H
+Yr8FejAZGEgpBiy00COOIzxjMiDr5XgJHm4iCmh/rt4F2IsmpCFlBRJm4hCTKGSD
+yTCRmDY5v27F6SgsbckCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQB312vpA1P4
+Jb35YVmNBtLlUL8OlBLj6hr48NBjkRigHSBeUiKMgUFjbWmTSK1DRddD6hZFTjBm
+S1yquCXxh+JMXi957NVHVk8w3fS1ySFpOxXMmZCjT5edgae7VTDBveDBQfAVtTP+
+02VUhdnwZGQAH7mBM8Ml9iHB5rcgS5BsMexu0YH3p7Qi5DBHeVo6CqbguST1Wczm
+Faql7m329stKQHf1nCOsSroLWb4qugtZ9rxpQJVIqOxVPxP3tBK1GQg/nM/VwI0l
+jMZ8lpn7J77XEYgb+Q642RoXxV8PRLtG4GKeZ13/5LssAsTvS4EkzuhxD+bPAcYu
+WXklJECG6mpw
+-----END CERTIFICATE REQUEST-----
diff --git a/testing/ca-example/requests/email.spkac b/testing/ca-example/requests/email.spkac
new file mode 100644
index 0000000..e1a3286
--- /dev/null
+++ b/testing/ca-example/requests/email.spkac
@@ -0,0 +1 @@
+SPKAC=MIICTDCCATQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCximUa27OhNYvidD/G6Y9iNZ8Zf4Q5Hs04CLNFfUhhhBXcC8BtkKrBrvXg+fCggPKSeubShlyOV7iKM5csNfEn4BC/z+AllnRvfZPCIcn/VMhT+iRNBtDP/P2A6wNcF2sNuGnTh/gpK2TBYEtA8zd/YD7vJIIGKcr9a3uzuC7Pg9s2xC7tfY+Lg1jgM1GPZ0T7iF1EA/4CqUDJQYvKFR/rbO6y43NsokwU6lDFNYqClu/vixQ/wp2S0sIPEg01LA39x2K/BXowGRhIKQYstNAjjiM8YzIg6+V4CR5uIgpof67eBdiLJqQhZQUSZuIQkyhkg8kwkZg2Ob9uxekoLG3JAgMBAAEWDE15IGNoYWxsZW5nZTANBgkqhkiG9w0BAQQFAAOCAQEAaovLKwXqvpz0M3QvLisqadPJ7zKHc267waOjvuOhgzFCa1VmlbaS5FgBf06EBluPHu23wy66NVSz/JGgm2HGwCAkD0JDziZUGtSDfQdEkIKgynJXaGQA3upBwCwJ8KT24sLF3Sv1OFKrMLWjWg3LJualnP/GBsPDd4P2uPyaVgilJCGBqxAMcPwB1SzFMD+LnRALY0o+MdvwR35X160biP5vGxUL6c9MQs9obQNQEv3wZHb+u+rMr9szoRUHga5Mt1ncG3o1bzP81U5cUGQ+GQoBAH3kImmyUX7xLBqMxq+BFjC4cqU1/dAtqeUeKdNLYs4Qt7D4PvVrYOLBhvb70Q==



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]