Skip to content

Commit

Permalink
Version 0.3.5
Browse files Browse the repository at this point in the history
Better certificate splitting methods.
More extensive tests with mock.
Documentation updated.
Removed unnecessary Python 3 text conversions.
  • Loading branch information
hbldh committed Apr 10, 2016
1 parent 0690353 commit 871fa88
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 166 deletions.
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ source = pybankid
include = */pybankid/*
omit =
*/setup.py

[report]
exclude_lines =
if 'requirementAlternatives' in kwargs

3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ branches:
- develop
install:
- "pip install pytest"
- "pip install mock"
- "pip install pytest-cov"
- "pip install python-coveralls"
- "pip install -r requirements.txt"
- "pip install -e ."
script: py.test tests/ --cov bankid --cov-report term-missing
after_success:
- coveralls
Expand Down
4 changes: 2 additions & 2 deletions bankid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
try:
from .client import BankIDClient
import bankid.exceptions as exceptions
from .testcert import create_bankid_test_server_cert_and_key
from .certutils import create_bankid_test_server_cert_and_key

__all__ = ['BankIDClient', 'exceptions', 'create_bankid_test_server_cert_and_key', 'version']
except ImportError:
Expand All @@ -21,7 +21,7 @@
# version.
_version_major = 0
_version_minor = 3
_version_patch = 4
_version_patch = 5
# _version_extra = 'dev1'
# _version_extra = 'a1'
_version_extra = '' # Uncomment this for full releases
Expand Down
127 changes: 127 additions & 0 deletions bankid/certutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
:mod:`bankid.certutils` -- Certificate Utilities
================================================
Created by hbldh <[email protected]>
Created on 2016-04-10
"""

from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import

import os
import tempfile
import subprocess
import requests

_TEST_CERT_PASSWORD = 'qwerty123'
_TEST_CERT_URL = "https://www.bankid.com/assets/bankid/rp/FPTestcert2_20150818_102329.pfx"


def create_bankid_test_server_cert_and_key(destination_path):
"""Fetch the P12 certificate from BankID servers, split it into
a certificate part and a key part and save them as separate files,
stored in PEM format.
:param destination_path: The directory to save certificate and key files to.
:type destination_path: str
:returns: The path tuple ``(cert_path, key_path)``.
:rtype: tuple
"""

# Fetch P12 certificate and store in temporary folder.
cert_tmp_path = os.path.join(tempfile.gettempdir(),
os.path.basename(_TEST_CERT_URL))
r = requests.get(_TEST_CERT_URL)
with open(cert_tmp_path, 'wb') as f:
f.write(r.content)

certificate, key = split_certificate(cert_tmp_path,
destination_path,
password=_TEST_CERT_PASSWORD)
# Try to remove temporary file.
try:
os.remove(cert_tmp_path)
except:
pass

# Return path tuples.
return certificate, key


def split_certificate(certificate_path, destination_folder, password=None):
"""Splits a PKCS12 certificate into Base64-encoded DER certificate and key.
This method splits a potentially password-protected
`PKCS12 <https://en.wikipedia.org/wiki/PKCS_12>`_ certificate
(format ``.p12`` or ``.pfx``) into one certificate and one key part, both in
`pem <https://en.wikipedia.org/wiki/X.509#Certificate_filename_extensions>`_
format.
:returns: Tuple of certificate and key string data.
:rtype: tuple
"""
try:
p = subprocess.Popen(["openssl", 'version'], stdout=subprocess.PIPE)
sout, serr = p.communicate()
if not sout.decode().lower().startswith('openssl'):
raise NotImplementedError(
"OpenSSL executable could not be found. "
"Splitting cannot be performed.")
except:
raise NotImplementedError(
"OpenSSL executable could not be found. "
"Splitting cannot be performed.")

# Paths to output files.
out_cert_path = os.path.join(os.path.abspath(
os.path.expanduser(destination_folder)), 'certificate.pem')
out_key_path = os.path.join(os.path.abspath(
os.path.expanduser(destination_folder)), 'key.pem')

# Use openssl for converting to pem format.
pipeline_1 = [
'openssl', 'pkcs12',
'-in', "{0}".format(certificate_path),
'-passin' if password is not None else '',
'pass:{0}'.format(password) if password is not None else '',
'-out', "{0}".format(out_cert_path),
'-clcerts', '-nokeys'
]
p = subprocess.Popen(list(filter(None, pipeline_1)),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p.communicate()
pipeline_2 = [
'openssl', 'pkcs12',
'-in', "{0}".format(certificate_path),
'-passin' if password is not None else '',
'pass:{0}'.format(password) if password is not None else '',
'-out', "{0}".format(out_key_path),
'-nocerts', '-nodes'
]
p = subprocess.Popen(list(filter(None, pipeline_2)),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p.communicate()

# Return path tuples.
return out_cert_path, out_key_path


def main():
paths = create_bankid_test_server_cert_and_key(os.path.expanduser('~'))
print('Saved certificate as {0}'.format(paths[0]))
print('Saved key as {0}'.format(paths[1]))
return paths

if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions bankid/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
=====================================
.. moduleauthor:: hbldh <[email protected]>
Created on 2014-09-09, 16:55
"""
Expand Down Expand Up @@ -106,7 +107,6 @@ def sign(self, user_visible_data, personal_number=None, **kwargs):
warnings.warn("Requirement Alternatives option is not tested.", BankIDWarning)

try:

out = self.client.service.Sign(
userVisibleData=six.text_type(base64.b64encode(six.b(user_visible_data)), encoding='utf-8'),
personalNumber=personal_number, **kwargs)
Expand Down Expand Up @@ -197,7 +197,7 @@ def open(self, request):
resp = self.requests_session.get(request.url,
data=request.message,
headers=request.headers)
result = six.BytesIO(six.b(resp.content.decode('utf-8')))
result = six.BytesIO(resp.content)
return result

def send(self, request):
Expand Down
1 change: 1 addition & 0 deletions bankid/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
===============================================
.. moduleauthor:: hbldh <[email protected]>
Created on 2014-09-10, 08:29
"""
Expand Down
124 changes: 0 additions & 124 deletions bankid/testcert.py

This file was deleted.

53 changes: 53 additions & 0 deletions docs/certutils.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.. _certutils:

Certificate methods
===================

Converting/splitting certificates
---------------------------------

To convert your production certificate from PKCS_12 format to two ``pem``,
ready to be used by PyBankID, one can do the following:

.. code-block:: python
In [1]: from bankid.certutils import split_certificate
In [2]: split_certificate('/path/to/certificate.p12',
'/destination/folder/',
'password_for_certificate_p12')
Out [2]: ('/destination/folder/certificate.pem',
'/destination/folder/key.pem')
It can also be done via regular OpenSSL terminal calls:

.. code-block:: bash
openssl pkcs12 -in /path/to/certificate.p12 -passin pass:password_for_certificate_p12 -out /destination/folder/certificate.pem -clcerts -nokeys
openssl pkcs12 -in /path/to/certificate.p12 -passin pass:password_for_certificate_p12 -out /destination/folder/key.pem -nocerts -nodes
.. note::
This also removes the password from the private key in the certificate,
which is a requirement for using the PyBankID package in an automated way.

Test server certificate
-----------------------

There is a test certificate available on `BankID Technical Information webpage
<https://www.bankid.com/bankid-i-dina-tjanster/rp-info>`_, which can be used for
testing authorization and signing. The
:py:func:`bankid.certutils.create_bankid_test_server_cert_and_key` in the
:py:mod:`bankid.certutils` module fetches that test certificate, splits it
into one certificate and one key part and converts it from
`.p12 or .pfx <https://en.wikipedia.org/wiki/PKCS_12>`_ format to
`pem <https://en.wikipedia.org/wiki/X.509#Certificate_filename_extensions>`_.
These can then be used for testing purposes, by sending in ``test_server=True``
keyword in the :py:class:`~BankIDClient`.


API
---

.. automodule:: bankid.certutils
:members:

2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ the BankID servers.
usage
client
exceptions
testcert
certutils


Indices and tables
Expand Down
Loading

0 comments on commit 871fa88

Please sign in to comment.