aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml16
-rwxr-xr-x.travis/install.sh14
-rw-r--r--CHANGELOG.rst4
-rw-r--r--docs/x509.rst11
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py16
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509v3.py6
-rw-r--r--src/cryptography/x509.py6
-rw-r--r--tests/test_x509.py90
8 files changed, 154 insertions, 9 deletions
diff --git a/.travis.yml b/.travis.yml
index c7413ea9..343576fe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,11 @@
sudo: false
+
language: python
+
cache:
directories:
- $HOME/.cache/pip
+
matrix:
include:
- python: 2.6 # these are just to make travis's UI a bit prettier
@@ -67,42 +70,55 @@ matrix:
env: TOXENV=py3pep8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py26
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py27
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py33
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py34
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=pypy
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=pypy3
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py26 OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py27 OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py33 OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=py34 OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=pypy OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=pypy3 OPENSSL=0.9.8
- language: generic
os: osx
+ osx_image: beta-xcode6.3
env: TOXENV=docs
install:
diff --git a/.travis/install.sh b/.travis/install.sh
index 9e14a92d..7c3e9de2 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -4,10 +4,10 @@ set -e
set -x
if [[ "$(uname -s)" == 'Darwin' ]]; then
- brew update
+ brew update || brew update
if [[ "${OPENSSL}" != "0.9.8" ]]; then
- brew upgrade openssl
+ brew outdated openssl || brew upgrade openssl
fi
if which pyenv > /dev/null; then
@@ -24,22 +24,22 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
python get-pip.py --user
;;
py33)
- brew upgrade pyenv
+ brew outdated pyenv || brew upgrade pyenv
pyenv install 3.3.6
pyenv global 3.3.6
;;
py34)
- brew upgrade pyenv
+ brew outdated pyenv || brew upgrade pyenv
pyenv install 3.4.2
pyenv global 3.4.2
;;
pypy)
- brew upgrade pyenv
+ brew outdated pyenv || brew upgrade pyenv
pyenv install pypy-2.5.1
pyenv global pypy-2.5.1
;;
pypy3)
- brew upgrade pyenv
+ brew outdated pyenv || brew upgrade pyenv
pyenv install pypy3-2.4.0
pyenv global pypy3-2.4.0
;;
@@ -49,7 +49,7 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
;;
esac
pyenv rehash
- pip install --user virtualenv
+ python -m pip install --user virtualenv
else
pip install virtualenv
fi
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e2f18909..4d7a9a82 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,10 @@ Changelog
.. note:: This version is not yet released and is under active development.
+* Support serialization of certificate signing requests using the
+ ``public_bytes`` method of
+ :class:`~cryptography.x509.CertificateSigningRequest`.
+
0.9 - 2015-05-13
~~~~~~~~~~~~~~~~
diff --git a/docs/x509.rst b/docs/x509.rst
index 3f1af86c..c8505a87 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -366,6 +366,17 @@ X.509 CSR (Certificate Signing Request) Object
>>> isinstance(csr.signature_hash_algorithm, hashes.SHA1)
True
+ .. method:: public_bytes(encoding)
+
+ :param encoding: The
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding`
+ that will be used to serialize the certificate request.
+
+ :return bytes: The data that can be written to a file or sent
+ over the network to be signed by the certificate
+ authority.
+
+
.. class:: Name
.. versionadded:: 0.8
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 6db6fc9c..72041366 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -25,7 +25,7 @@ from six.moves import urllib_parse
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
def _obj2txt(backend, obj):
@@ -689,3 +689,17 @@ class _CertificateSigningRequest(object):
extensions.append(x509.Extension(oid, critical, value))
return x509.Extensions(extensions)
+
+ def public_bytes(self, encoding):
+ if not isinstance(encoding, serialization.Encoding):
+ raise TypeError("encoding must be an item from the Encoding enum")
+
+ bio = self._backend._create_mem_bio()
+ if encoding is serialization.Encoding.PEM:
+ res = self._backend._lib.PEM_write_bio_X509_REQ(
+ bio, self._x509_req
+ )
+ elif encoding is serialization.Encoding.DER:
+ res = self._backend._lib.i2d_X509_REQ_bio(bio, self._x509_req)
+ assert res == 1
+ return self._backend._read_mem_bio(bio)
diff --git a/src/cryptography/hazmat/bindings/openssl/x509v3.py b/src/cryptography/hazmat/bindings/openssl/x509v3.py
index 054ab624..e9bc461a 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509v3.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509v3.py
@@ -172,16 +172,20 @@ void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *);
/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
x509v3.h header. */
void AUTHORITY_KEYID_free(AUTHORITY_KEYID *);
+
void *X509V3_set_ctx_nodb(X509V3_CTX *);
int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *);
int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *);
GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int);
+Cryptography_STACK_OF_ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_new_null(void);
int sk_ACCESS_DESCRIPTION_num(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_value(
Cryptography_STACK_OF_ACCESS_DESCRIPTION *, int
);
void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
+int sk_ACCESS_DESCRIPTION_push(Cryptography_STACK_OF_ACCESS_DESCRIPTION *,
+ ACCESS_DESCRIPTION *);
X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *,
X509V3_CTX *, int, char *);
@@ -206,6 +210,8 @@ POLICYQUALINFO *sk_POLICYQUALINFO_value(Cryptography_STACK_OF_POLICYQUALINFO *,
void sk_ASN1_INTEGER_free(Cryptography_STACK_OF_ASN1_INTEGER *);
int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *);
ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int);
+
+X509_EXTENSION *X509V3_EXT_i2d(int, int, void *);
"""
CUSTOMIZATIONS = """
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 7ac06622..9a3295ce 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -1194,3 +1194,9 @@ class CertificateSigningRequest(object):
"""
Returns the extensions in the signing request.
"""
+
+ @abc.abstractmethod
+ def public_bytes(self, encoding):
+ """
+ Encodes the request to PEM or DER format.
+ """
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 47c1c647..72fc9d40 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -15,7 +15,7 @@ from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.interfaces import (
DSABackend, EllipticCurveBackend, RSABackend, X509Backend
)
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
from .hazmat.primitives.test_ec import _skip_curve_unsupported
@@ -471,6 +471,94 @@ class TestRSACertificate(object):
),
]
+ def test_public_bytes_pem(self, backend):
+ # Load an existing CSR.
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+
+ # Encode it to PEM and load it back.
+ request = x509.load_pem_x509_csr(request.public_bytes(
+ encoding=serialization.Encoding.PEM,
+ ), backend)
+
+ # We should recover what we had to start with.
+ assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
+ public_key = request.public_key()
+ assert isinstance(public_key, rsa.RSAPublicKey)
+ subject = request.subject
+ assert isinstance(subject, x509.Name)
+ assert list(subject) == [
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
+ x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
+ ]
+
+ def test_public_bytes_der(self, backend):
+ # Load an existing CSR.
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+
+ # Encode it to DER and load it back.
+ request = x509.load_der_x509_csr(request.public_bytes(
+ encoding=serialization.Encoding.DER,
+ ), backend)
+
+ # We should recover what we had to start with.
+ assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
+ public_key = request.public_key()
+ assert isinstance(public_key, rsa.RSAPublicKey)
+ subject = request.subject
+ assert isinstance(subject, x509.Name)
+ assert list(subject) == [
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
+ x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
+ ]
+
+ def test_public_bytes_invalid_encoding(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+
+ with pytest.raises(TypeError):
+ request.public_bytes('NotAnEncoding')
+
+ @pytest.mark.parametrize(
+ ("request_path", "loader_func", "encoding"),
+ [
+ (
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ serialization.Encoding.PEM,
+ ),
+ (
+ os.path.join("x509", "requests", "rsa_sha1.der"),
+ x509.load_der_x509_csr,
+ serialization.Encoding.DER,
+ ),
+ ]
+ )
+ def test_public_bytes_match(self, request_path, loader_func, encoding,
+ backend):
+ request_bytes = load_vectors_from_file(
+ request_path, lambda pemfile: pemfile.read(), mode="rb"
+ )
+ request = loader_func(request_bytes, backend)
+ serialized = request.public_bytes(encoding)
+ assert serialized == request_bytes
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)