You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@labs.apache.org by dr...@apache.org on 2007/12/14 01:13:22 UTC

svn commit: r604069 - in /labs/badca: BaDCA/Certificates.py openssl/certmodule.c tests/03CertTestCase.py

Author: dreid
Date: Thu Dec 13 16:13:21 2007
New Revision: 604069

URL: http://svn.apache.org/viewvc?rev=604069&view=rev
Log:
Allow the passing of options when creating a certificate from a request.
Add test for passing options.
Refactor the code to make it simpler
Add handling of certificate attributes

This gets us much closer to the functionality of the old
BaDCA but means we have much more flexability than we did.


Modified:
    labs/badca/BaDCA/Certificates.py
    labs/badca/openssl/certmodule.c
    labs/badca/tests/03CertTestCase.py

Modified: labs/badca/BaDCA/Certificates.py
URL: http://svn.apache.org/viewvc/labs/badca/BaDCA/Certificates.py?rev=604069&r1=604068&r2=604069&view=diff
==============================================================================
--- labs/badca/BaDCA/Certificates.py (original)
+++ labs/badca/BaDCA/Certificates.py Thu Dec 13 16:13:21 2007
@@ -85,10 +85,14 @@
         if key.isValid():
             self.key = key
 
-    def signRequest(self, csr = None):
+    def signRequest(self, csr = None, options = None):
         if csr is None or self.cert is None or not self.key.hasPrivate():
             return None
-        nCert = cert.signRequest(self.cert, csr.csr, self.key.privRSA)
+        if options is None:
+            nCert = cert.signRequest(self.cert, csr.csr, self.key.privRSA)
+        else:
+            nCert = cert.signRequest(self.cert, csr.csr, self.key.privRSA, \
+                                     options)
         thecert = Certificate(obj = nCert)
         if thecert is not None:
             return thecert

Modified: labs/badca/openssl/certmodule.c
URL: http://svn.apache.org/viewvc/labs/badca/openssl/certmodule.c?rev=604069&r1=604068&r2=604069&view=diff
==============================================================================
--- labs/badca/openssl/certmodule.c (original)
+++ labs/badca/openssl/certmodule.c Thu Dec 13 16:13:21 2007
@@ -198,6 +198,107 @@
 }
 
 static int 
+removeNameFields(X509_NAME *subj, PyObject *list)
+{
+    Py_ssize_t sz = PyList_Size(list), pos = 0;
+
+    for (pos = 0; pos < sz; pos++) {
+        PyObject *pVal = PyList_GetItem(list, pos);
+        char *val = PyString_AsString(pVal);
+        int nid = 0, nPos = -1;
+
+        if ((nid = OBJ_sn2nid(val)) == NID_undef &&
+            (nid = OBJ_ln2nid(val)) == NID_undef) {
+            PyObject *errStr = makePyObjectFromPrintf("Unable to find NID "
+                         "for string '%s'", val);
+            PyErr_SetObject(PyExc_RuntimeError, errStr);
+            return 0;
+        }
+        while ((nPos = X509_NAME_get_index_by_NID(subj, nid, nPos)) != -1) {
+            X509_NAME_ENTRY *ne = X509_NAME_delete_entry(subj, nPos);
+            if (ne)
+                X509_NAME_ENTRY_free(ne);
+        }
+
+    }
+    return 1;
+}
+
+static int
+mergeSubjects(X509_NAME *to, X509_NAME *from, PyObject *ignore)
+{
+    int i, sz = X509_NAME_entry_count(from);
+    Py_ssize_t iSz = 0, iPos = 0;
+    int *nids = NULL;
+
+    if (ignore) {
+        iSz = PyList_Size(ignore);
+        nids = (int *)malloc(sizeof(int) * (int)iSz);
+        if (!nids) {
+            return 0;
+        }
+        for (i = 0, iPos = 0; iPos < iSz; iPos++) {
+            PyObject *pVal = PyList_GetItem(ignore, iPos);
+            char *val = PyString_AsString(pVal);
+            int nid = 0;
+
+            if ((nid = OBJ_sn2nid(val)) == NID_undef &&
+                (nid = OBJ_ln2nid(val)) == NID_undef) {
+                PyObject *errStr = makePyObjectFromPrintf("Unable to find NID "
+                             "for string '%s'", val);
+                PyErr_SetObject(PyExc_RuntimeError, errStr);
+                return 0;
+            }
+            nids[i++] = nid;
+        }
+    }
+
+    for (i = 0; i < sz; i++) {
+        X509_NAME_ENTRY *ne = X509_NAME_get_entry(from, i);
+
+        if (ignore) {
+            int j;
+            int skip = 0;
+            for (j = 0; j < (int)iSz; j++) {
+                int fPos = -1;
+
+                do {
+                    fPos = X509_NAME_get_index_by_NID(from, nids[j], fPos);
+                    if (fPos == i)
+                        skip = 1;
+                } while (fPos != -1);
+            }
+            if (skip)
+                continue;
+        }
+        if (!(X509_NAME_add_entry(to, ne, -1, 0))) {
+            PyErr_SetString(PyExc_RuntimeError, "Unable to adjust the "
+                                 "subject for the new certificate");
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static int
+addV3Attributes(X509 *cert, X509 *issuer, PyObject *attrDict)
+{
+    X509V3_CTX ctx;
+    int pos = 0;
+    PyObject *pKey = NULL, *pValue = NULL;
+
+    X509V3_set_ctx(&ctx, issuer, cert, NULL, NULL, 0);
+    
+    while (PyDict_Next(attrDict, &pos, &pKey, &pValue)) {
+        char *key = PyString_AsString(pKey);
+        char *val = PyString_AsString(pValue);
+        if (certificateAddExtension(cert, &ctx, key, val) == 0)
+            return 0;
+    }
+    return 1;
+}
+
+static int 
 signCertificateWithKey(X509 *cert, RSA *rsa)
 {
     int rv = 1;
@@ -469,23 +570,30 @@
     X509_NAME *xn_req = NULL, *subject = NULL;
     RSA *key = NULL;
     EVP_PKEY *pkey = NULL;
-    void *tmp[2];
-    int i, rv;
-    int remove_nids[] = { NID_commonName, NID_pkcs9_emailAddress, -1 };
-    int ignore_nids[] = { NID_countryName, NID_organizationName, 
-                          NID_organizationalUnitName, -1 };
+    PyObject *optionsDict = NULL, *attrDict = NULL;
+    PyObject *removeList = NULL, *ignoreList = NULL;
+    void *tmp[3];
+    int rv;
 
-    if (! PyArg_ParseTuple(args, "OOO", &tmp[0], &tmp[1], &tmp[2]))
+    if (! PyArg_ParseTuple(args, "OOO|O", &tmp[0], &tmp[1], &tmp[2], 
+                           &optionsDict))
         return NULL;
 
     issuer = (X509 *)PyCObject_AsVoidPtr(tmp[0]);
     req = (X509_REQ *)PyCObject_AsVoidPtr(tmp[1]);
     key = (RSA *)PyCObject_AsVoidPtr(tmp[2]);
+
     if (!issuer || !req || !key) {
         PyErr_SetString(PyExc_ValueError, "Invalid parameters passed");
         return NULL;
     }
 
+    if (optionsDict) {
+        attrDict = PyDict_GetItem(optionsDict, Py_BuildValue("s", "attributes"));
+        removeList = PyDict_GetItem(optionsDict, Py_BuildValue("s", "remove"));
+        ignoreList = PyDict_GetItem(optionsDict, Py_BuildValue("s", "ignore"));
+    }
+
     cert = X509_new();
     if (!cert) {
         PyErr_SetString(PyExc_MemoryError, "Unable to get a new X509 "
@@ -537,52 +645,29 @@
         }
     }
 
-    /* remove some uneeded fields */
-    for (i = 0; remove_nids[i] != -1; i++) {
-        int pos = -1;
-        while ((pos = X509_NAME_get_index_by_NID(subject, 
-                remove_nids[i], pos)) != -1) {
-            X509_NAME_ENTRY *ne = X509_NAME_delete_entry(subject, pos);
-            if (ne)
-                X509_NAME_ENTRY_free(ne);
-        }
-    }
-
-    /* go through the fields present in the request and add those that
-     * we need for the final certificate.
+    /* We create the subject for the new certificate by copying the
+     * subject of the issuing certificate, which means we may feel the
+     * need to remove some of the fields before we add the new ones.
+     * Do this before we add any additional ones we need, but only if
+     * we havea list of items to remove.
      */
+    if (removeList && removeNameFields(subject, removeList) != 1)
+        goto out;
+
+    /* Get the subject from the request. */
     if (!(xn_req = X509_REQ_get_subject_name(req))) {
         PyErr_SetString(PyExc_RuntimeError, "Unable to get the "
                                     "subject name from the request");
         goto out;
     }
-
-    for (i = 0; i < X509_NAME_entry_count(xn_req); i++) {
-        int j;
-        int skip = 0;
-        X509_NAME_ENTRY *ne = X509_NAME_get_entry(xn_req, i);
-        for (j = 0; ignore_nids[j] != -1; j++) {
-            int pos = -1;
-
-            /* The OU may be set in the request, but unless the certificate
-             * will be used for signing we want to set the OU from the
-             * issuing certificate.
-             */
-            do {
-                pos = X509_NAME_get_index_by_NID(xn_req, ignore_nids[j], pos);
-                if (pos == i)
-                    skip = 1;
-            } while (pos != -1);
-        }
-        if (skip)
-            continue;
-        if (!(X509_NAME_add_entry(subject, ne, -1, 0))) {
-            PyErr_SetString(PyExc_RuntimeError, "Unable to adjust the "
-                                 "subject for the new certificate");
-            goto out;
-        }
-    }
-
+    /* Create the final subject for our new certificate. This is done
+     * by merging the issuer subject (after adjustments) and the subject
+     * from the request. Optionally, some fields from the request subject
+     * can be ignored.
+     */
+    if (mergeSubjects(subject, xn_req, ignoreList) != 1)
+        goto out;
+    /* Now set the final subject into the certificate! */
     if (X509_set_subject_name(cert, subject) != 1) {
         PyErr_SetString(PyExc_RuntimeError, "Unable to set the subject "
                                       "name of the new certificate");
@@ -622,25 +707,16 @@
     if (certificate_set_serial(cert) == 0)
         goto out;
 
-    /* Add the X509_v3 extensions we need to set */
-/*
-    X509V3_set_ctx(&ctx, isroot ? cert : ca_cert, cert, NULL, NULL, 0);
-    for (i = 0; always_add[i].left; i++) {
-        if (certificate_add_extension(cac, cert, &ctx, always_add[i].left,
-                                      always_add[i].right) != 0)
-            goto out;
-    }
+    /* Add the X509_v3 extensions we need to set. If this fails
+     * the exeception will already have been set, so just bail out.
+     */
+    if (attrDict && addV3Attributes(cert, issuer, attrDict) != 1)
+        goto out;
 
-    if (cac->ca_extensions) {
-        for (i = 0; i < cac->ca_ext_count; i++) {
-            if (certificate_add_extension(cac, cert, &ctx, cac->ca_extensions[i].key,
-                                          cac->ca_extensions[i].value) != 0)
-                goto out;
-        }
-    }
-*/
 
-    /* Claim credit for the creation... */
+    /* Claim credit for the creation... 
+     * We don't care if this fails ;-)
+     */
     certificateAddExtension(cert, &ctx, "nsComment", "Created by BaDCA");
 
     if (signCertificateWithKey(cert, key) == 0)

Modified: labs/badca/tests/03CertTestCase.py
URL: http://svn.apache.org/viewvc/labs/badca/tests/03CertTestCase.py?rev=604069&r1=604068&r2=604069&view=diff
==============================================================================
--- labs/badca/tests/03CertTestCase.py (original)
+++ labs/badca/tests/03CertTestCase.py Thu Dec 13 16:13:21 2007
@@ -119,5 +119,28 @@
                     "Failed to create a certificate object from the CSR"
         newcert.writeToFile()
 
+    def test08(self):
+        """ Create a certificate from a CSR using options """
+        csr = CSRs.CSR('tests/csr/test2.csr')
+        assert csr is not None, "Failed to get the CSR object"
+        assert self.obj.readFromFile('tests/certs/ca1.pem') == 1, \
+                                     "Failed to read the CA certificate"
+        self.obj.addKeyPath('tests/keys/private')
+        options = { 'attributes': 
+                    {
+                      'subjectKeyIdentifier': 'hash',
+                      "authorityKeyIdentifier": "keyid:always,issuer:always",
+                      "subjectAltName": "email:copy",
+                      "issuerAltName": "issuer:copy",
+                    },
+                    'remove': [ 'CN', 'emailAddress', 'ST' ],
+                    'ignore': [ 'C', 'O', 'OU', 'ST' ], 
+                  }
+
+        newcert = self.obj.signRequest(csr, options)
+        assert newcert is not None, \
+                    "Failed to create a certificate object from the CSR"
+        newcert.writeToFile()
+
 if __name__ == "__main__":
     unittest.main()



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@labs.apache.org
For additional commands, e-mail: commits-help@labs.apache.org