diff --git a/CHANGES b/CHANGES
index 8355a94..4f14a9a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+Patch
+- X509 calculated extensions work, patch by Carlos Neves
+
 0.21.1 - 2011-01-15
 -------------------
 - Distribution fix
diff --git a/M2Crypto/X509.py b/M2Crypto/X509.py
index eef83fe..f64289e 100644
--- a/M2Crypto/X509.py
+++ b/M2Crypto/X509.py
@@ -24,9 +24,11 @@ def new_extension(name, value, critical=0, _pyfree=1):
     """
     Create new X509_Extension instance.
     """
-    if name == 'subjectKeyIdentifier' and \
-        value.strip('0123456789abcdefABCDEF:') is not '':
-        raise ValueError('value must be precomputed hash')
+    if X509_Extension_Proxy.must_proxy(name, value):
+        # Maybe all extensions should be proxies?
+        # XXX Either all proxies (might solve other problems too), or at
+        # XXX least at least need to refactor to avoid code duplication
+        return X509_Extension_Proxy(name, value, critical, _pyfree)
     lhash = m2.x509v3_lhash()
     ctx = m2.x509v3_set_conf_lhash(lhash)
     x509_ext_ptr = m2.x509v3_ext_conf(lhash, ctx, name, value)
@@ -35,6 +37,96 @@ def new_extension(name, value, critical=0, _pyfree=1):
     return x509_ext 
 
 
+class X509_Extension_Proxy:
+    """
+    A holder object for X509 Extensions that need to be created with
+    certificate based calculated values.
+    """
+    def __init__ (self, name, value, critical=0, _pyfree=1):
+        self.name = name
+        self.value = value
+        self.critical = critical
+        self._pyfree = _pyfree
+
+    @staticmethod
+    def must_proxy (name, value):
+        """
+        Checks if this extension name and value define a calculated extension,
+        i.e. one that needs the certificate to render a valid value.
+        """
+        # XXX Are these the only cases where we must proxy?
+        # XXX Even if they are now, it does not seem this is future proof...
+        # XXX any other way around this?
+        return (name == 'subjectKeyIdentifier' and value == 'hash') or \
+               (name == 'authorityKeyIdentifier')
+
+    def set_critical(self, critical=1):
+        """
+        Mark this extension critical or noncritical. By default an
+        extension is not critical.
+
+        @type critical:  int
+        @param critical: Nonzero sets this extension as critical.
+                         Calling this method without arguments will
+                         set this extension to critical.
+        """
+        self.critical = critical
+        return self.critical
+    
+    def get_critical(self):
+        """
+        Return whether or not this is a critical extension.
+
+        @rtype:   int
+        @return:  Nonzero if this is a critical extension.
+        """
+        return self.critical
+    
+    def get_name(self):
+        """
+        Get the extension name, for example 'subjectAltName'.
+        """
+        return self.name
+
+    def get_value(self, flag=0, indent=0):
+        """
+        Get the extension value, for example 'DNS:www.example.com'.
+        
+        @param flag:   Flag to control what and how to print.
+                       Ignored in this proxy.
+        @param indent: How many spaces to print before actual value.
+        """
+        # XXX It is a problem that this method can give different output
+        # XXX compared to the real object.
+        return (" " * indent) + self.value
+
+    def __call__ (self, cert=None):
+        """
+        Create new X509_Extension instance.
+        """
+        # XXX Why is this __call__, wouldn't it be clearer to have an explicit
+        # XXX name for this? Or is there some usage I am missing...?
+        lhash = m2.x509v3_lhash()
+        ctx = m2.x509v3_set_conf_lhash(lhash)
+        if cert:
+            if isinstance(cert, X509) and m2.x509_type_check(cert.x509):
+                m2.x509v3_set_ctx_subject(ctx, cert.x509)
+                if cert.issuer and m2.x509_type_check(cert.issuer.x509):
+                    m2.x509v3_set_ctx_issuer(ctx, cert.issuer.x509)
+                else:
+                    m2.x509v3_set_ctx_issuer(ctx, cert.x509)
+            elif isinstance(cert, Request) and cert.req:
+                m2.x509v3_set_ctx_request(ctx, cert.req)
+        else:
+            if self.name == 'subjectKeyIdentifier' and self.value == 'hash':
+                raise ValueError('value must be precomputed hash if no context is given')
+
+        x509_ext_ptr = m2.x509v3_ext_conf(lhash, ctx, self.name, self.value)
+        x509_ext = X509_Extension(x509_ext_ptr, self._pyfree)
+        x509_ext.set_critical(self.critical)
+        return x509_ext
+        
+
 class X509_Extension:
     """
     X509 Extension
@@ -117,6 +209,7 @@ class X509_Extension_Stack:
             self.stack = m2.sk_x509_extension_new_null()
             self._pyfree = 1
             self.pystack = [] # This must be kept in sync with self.stack
+        self.proxies = []
         
     def __del__(self):
         if getattr(self, '_pyfree', 0):
@@ -143,6 +236,9 @@ class X509_Extension_Stack:
         @param x509_ext: X509_Extension object to be pushed onto the stack.
         @return: The number of extensions on the stack.
         """
+        if isinstance(x509_ext, X509_Extension_Proxy):
+            self.proxies.append(x509_ext)
+            return len(self.proxies)
         self.pystack.append(x509_ext)
         ret = m2.sk_x509_extension_push(self.stack, x509_ext._ptr())
         assert ret == len(self.pystack)
@@ -160,6 +256,13 @@ class X509_Extension_Stack:
             return None
         return self.pystack.pop()
 
+    def process_proxies(self, cert):
+        """
+        Create extensions from stored proxies, now that we have a context to work with.
+        """
+        for p in self.proxies:
+            self.push(p(cert))
+        self.proxies = []
 
 class X509_Name_Entry:
     """
@@ -333,6 +436,7 @@ class X509:
         else:
             self.x509 = m2.x509_new ()
             self._pyfree = 1
+        self.issuer = None
             
     def __del__(self):
         if getattr(self, '_pyfree', 0):
@@ -464,15 +568,18 @@ class X509:
         assert m2.x509_type_check(self.x509), "'x509' type error"
         return X509_Name(m2.x509_get_issuer_name(self.x509))
 
-    def set_issuer(self, name):
+    def set_issuer(self, issuer):
         """
-        Set issuer name.
+        Set issuer object (and set issuer name). The object will be used in extension contexts if needed.
 
-        @type name:     X509_Name
-        @param name:    subjectName field.
+        @type name:     X509_Name or X509
+        @param name:    subjectName field or issuer object.
         """
         assert m2.x509_type_check(self.x509), "'x509' type error"
-        return m2.x509_set_issuer_name(self.x509, name.x509_name)
+        if isinstance(issuer, X509):
+            self.issuer = issuer
+            issuer = X509_Name(m2.x509_get_subject_name(issuer.x509))
+        return m2.x509_set_issuer_name(self.x509, issuer.x509_name)
 
     def get_subject(self):
         assert m2.x509_type_check(self.x509), "'x509' type error"
@@ -495,6 +602,9 @@ class X509:
         @type ext:     X509_Extension
         @param ext:    Extension
         """
+        if isinstance(ext, X509_Extension_Proxy):
+            ext = ext(self)
+
         assert m2.x509_type_check(self.x509), "'x509' type error"
         return m2.x509_add_ext(self.x509, ext.x509_ext, -1)
 
@@ -966,6 +1076,7 @@ class Request:
         @type ext_stack:  X509_Extension_Stack
         @param ext_stack: Stack of extensions to add.
         """
+        ext_stack.process_proxies(self)
         return m2.x509_req_add_extensions(self.req, ext_stack._ptr())
 
     def verify(self, pkey):
diff --git a/SWIG/_x509.i b/SWIG/_x509.i
index 0471f68..a1a5212 100644
--- a/SWIG/_x509.i
+++ b/SWIG/_x509.i
@@ -531,6 +531,10 @@ x509v3_set_conf_lhash(LHASH                * lhash) {
           PyErr_SetString(PyExc_MemoryError, "x509v3_set_conf_lhash");
           return NULL;
       }
+      /* XXX Why is this needed? */
+      /* Make sure the structure members are nulled */
+      X509V3_set_ctx(ctx, NULL, NULL, NULL, NULL, 0);
+
       X509V3_set_conf_lhash(ctx, lhash);        
       return ctx;
 }
@@ -659,6 +663,30 @@ get_der_encoding_stack(STACK_OF(X509) *stack){
     return encodedString; 
 }
 
+/* XXX Are these simple pointers in the context, or should they be refcounted? */
+
+/* XXX Since these are really only used in one method, should these instead be
+ * XXX made into a single function, which can be called with NULL for
+ * XXX optional fields? Otherwise, what is there stopping calling 
+ * XXX set_ctx_subject without having set ctx first? */
+
+void x509v3_set_ctx_issuer(X509V3_CTX *ctx, X509 *issuer) {
+	ctx->issuer_cert = issuer;
+}
+
+void x509v3_set_ctx_subject(X509V3_CTX *ctx, X509 *subject) {
+	ctx->subject_cert = subject;
+}
+
+void x509v3_set_ctx_request(X509V3_CTX *ctx, X509_REQ *request) {
+	ctx->subject_req = request;
+}
+
+/* XXX This does not seem to be used, why is it here? */
+void x509v3_set_ctx_crl(X509V3_CTX *ctx, X509_CRL *crl) {
+	ctx->crl = crl;
+}
+
 %}
 
 /* Free malloc'ed return value for x509_name_oneline */
diff --git a/demo/x509/calc_extensions.py b/demo/x509/calc_extensions.py
new file mode 100644
index 0000000..136ff86
--- /dev/null
+++ b/demo/x509/calc_extensions.py
@@ -0,0 +1,118 @@
+from M2Crypto import *
+import time
+
+
+#!/bin/bash
+
+## Create Root private key.
+#openssl genrsa -out rootCA.key 2048
+
+def callback(*args):
+    pass
+
+rsa = RSA.gen_key(2048, 65537, callback)
+rsa.save_key('rootCA2.key', None)
+
+#
+## Create self-signed Root certificate.
+#openssl req -config openssl.cnf -new \
+#    -x509 -extensions root_ext \
+#    -key rootCA.key -out rootCA.crt \
+#    -subj "/C=GB/O=m2crypto/CN=Root CA" -set_serial 1
+
+exts = (('basicConstraints','critical, CA:TRUE'),
+        ('keyUsage','cRLSign, keyCertSign, keyEncipherment, nonRepudiation, digitalSignature'),
+        ('subjectKeyIdentifier','hash'),
+        ('authorityKeyIdentifier','keyid,issuer:always'),
+        ('crlDistributionPoints','URI:http://ca.fcpl.com/subord4CA.crl'),
+        ('nsCertType','sslCA, emailCA, objCA'),
+        )
+
+root_priv = EVP.load_key('rootCA2.key')
+root_ca = X509.X509()
+root_ca.set_pubkey(root_priv)
+root_ca.set_serial_number(1)
+name = X509.X509_Name()
+name.C = 'GB'
+name.O = 'm2crypto'
+name.CN = 'Root CA'
+t = long(time.time()) + time.timezone
+now = ASN1.ASN1_UTCTIME()
+now.set_time(t)
+nowPlusYear = ASN1.ASN1_UTCTIME()
+nowPlusYear.set_time(t + 60 * 60 * 24 * 365)
+root_ca.set_not_before(now)
+root_ca.set_not_after(nowPlusYear)
+root_ca.set_subject_name(name)
+root_ca.set_issuer_name(root_ca.get_subject())
+for k,v in exts:
+    root_ca.add_ext(X509.new_extension(k,v))
+
+root_ca.sign(root_priv, 'sha1')
+root_ca.save_pem('rootCA2.crt')
+
+#
+#mkdir certs_db
+#touch index.txt
+#echo 02 > serial
+#
+# Create Subord private key.
+#openssl genrsa -out subordCA.key 2048
+
+rsa = RSA.gen_key(2048, 65537, callback)
+rsa.save_key('suborbCA.key', None)
+
+
+#
+## Create Subord request.
+#openssl req -config openssl.cnf -new \
+#    -key subordCA.key -out subordCA.csr \
+#    -subj "/C=GB/O=m2crypto/CN=SubOrd CA"
+
+sub_priv = EVP.load_key('rootCA2.key')
+sub_req = X509.Request()
+sub_req.set_pubkey(sub_priv)
+name = X509.X509_Name()
+name.C = 'GB'
+name.O = 'm2crypto'
+name.CN = 'SubOrd CA'
+sub_req.set_subject_name(name)
+sub_req.sign(sub_priv, 'sha1')
+sub_req.save_pem('subordCA2.csr')
+
+
+# Sign Subord request with Root CA.
+#openssl ca -config openssl.cnf \
+#    -extensions subord_ext \
+#    -in subordCA.csr -out subordCA.crt
+
+exts = (('basicConstraints','critical, CA:TRUE, pathlen:0'),
+        ('keyUsage','cRLSign, keyCertSign, keyEncipherment, nonRepudiation, digitalSignature'),
+        ('subjectKeyIdentifier','hash'),
+        ('authorityKeyIdentifier','keyid,issuer:always'),
+        ('crlDistributionPoints','URI:http://ca.fcpl.com/subord4CA.crl'),
+        ('nsCertType','sslCA, emailCA, objCA'),
+        )
+
+req = X509.load_request('subordCA2.csr')
+ca = X509.load_cert('rootCA2.crt')
+
+out = X509.X509()
+out.set_issuer(ca)
+out.set_subject_name(req.get_subject())
+t = long(time.time()) + time.timezone
+now = ASN1.ASN1_UTCTIME()
+now.set_time(t)
+nowPlusYear = ASN1.ASN1_UTCTIME()
+nowPlusYear.set_time(t + 60 * 60 * 24 * 365)
+out.set_not_before(now)
+out.set_not_after(nowPlusYear)
+for k,v in exts:
+    out.add_ext(X509.new_extension(k,v))
+out.set_pubkey(req.get_pubkey())
+out.sign(root_priv, 'sha1')
+out.save_pem('subordCA2.crt')
+
+
+
+
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 7ea86df..0568d61 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -31,9 +31,11 @@ class X509TestCase(unittest.TestCase):
         if not ca:
             ext1 = X509.new_extension('subjectAltName', 'DNS:foobar.example.com')
             ext2 = X509.new_extension('nsComment', 'Hello there')
+            ext3 = X509.new_extension('subjectKeyIdentifier', 'hash')
             extstack = X509.X509_Extension_Stack()
             extstack.push(ext1)
             extstack.push(ext2)
+            extstack.push(ext3)
             x.add_extensions(extstack)
         self.assertRaises(ValueError, x.sign, pk, 'sha513')
         x.sign(pk,'sha1')
@@ -43,9 +45,15 @@ class X509TestCase(unittest.TestCase):
         return x, pk
 
     def test_ext(self):
-        self.assertRaises(ValueError, X509.new_extension,
-                          'subjectKeyIdentifier', 'hash')
+        ext = X509.new_extension('subjectKeyIdentifier', 'hash')
+        assert isinstance(ext, X509.X509_Extension_Proxy), ext
+        # XXX should check value too
+        self.assertRaises(ValueError, ext)
+        ext = X509.new_extension('authorityKeyIdentifier', 'keyid,issuer:always')
+        assert isinstance(ext, X509.X509_Extension_Proxy), ext
+        # XXX should check value too
         ext = X509.new_extension('subjectAltName', 'DNS:foobar.example.com')
+        assert isinstance(ext, X509.X509_Extension), ext
         assert ext.get_value() == 'DNS:foobar.example.com'
         assert ext.get_value(indent=2) == '  DNS:foobar.example.com'
         assert ext.get_value(flag=m2.X509V3_EXT_PARSE_UNKNOWN) == 'DNS:foobar.example.com'
@@ -250,6 +258,8 @@ class X509TestCase(unittest.TestCase):
         cert.set_pubkey(pkey) 
         ext = X509.new_extension('basicConstraints', 'CA:TRUE')
         cert.add_ext(ext)
+        cert.add_ext(X509.new_extension('subjectKeyIdentifier', 'hash'))
+        cert.add_ext(X509.new_extension('authorityKeyIdentifier', 'keyid,issuer:always'))
         cert.sign(pk, 'sha1')
 
         if m2.OPENSSL_VERSION_NUMBER >= 0x0090800fL:
@@ -268,6 +278,9 @@ class X509TestCase(unittest.TestCase):
     def test_mkcacert(self): 
         cacert, pk, pkey = self.mkcacert()
         assert cacert.verify(pkey)
+        ski = cacert.get_ext('subjectKeyIdentifier').get_value()
+        aki = cacert.get_ext('authorityKeyIdentifier').get_value()
+        self.assertEqual('keyid:'+ski, aki.split('\n')[0])
         
 
     def test_mkproxycert(self): 
