diff --git a/autovpn/test/__init__.py b/autovpn/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/autovpn/test/test_vpn_app.py b/autovpn/test/test_vpn_app.py
new file mode 100644
index 0000000000000000000000000000000000000000..93a796f168a3d64878ed335504b3acec5dc95b8b
--- /dev/null
+++ b/autovpn/test/test_vpn_app.py
@@ -0,0 +1,65 @@
+import tempfile
+import shutil
+import unittest
+from flask import session
+from autovpn import vpn_app
+
+
+class VpnAppTest(unittest.TestCase):
+
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp()
+        self.config = {
+            'DEBUG': 'true',
+            'SECRET_KEY': 'somesecret',
+            'VPN_CA_ROOT': self.tmpdir,
+            'VPN_CA_SUBJECT': {'CN': 'test CA', 'O': 'test'},
+            'VPN_CA_BITS': 1024,
+            'VPN_ENDPOINT': 'vpn.example.com',
+            'VPN_SITE_URL': 'http://localhost:4000/',
+            'FOOTER': '''
+<p class="footer">
+  built by <a href="http://www.autistici.org/">autistici.org</a>
+</p>
+''',
+            'AUTH_ENABLE': True,
+            'AUTH_FUNCTION': lambda x, y: (x and y and x == y),
+            }
+
+        self.app = vpn_app.make_app(self.config)
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir)
+
+    def test_login_ok(self):
+        with self.app.test_client() as c:
+            rv = c.get('/login')
+            csrf = session['_csrf']
+            rv = c.post('/login', data={
+                    '_csrf': csrf,
+                    'username': 'admin',
+                    'password': 'admin'},
+                        follow_redirects=True)
+            self.assertTrue('download of the ZIP file' in rv.data)
+
+    def test_login_fail(self):
+        with self.app.test_client() as c:
+            rv = c.get('/login')
+            csrf = session['_csrf']
+            rv = c.post('/login', data={
+                    '_csrf': csrf,
+                    'username': 'user',
+                    'password': 'wrong password'},
+                        follow_redirects=True)
+            self.assertFalse(session.get('dl_ok'))
+            self.assertTrue('Authentication failed' in rv.data)
+
+    def test_cert_dl(self):
+        with self.app.test_client() as c:
+            with c.session_transaction() as sess:
+                sess['dl_ok'] = True
+                sess['_csrf'] = 'csrf'
+                sess['logged_in'] = True
+            rv = c.get('/newcertdl?_csrf=csrf')
+            self.assertEquals('200 OK', rv.status)
+            self.assertEquals('application/zip', rv.content_type)
diff --git a/autovpn/vpn_app.py b/autovpn/vpn_app.py
index 5a350ba4c9f15b47d3ac2560aa8ebcbf0918e07c..d4f50ae3c0c57dc6872fd5f17742faf412c679fb 100644
--- a/autovpn/vpn_app.py
+++ b/autovpn/vpn_app.py
@@ -139,7 +139,7 @@ def to_pkcs12(crt_pem, key_pem, ca_pem):
     try:
         for name, content in [
             ('crt.pem', crt_pem), ('key.pem', key_pem), ('ca.pem', ca_pem)]:
-            with open(os.path.join(tmpdir, name)) as fd:
+            with open(os.path.join(tmpdir, name), 'w') as fd:
                 fd.write(content)
         pipe = subprocess.Popen(
             ['openssl', 'pkcs12', '-export', '-password', 'pass:',