Add subject and issuer parameters to X509Extension(). Fix bug in OpenSSL.test.util.failUnlessRaises().
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index cf69e0f..0e1e6c2 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -188,8 +188,11 @@
See \class{X509Extension}.
\end{datadesc}
-\begin{classdesc}{X509Extension}{typename, critical, value}
-A class representing an X.509 v3 certificate extensions.
+\begin{classdesc}{X509Extension}{typename, critical, value\optional{, subject}\optional{, issuer}}
+A class representing an X.509 v3 certificate extensions.
+See \url{http://openssl.org/docs/apps/x509v3_config.html\#STANDARD_EXTENSIONS}
+for \var{typename} strings and their options.
+Optional parameters \var{subject} and \var{issuer} must be X509 objects.
\end{classdesc}
\begin{datadesc}{NetscapeSPKIType}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 2322720..7b8bade 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -58,7 +58,7 @@
#define crypto_X509Extension_New_NUM 5
#define crypto_X509Extension_New_RETURN crypto_X509ExtensionObj *
-#define crypto_X509Extension_New_PROTO (char *, int, char *)
+#define crypto_X509Extension_New_PROTO (char *, int, char *, crypto_X509Obj *, crypto_X509Obj *)
#define crypto_PKCS7_New_NUM 6
#define crypto_PKCS7_New_RETURN crypto_PKCS7Obj *
diff --git a/src/crypto/x509ext.c b/src/crypto/x509ext.c
index cd169d2..ce3d95d 100644
--- a/src/crypto/x509ext.c
+++ b/src/crypto/x509ext.c
@@ -75,7 +75,8 @@
* Returns: The newly created X509Extension object
*/
crypto_X509ExtensionObj *
-crypto_X509Extension_New(char *type_name, int critical, char *value)
+crypto_X509Extension_New(char *type_name, int critical, char *value,
+ crypto_X509Obj *subject, crypto_X509Obj *issuer)
{
X509V3_CTX ctx;
crypto_X509ExtensionObj *self;
@@ -84,7 +85,12 @@
/* We have no configuration database - but perhaps we should. Anyhow, the
* context is necessary for any extension which uses the r2i conversion
* method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx. */
+ X509V3_set_ctx(&ctx, NULL, NULL, NULL, NULL, 0);
X509V3_set_ctx_nodb(&ctx);
+ if(subject)
+ ctx.subject_cert = subject->x509;
+ if(issuer)
+ ctx.issuer_cert = issuer->x509;
self = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type);
@@ -137,27 +143,40 @@
}
static char crypto_X509Extension_doc[] = "\n\
-X509Extension(typename, critical, value) -> X509Extension instance\n\
+X509Extension(typename, critical, value[, subject][, issuer]) -> \n\
+ X509Extension instance\n\
\n\
@param typename: The name of the extension to create.\n\
@type typename: C{str}\n\
@param critical: A flag indicating whether this is a critical extension.\n\
@param value: The value of the extension.\n\
@type value: C{str}\n\
+@param subject: Optional X509 cert to use as subject.\n\
+@type subject: C{X509}\n\
+@param issuer: Optional X509 cert to use as issuer.\n\
+@type issuer: C{X509}\n\
@return: The X509Extension object\n\
";
static PyObject *
-crypto_X509Extension_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
+crypto_X509Extension_new(PyTypeObject *subtype, PyObject *args,
+ PyObject *kwargs) {
char *type_name, *value;
- int critical;
+ int critical = 0;
+ crypto_X509Obj * subject = NULL;
+ crypto_X509Obj * issuer = NULL;
+ static char *kwlist[] = {"type_name", "critical", "value", "subject",
+ "issuer", NULL};
- if (!PyArg_ParseTuple(args, "sis:X509Extension", &type_name, &critical,
- &value)) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sis|O!O!:X509Extension",
+ kwlist, &type_name, &critical, &value,
+ &crypto_X509_Type, &subject,
+ &crypto_X509_Type, &issuer )) {
return NULL;
}
- return (PyObject *)crypto_X509Extension_New(type_name, critical, value);
+ return (PyObject *)crypto_X509Extension_New(type_name, critical, value,
+ subject, issuer);
}
/*
diff --git a/test/test_crypto.py b/test/test_crypto.py
index aa22cd0..8da81ef 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -7,6 +7,7 @@
from unittest import main
from os import popen2
+from datetime import datetime, timedelta
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
@@ -234,6 +235,51 @@
self.assertEqual(ext.get_short_name(), 'nsComment')
+ def test_issuer_and_subject(self):
+ """
+ Use L{X509Extension} to create a root cert with X509v3
+ extensions, which requires the "subject" or "issuer" optional args.
+ """
+ # Basic setup stuff to generate a certificate
+ pkey = PKey()
+ pkey.generate_key(TYPE_RSA, 1024)
+ req = X509Req()
+ req.set_pubkey(pkey)
+ req.get_subject().commonName = "Yoda root CA" # Authority good you have.
+ x509 = X509()
+ subject = x509.get_subject()
+ subject.commonName = req.get_subject().commonName
+ x509.set_issuer(subject)
+ x509.set_pubkey(pkey)
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+ expire = (datetime.now() + timedelta(days=100)).strftime("%Y%m%d%H%M%SZ")
+ x509.set_notBefore(now)
+ x509.set_notAfter(expire)
+ # Test "subject" as a unneeded parameter
+ ext1 = X509Extension('basicConstraints', False, 'CA:TRUE', subject=x509)
+ x509.add_extensions( (ext1, ) )
+ # Correct use of "subject" and "issuer"
+ ext3 = X509Extension('subjectKeyIdentifier', False, 'hash', subject=x509, )
+ x509.add_extensions( (ext3, ) )
+ ext2 = X509Extension('authorityKeyIdentifier', False, 'keyid:always,issuer:always', issuer=x509, )
+ x509.add_extensions( (ext2, ) )
+ # Test missing issuer
+ self.assertRaises(Error, X509Extension, 'authorityKeyIdentifier', False, 'keyid:always,issuer:always', )
+ # Test missing subject
+ self.assertRaises(Error, X509Extension, 'subjectKeyIdentifier', False, 'hash', )
+ # Test bad type of issuer and subject
+ self.assertRaises(TypeError, eval,
+ "OpenSSL.crypto.X509Extension('basicConstraints', False, 'CA:TRUE', subject=True)", ())
+ self.assertRaises(TypeError, eval,
+ "OpenSSL.crypto.X509Extension('basicConstraints', False, 'CA:TRUE', issuer=3)", ())
+ # Complete the certificate
+ x509.sign(pkey, 'sha1')
+ # Verify the certificate
+ text = dump_certificate(FILETYPE_TEXT, x509)
+ self.assertTrue( text.index('X509v3 Subject Key Identifier') > 100 )
+ self.assertTrue( text.index('X509v3 Authority Key Identifier') > 100 )
+
+
class PKeyTests(TestCase):
"""
diff --git a/test/util.py b/test/util.py
index fdc9348..72c9e44 100644
--- a/test/util.py
+++ b/test/util.py
@@ -11,6 +11,7 @@
import os, os.path
from tempfile import mktemp
from unittest import TestCase
+import sys
class TestCase(TestCase):
@@ -81,10 +82,10 @@
except exception, inst:
return inst
except:
- raise self.failureException('%s raised instead of %s:\n %s'
+ raise self.failureException('%s raised instead of %s'
% (sys.exc_info()[0],
exception.__name__,
- failure.Failure().getTraceback()))
+ ))
else:
raise self.failureException('%s not raised (%r returned)'
% (exception.__name__, result))