diff options
author | Matěj Cepl <mcepl@cepl.eu> | 2017-04-20 16:48:07 +0200 |
---|---|---|
committer | Matěj Cepl <mcepl@cepl.eu> | 2017-09-20 20:52:13 +0200 |
commit | c0b300a5e4532b549f18b5526604219d717e2582 (patch) | |
tree | f5ac18c4cbc4b61c6f256f5a925168a7acff4080 /doc | |
parent | 5b1beb4b943ab7866162c4f8d0a19a779c0e2d1b (diff) | |
download | m2crypto-c0b300a5e4532b549f18b5526604219d717e2582.tar.gz |
Incorporate existing HTML files into Sphinx documents.
Diffstat (limited to 'doc')
-rw-r--r-- | doc/ZServerSSL-HOWTO.html | 271 | ||||
-rw-r--r-- | doc/ZServerSSL-HOWTO.rst | 237 | ||||
-rw-r--r-- | doc/howto.ca.html | 891 | ||||
-rw-r--r-- | doc/howto.ca.rst | 368 | ||||
-rw-r--r-- | doc/howto.smime.html | 1573 | ||||
-rw-r--r-- | doc/howto.smime.rst | 776 | ||||
-rw-r--r-- | doc/howto.ssl.html | 206 | ||||
-rw-r--r-- | doc/howto.ssl.rst | 129 | ||||
-rw-r--r-- | doc/index.rst | 17 |
9 files changed, 1522 insertions, 2946 deletions
diff --git a/doc/ZServerSSL-HOWTO.html b/doc/ZServerSSL-HOWTO.html deleted file mode 100644 index 827cd0e..0000000 --- a/doc/ZServerSSL-HOWTO.html +++ /dev/null @@ -1,271 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<base href="http://localhost:9080/home/m2/zserverssl-011-howto/" /> - -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.2.8: http://docutils.sourceforge.net/" /> -<title>ZServerSSL HOWTO</title> -<meta name="author" content="Ng Pheng Siong" /> -<meta name="date" content="2003-06-22" /> -<link rel="stylesheet" href="default.css" type="text/css" /> -</head> -<body> -<div class="document" id="zserverssl-howto"> -<h1 class="title">ZServerSSL HOWTO</h1> -<table class="docinfo" frame="void" rules="none"> -<col class="docinfo-name" /> -<col class="docinfo-content" /> -<tbody valign="top"> -<tr><th class="docinfo-name">Author:</th> -<td>Ng Pheng Siong</td></tr> -<tr class="field"><th class="docinfo-name">Id:</th><td class="field-body">ZServerSSL-HOWTO,v 1.1 2003/06/22 17:40:13 ngps Exp</td> -</tr> -<tr><th class="docinfo-name">Date:</th> -<td>2003-06-22</td></tr> -<tr class="field"><th class="docinfo-name">Web-Site:</th><td class="field-body"><a class="reference" href="http://chandlerproject.org/Projects/MeTooCrypto">http://chandlerproject.org/Projects/MeTooCrypto</a></td> -</tr> -</tbody> -</table> -<div class="contents topic" id="contents"> -<p class="topic-title"><a name="contents">Contents</a></p> -<ul class="simple"> -<li><a class="reference" href="#introduction" id="id2" name="id2">Introduction</a></li> -<li><a class="reference" href="#preparation" id="id3" name="id3">Preparation</a></li> -<li><a class="reference" href="#installation" id="id4" name="id4">Installation</a></li> -<li><a class="reference" href="#testing" id="id5" name="id5">Testing</a><ul> -<li><a class="reference" href="#https" id="id6" name="id6">HTTPS</a></li> -<li><a class="reference" href="#webdav-over-https" id="id7" name="id7">WebDAV-over-HTTPS</a></li> -<li><a class="reference" href="#webdav-source-over-https" id="id8" name="id8">WebDAV-Source-over-HTTPS</a></li> -<li><a class="reference" href="#python-with-m2crypto" id="id9" name="id9">Python with M2Crypto</a><ul> -<li><a class="reference" href="#id1" id="id10" name="id10">HTTPS</a></li> -<li><a class="reference" href="#xmlrpc-over-https" id="id11" name="id11">XMLRPC-over-HTTPS</a></li> -</ul> -</li> -</ul> -</li> -<li><a class="reference" href="#conclusion" id="id12" name="id12">Conclusion</a></li> -</ul> -</div> -<div class="section" id="introduction"> -<h1><a class="toc-backref" href="#id2" name="introduction">Introduction</a></h1> -<p>ZServerSSL adds to Zope's ZServer the following:</p> -<ul class="simple"> -<li>HTTPS server</li> -<li>WebDAV-source-over-HTTPS server</li> -</ul> -<p>With the HTTPS server, ZServerSSL also provides WebDAV-over-HTTPS -and XMLRPC-over-HTTPS access to Zope.</p> -<p>These instructions apply to both Un*x and Windows installations of -Zope 2.6.1. To avoid cluttering the presentation, Windows pathnames -are shown in Un*x fashion.</p> -</div> -<div class="section" id="preparation"> -<h1><a class="toc-backref" href="#id3" name="preparation">Preparation</a></h1> -<ol class="arabic simple"> -<li>Download M2Crypto 0.11, contained in the file <tt class="literal"><span class="pre">m2crypto-0.11.zip</span></tt>.</li> -<li>Unpack <tt class="literal"><span class="pre">m2crypto-0.11.zip</span></tt>. This will create a directory -<tt class="literal"><span class="pre">m2crypto-0.11</span></tt>. Henceforth, we refer to this directory as <tt class="literal"><span class="pre">$M2</span></tt>.</li> -<li>Install M2Crypto per the instructions in <tt class="literal"><span class="pre">$M2/INSTALL</span></tt>.</li> -</ol> -<p>The ZServerSSL distribution is in <tt class="literal"><span class="pre">$M2/demo/Zope</span></tt>. We shall refer to -this directory as <tt class="literal"><span class="pre">$ZSSL</span></tt>.</p> -</div> -<div class="section" id="installation"> -<h1><a class="toc-backref" href="#id4" name="installation">Installation</a></h1> -<p>Below, we refer to your Zope top-level directory as <tt class="literal"><span class="pre">$ZOPE</span></tt>.</p> -<ol class="arabic"> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/z2s.py</span></tt> into <tt class="literal"><span class="pre">$ZOPE</span></tt>.</p> -</li> -<li><p class="first">Depending on your operating system, modify <tt class="literal"><span class="pre">$ZOPE/start</span></tt> or -<tt class="literal"><span class="pre">$ZOPE/start.bat</span></tt> to invoke <tt class="literal"><span class="pre">$ZOPE/z2s.py</span></tt>, instead of -<tt class="literal"><span class="pre">$ZOPE/z2.py</span></tt>. The files <tt class="literal"><span class="pre">$ZSSL/starts</span></tt> and -<tt class="literal"><span class="pre">$ZSSL/starts.bat</span></tt> serve as examples.</p> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/dh1024.pem</span></tt> into <tt class="literal"><span class="pre">$ZOPE</span></tt>. This file contains -Diffie-Hellman parameters for use by the SSL protocol.</p> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/randpool.dat</span></tt> into <tt class="literal"><span class="pre">$ZOPE</span></tt>. This file contains seed -material for the OpenSSL PRNG. Alternatively, create -<tt class="literal"><span class="pre">$ZOPE/randpool.dat</span></tt> thusly:</p> -<pre class="literal-block"> -$ dd if=/dev/urandom of=randpool.dat bs=1024 count=1 -</pre> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/ca.pem</span></tt> to <tt class="literal"><span class="pre">$ZOPE</span></tt>. This file contains an example -Certification Authority (CA) certificate. For information on -operating your own CA, see -<a class="reference" href="http://svn.osafoundation.org/m2crypto/trunk/doc/howto.ca.html">howto.ca.html</a> or one of numerous -similar documents available on the web.</p> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/server.pem</span></tt> to <tt class="literal"><span class="pre">$ZOPE</span></tt>. This file contains an RSA -key pair and its X.509v3 certificate issued by the above CA. You -may also create your own key/certificate bundle.</p> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/ZServer/HTTPS_Server.py</span></tt> to <tt class="literal"><span class="pre">$ZOPE/ZServer</span></tt>.</p> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/ZServer/__init__.py</span></tt> to <tt class="literal"><span class="pre">$ZOPE/ZServer</span></tt>. This -overwrites the existing <tt class="literal"><span class="pre">$ZOPE/ZServer/__init__.py</span></tt>. Alternatively, -apply the following patch to <tt class="literal"><span class="pre">$ZOPE/ZServer/__init__.py</span></tt>:</p> -<pre class="literal-block"> ---- __init__.py.org Sat Jun 21 23:20:41 2003 -+++ __init__.py Tue Jan 7 23:30:53 2003 -@@ -84,6 +84,7 @@ - import asyncore - from medusa import resolver, logger - from HTTPServer import zhttp_server, zhttp_handler -+from HTTPS_Server import zhttps_server, zhttps_handler - from PCGIServer import PCGIServer - from FCGIServer import FCGIServer - from FTPServer import FTPServer -</pre> -</li> -<li><p class="first">Copy <tt class="literal"><span class="pre">$ZSSL/ZServer/medusa/https_server.py</span></tt> to -<tt class="literal"><span class="pre">$ZOPE/ZServer/medusa</span></tt>.</p> -</li> -<li><p class="first">Stop Zope, if it is running.</p> -</li> -<li><p class="first">Start Zope with ZServerSSL thusly:</p> -<pre class="literal-block"> -./starts -X -f 9021 -w 9080 -W 9081 -y 9443 -Y 9444 -</pre> -<p>This starts the following:</p> -<ul class="simple"> -<li>an FTP server on port 9021</li> -<li>a HTTP server on port 9080</li> -<li>a WebDAV-source server on port 9081</li> -<li>a HTTPS server on port 9443</li> -<li>a WebDAV-source-over-HTTPS server on port 9444</li> -</ul> -</li> -</ol> -</div> -<div class="section" id="testing"> -<h1><a class="toc-backref" href="#id5" name="testing">Testing</a></h1> -<p>Below, we assume your Zope server is running on <tt class="literal"><span class="pre">localhost</span></tt>.</p> -<div class="section" id="https"> -<h2><a class="toc-backref" href="#id6" name="https">HTTPS</a></h2> -<p>This testing is done with Mozilla 1.1 on FreeBSD.</p> -<ol class="arabic simple"> -<li>With a browser, connect to <a class="reference" href="https://localhost:9443/">https://localhost:9443/</a>. Browse -around. Check out your browser's HTTPS informational screens.</li> -<li>Connect to <a class="reference" href="https://localhost:9443/manage">https://localhost:9443/manage</a>. Verify that you can -access Zope's management functionality.</li> -</ol> -</div> -<div class="section" id="webdav-over-https"> -<h2><a class="toc-backref" href="#id7" name="webdav-over-https">WebDAV-over-HTTPS</a></h2> -<p>This testing is done with Cadaver 0.21.0 on FreeBSD.</p> -<pre class="literal-block"> -$ cadaver https://localhost:9443/ -WARNING: Untrusted server certificate presented: -Issued to: M2Crypto, SG -Issued by: M2Crypto, SG -Do you wish to accept the certificate? (y/n) y -dav:/> ls -Listing collection `/': succeeded. -Coll: Channels 0 Jun 19 00:04 -Coll: Control_Panel 0 Jun 6 00:13 -Coll: Examples 0 Jun 6 00:12 -Coll: catalog 0 Jun 12 11:53 -Coll: ngps 0 Jun 16 15:34 -Coll: portal 0 Jun 21 15:21 -Coll: skunk 0 Jun 18 21:18 -Coll: temp_folder 0 Jun 22 17:57 -Coll: zope 0 Jun 20 15:27 - acl_users 0 Dec 30 1998 - browser_id_manager 0 Jun 6 00:12 - default.css 3037 Jun 21 16:38 - error_log 0 Jun 6 00:12 - index_html 313 Jun 12 13:36 - portal0 0 Jun 21 15:21 - session_data_manager 0 Jun 6 00:12 - standard_error_message 1365 Jan 21 2001 - standard_html_footer 50 Jun 12 12:30 - standard_html_header 80 Jan 21 2001 - standard_template.pt 282 Jun 6 00:12 - zsyncer 0 Jun 17 15:28 -dav:/> quit -Connection to `localhost' closed. -$ -</pre> -</div> -<div class="section" id="webdav-source-over-https"> -<h2><a class="toc-backref" href="#id8" name="webdav-source-over-https">WebDAV-Source-over-HTTPS</a></h2> -<p>This testing is done with Mozilla 1.1 on FreeBSD.</p> -<ol class="arabic simple"> -<li>Open the Mozilla Composer window.</li> -<li>Click "File", "Open Web Location". A dialog box appears.</li> -<li>Enter <tt class="literal"><span class="pre">https://localhost:9444/index_html</span></tt> for the URL.</li> -<li>Select "Open in new Composer window."</li> -<li>Click "Open". A new Composer window will open with <tt class="literal"><span class="pre">index_html</span></tt> -loaded.</li> -</ol> -</div> -<div class="section" id="python-with-m2crypto"> -<h2><a class="toc-backref" href="#id9" name="python-with-m2crypto">Python with M2Crypto</a></h2> -<p>This testing is done with M2Crypto 0.11 and Python 2.2.2 on FreeBSD.</p> -<div class="section" id="id1"> -<h3><a class="toc-backref" href="#id10" name="id1">HTTPS</a></h3> -<pre class="doctest-block"> ->>> from M2Crypto import Rand, SSL, m2urllib ->>> url = m2urllib.FancyURLopener() ->>> url.addheader('Connection', 'close') ->>> u = url.open('https://127.0.0.1:9443/') -send: 'GET / HTTP/1.1\r\nHost: 127.0.0.1:9443\r\nAccept-Encoding: identity\r\nUser-agent: Python-urllib/1.15\r\nConnection: close\r\n\r\n' -reply: 'HTTP/1.1 200 OK\r\n' -header: Server: ZServerSSL/0.11 -header: Date: Sun, 22 Jun 2003 13:42:34 GMT -header: Connection: close -header: Content-Type: text/html -header: Etag: -header: Content-Length: 535 ->>> while 1: -... data = u.read() -... if not data: break -... print(data) -... -</pre> -<pre class="literal-block"> -<html><head> -<base href="https://127.0.0.1:9443/" /> -<title>Zope</title></head><body bgcolor="#FFFFFF"> - -<h1>NgPS Desktop Portal</h1> - -&nbsp;&nbsp;So many hacks.<br> -&nbsp;&nbsp;So little time.<br> - -<h2>Link Farm</h2> -<ul> -<li><a href="http://localhost:8080/portal">Portal</a></li> -<li><a href="http://localhost/">Local Apache Home Page</a></li> -</ul> - -<hr><a href="http://www.zope.org/Credits" target="_top"><img src="https://127.0.0.1:9443/p_/ZopeButton" width="115" height="50" border="0" alt="Powered by Zope" /></a></body></html> -</pre> -<pre class="doctest-block"> ->>> u.close() ->>> -</pre> -</div> -<div class="section" id="xmlrpc-over-https"> -<h3><a class="toc-backref" href="#id11" name="xmlrpc-over-https">XMLRPC-over-HTTPS</a></h3> -<pre class="doctest-block"> ->>> from M2Crypto.m2xmlrpclib import Server, SSL_Transport ->>> zs = Server('https://127.0.0.1:9443/', SSL_Transport()) ->>> print(zs.propertyMap()) -[{'type': 'string', 'id': 'title', 'mode': 'w'}] ->>> -</pre> -</div> -</div> -</div> -<div class="section" id="conclusion"> -<h1><a class="toc-backref" href="#id12" name="conclusion">Conclusion</a></h1> -<p>Well, it works! ;-)</p> -</div> -</div> -</body> -</html> diff --git a/doc/ZServerSSL-HOWTO.rst b/doc/ZServerSSL-HOWTO.rst new file mode 100644 index 0000000..d09426d --- /dev/null +++ b/doc/ZServerSSL-HOWTO.rst @@ -0,0 +1,237 @@ +.. _zserverssl-howto: + +ZServerSSL-HOWTO +################ + +:author: Pheng Siong Ng <ngps@post1.com> +:copyright: © 2000, 2001 by Ng Pheng Siong. +:date: 2003-06-22 + +.. contents:: + :backlinks: entry + +.. sectnum:: + :suffix: . + +Introduction +============ + +ZServerSSL adds to Zope's ZServer the following: + +- HTTPS server +- WebDAV-source-over-HTTPS server + +With the HTTPS server, ZServerSSL also provides WebDAV-over-HTTPS and +XMLRPC-over-HTTPS access to Zope. + +These instructions apply to both Un\*x and Windows installations of Zope +2.6.1. To avoid cluttering the presentation, Windows pathnames are shown +in Un\*x fashion. + +Preparation +=========== + +#. Download M2Crypto 0.11, contained in the file ``m2crypto-0.11.zip``. +#. Unpack ``m2crypto-0.11.zip``. This will create a directory + ``m2crypto-0.11``. Henceforth, we refer to this directory as ``$M2``. +#. Install M2Crypto per the instructions in ``$M2/INSTALL``. + +The ZServerSSL distribution is in ``$M2/demo/Zope``. We shall refer to +this directory as ``$ZSSL``. + +Installation +============ + +Below, we refer to your Zope top-level directory as ``$ZOPE``. + +#. Copy ``$ZSSL/z2s.py`` into ``$ZOPE``. + +#. Depending on your operating system, modify ``$ZOPE/start`` or + ``$ZOPE/start.bat`` to invoke ``$ZOPE/z2s.py``, instead of + ``$ZOPE/z2.py``. The files ``$ZSSL/starts`` and ``$ZSSL/starts.bat`` + serve as examples. + +#. Copy ``$ZSSL/dh1024.pem`` into ``$ZOPE``. This file contains + Diffie-Hellman parameters for use by the SSL protocol. + +#. Copy ``$ZSSL/randpool.dat`` into ``$ZOPE``. This file contains seed + material for the OpenSSL PRNG. Alternatively, create + ``$ZOPE/randpool.dat`` thusly:: + + $ dd if=/dev/urandom of=randpool.dat bs=1024 count=1 + +#. Copy ``$ZSSL/ca.pem`` to ``$ZOPE``. This file contains an + example Certification Authority (CA) certificate. For + information on operating your own CA, see :ref:`howto-ca` or + one of numerous similar documents available on the web. + +#. Copy ``$ZSSL/server.pem`` to ``$ZOPE``. This file contains an RSA key + pair and its X.509v3 certificate issued by the above CA. You may also + create your own key/certificate bundle. + +#. Copy ``$ZSSL/ZServer/HTTPS_Server.py`` to ``$ZOPE/ZServer``. + +#. Copy ``$ZSSL/ZServer/__init__.py`` to ``$ZOPE/ZServer``. This + overwrites the existing ``$ZOPE/ZServer/__init__.py``. Alternatively, + apply the following patch to ``$ZOPE/ZServer/__init__.py``:: + + --- __init__.py.org Sat Jun 21 23:20:41 2003 + +++ __init__.py Tue Jan 7 23:30:53 2003 + @@ -84,6 +84,7 @@ + import asyncore + from medusa import resolver, logger + from HTTPServer import zhttp_server, zhttp_handler + +from HTTPS_Server import zhttps_server, zhttps_handler + from PCGIServer import PCGIServer + from FCGIServer import FCGIServer + from FTPServer import FTPServer + +#. Copy ``$ZSSL/ZServer/medusa/https_server.py`` to + ``$ZOPE/ZServer/medusa``. + +#. Stop Zope, if it is running. + +#. Start Zope with ZServerSSL thusly:: + + ./starts -X -f 9021 -w 9080 -W 9081 -y 9443 -Y 9444 + + This starts the following: + + - an FTP server on port 9021 + - a HTTP server on port 9080 + - a WebDAV-source server on port 9081 + - a HTTPS server on port 9443 + - a WebDAV-source-over-HTTPS server on port 9444 + +Testing +======= + +Below, we assume your Zope server is running on ``localhost``. + +HTTPS +===== + +This testing is done with Mozilla 1.1 on FreeBSD. + +#. With a browser, connect to https://localhost:9443/. Browse around. + Check out your browser's HTTPS informational screens. +#. Connect to https://localhost:9443/manage. Verify that you can access + Zope's management functionality. + +WebDAV-over-HTTPS +================= + +This testing is done with Cadaver 0.21.0 on FreeBSD.:: + + $ cadaver https://localhost:9443/ + WARNING: Untrusted server certificate presented: + Issued to: M2Crypto, SG + Issued by: M2Crypto, SG + Do you wish to accept the certificate? (y/n) y + dav:/> ls + Listing collection `/': succeeded. + Coll: Channels 0 Jun 19 00:04 + Coll: Control_Panel 0 Jun 6 00:13 + Coll: Examples 0 Jun 6 00:12 + Coll: catalog 0 Jun 12 11:53 + Coll: ngps 0 Jun 16 15:34 + Coll: portal 0 Jun 21 15:21 + Coll: skunk 0 Jun 18 21:18 + Coll: temp_folder 0 Jun 22 17:57 + Coll: zope 0 Jun 20 15:27 + acl_users 0 Dec 30 1998 + browser_id_manager 0 Jun 6 00:12 + default.css 3037 Jun 21 16:38 + error_log 0 Jun 6 00:12 + index_html 313 Jun 12 13:36 + portal0 0 Jun 21 15:21 + session_data_manager 0 Jun 6 00:12 + standard_error_message 1365 Jan 21 2001 + standard_html_footer 50 Jun 12 12:30 + standard_html_header 80 Jan 21 2001 + standard_template.pt 282 Jun 6 00:12 + zsyncer 0 Jun 17 15:28 + dav:/> quit + Connection to `localhost' closed. + $ + + +WebDAV-Source-over-HTTPS +======================== + +This testing is done with Mozilla 1.1 on FreeBSD. + +#. Open the Mozilla Composer window. +#. Click "File", "Open Web Location". A dialog box appears. +#. Enter ``https://localhost:9444/index_html`` for the URL. +#. Select "Open in new Composer window." +#. Click "Open". A new Composer window will open with ``index_html`` + loaded. + +Python with M2Crypto +==================== + +This testing is done with M2Crypto 0.11 and Python 2.2.2 on FreeBSD. + +HTTPS +===== + +:: + + >>> from M2Crypto import Rand, SSL, m2urllib + >>> url = m2urllib.FancyURLopener() + >>> url.addheader('Connection', 'close') + >>> u = url.open('https://127.0.0.1:9443/') + send: 'GET / HTTP/1.1\r\nHost: 127.0.0.1:9443\r\nAccept-Encoding: identity\r\nUser-agent: Python-urllib/1.15\r\nConnection: close\r\n\r\n' + reply: 'HTTP/1.1 200 OK\r\n' + header: Server: ZServerSSL/0.11 + header: Date: Sun, 22 Jun 2003 13:42:34 GMT + header: Connection: close + header: Content-Type: text/html + header: Etag: + header: Content-Length: 535 + >>> while 1: + ... data = u.read() + ... if not data: break + ... print(data) + ... + +:: + + <html><head> + <base href="https://127.0.0.1:9443/" /> + <title>Zope</title></head><body bgcolor="#FFFFFF"> + + <h1>NgPS Desktop Portal</h1> + + So many hacks.<br> + So little time.<br> + + <h2>Link Farm</h2> + <ul> + <li><a href="http://localhost:8080/portal">Portal</a></li> + <li><a href="http://localhost/">Local Apache Home Page</a></li> + </ul> + + <hr><a href="http://www.zope.org/Credits" target="_top"><img src="https://127.0.0.1:9443/p_/ZopeButton" width="115" height="50" border="0" alt="Powered by Zope" /></a></body></html> + +:: + + >>> u.close() + >>> + +XMLRPC-over-HTTPS +================= + +:: + + >>> from M2Crypto.m2xmlrpclib import Server, SSL_Transport + >>> zs = Server('https://127.0.0.1:9443/', SSL_Transport()) + >>> print(zs.propertyMap()) + [{'type': 'string', 'id': 'title', 'mode': 'w'}] + >>> + +Conclusion +========== + +Well, it works! ;-) diff --git a/doc/howto.ca.html b/doc/howto.ca.html deleted file mode 100644 index f33d7e5..0000000 --- a/doc/howto.ca.html +++ /dev/null @@ -1,891 +0,0 @@ -<HTML -><HEAD -><TITLE ->HOWTO: Creating your own CA with OpenSSL</TITLE -><META -NAME="GENERATOR" -CONTENT="Modular DocBook HTML Stylesheet Version 1.64 -"></HEAD -><BODY -CLASS="ARTICLE" -BGCOLOR="#FFFFFF" -TEXT="#000000" -LINK="#0000FF" -VLINK="#840084" -ALINK="#0000FF" -><DIV -CLASS="ARTICLE" -><DIV -CLASS="TITLEPAGE" -><H1 -CLASS="TITLE" -><A -NAME="AEN2" ->HOWTO: Creating your own CA with OpenSSL</A -></H1 -><H3 -CLASS="AUTHOR" -><A -NAME="AEN4" ->Pheng Siong Ng</A -></H3 -><DIV -CLASS="AFFILIATION" -><DIV -CLASS="ADDRESS" -><P -CLASS="ADDRESS" ->ngps@post1.com</P -></DIV -></DIV -><P -CLASS="COPYRIGHT" ->Copyright © 2000, 2001 by Ng Pheng Siong.</P -><DIV -CLASS="REVHISTORY" -><TABLE -WIDTH="100%" -BORDER="0" -><TR -><TH -ALIGN="LEFT" -VALIGN="TOP" -COLSPAN="3" -><B ->Revision History</B -></TH -></TR -><TR -><TD -ALIGN="LEFT" ->Revision $Revision: 1.1 $</TD -><TD -ALIGN="LEFT" ->$Date: 2003/06/22 16:41:18 $</TD -><TD -ALIGN="LEFT" -></TD -></TR -><TR -><TD -ALIGN="LEFT" -COLSPAN="3" -></TD -></TR -></TABLE -></DIV -><HR></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="INTRODUCTION" ->Introduction</A -></H1 -><P ->This is a HOWTO on creating your own <I -CLASS="EMPHASIS" ->certification - authority</I -> (<I -CLASS="EMPHASIS" ->CA</I ->) with OpenSSL. - </P -><P ->I last created a CA about a year ago, when I began work on <A -HREF="http://chandlerproject.org/Projects/MeTooCrypto" -TARGET="_top" ->M2Crypto</A -> and needed - certificates for the SSL bits. I accepted the tools' default settings - then, e.g., certificate validity of 365 days; this meant that my - certificates, including my CA's certificate, have now expired. - </P -><P ->Since I am using these certificates for M2Crypto's demonstration - programs (and I have forgotten the passphrase to the CA's private key), - I decided to discard the old CA and start afresh. I also decided to - document the process, hence this HOWTO. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="PROCEDURE" ->The Procedure</A -></H1 -><P ->I use <TT -CLASS="FILENAME" ->CA.pl</TT ->, a Perl program written by - Steve Hanson and bundled with OpenSSL. - </P -><P ->The following are the steps to create a CA: - </P -><DIV -CLASS="PROCEDURE" -><OL -TYPE="1" -><LI -><P ->Choose a directory to do your CA work. All commands are executed - within this directory. Let's call the directory <TT -CLASS="FILENAME" ->demo</TT ->. - </P -></LI -><LI -><P ->Copy <TT -CLASS="FILENAME" ->CA.pl</TT -> and <TT -CLASS="FILENAME" ->openssl.cnf</TT -> - into <TT -CLASS="FILENAME" ->demo</TT ->. - </P -></LI -><LI -><P ->Apply the following patch to <TT -CLASS="FILENAME" ->CA.pl</TT ->, which - allows it to generate a CA certificate with a validity period of 1095 days, - i.e., 3 years: - </P -><PRE -CLASS="PROGRAMLISTING" -> --- CA.pl.org Sat Mar 31 12:40:13 2001 - +++ CA.pl Sat Mar 31 12:41:15 2001 - @@ -97,7 +97,7 @@ - } else { - print "Making CA certificate ...\n"; - system ("$REQ -new -x509 -keyout " . - - "${CATOP}/private/$CAKEY -out ${CATOP}/$CACERT $DAYS"); - + "${CATOP}/private/$CAKEY -out ${CATOP}/$CACERT -days 1095"); - $RET=$?; - } - } - </PRE -></LI -><LI -><P ->Create a new CA like this: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->./CA.pl -newca - </B -></TT -> - A certificate filename (or enter to create) <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><enter></I -></TT -></B -></TT -> - - Making CA certificate ... - Using configuration from openssl.cnf - Generating a 1024 bit RSA private key - ............++++++ - ......................++++++ - writing new private key to './demoCA/private/cakey.pem' - Enter PEM pass phrase: <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><secret passphrase here></I -></TT -></B -></TT -> - Verifying password - Enter PEM pass phrase: <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><secret passphrase again></I -></TT -></B -></TT -> - ----- - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [AU]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->SG</I -></TT -></B -></TT -> - State or Province Name (full name) [Some-State]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Locality Name (eg, city) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT ->. - Organization Name (eg, company) [Internet Widgits Pty Ltd]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->DemoCA</I -></TT -></B -></TT -> - Organizational Unit Name (eg, section) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Common Name (eg, YOUR name) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->DemoCA Certificate Master</I -></TT -></B -></TT -> - Email Address []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->certmaster@democa.dom</I -></TT -></B -></TT -> - </PRE -><P ->This creates a new CA in the directory <TT -CLASS="FILENAME" ->demoCA</TT ->. - The CA's self-signed certificate is in - <TT -CLASS="FILENAME" ->demoCA/cacert.pem</TT -> and its RSA key pair is in - <TT -CLASS="FILENAME" ->demoCA/private/cakey.pem</TT ->. - </P -><P -><TT -CLASS="FILENAME" ->demoCA/private/cakey.pem</TT -> looks like this: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->cat demoCA/private/cakey.pem - </B -></TT -> - -----BEGIN RSA PRIVATE KEY----- - Proc-Type: 4,ENCRYPTED - DEK-Info: DES-EDE3-CBC,19973A9DBBB601BA - - eOq9WFScNiI4/UWEUaSnGTKpJv2JYuMD3HwQox2Q3Cd4zGqVjJ6gF3exa5126cKf - X/bMVnwbPpuFZPiAIvaLyCjT6pYeXTBbSzs7/GQnvEOv+nYnDUFWi0Qm92qLk0uy - pFi/M1aWheN3vir2ZlAw+DW0bOOZhj8tC7Co7lMYb0YE271b6/YRPZCwQ3GXAHUJ - +aMYxlUDrK45aCUa/1CZDzTgk7h9cDgx2QJSIvYMYytCfI3zsuZMJS8/4OXLL0bI - lKmAc1dwB3DqGJt5XK4WJesiNfdxeCNEgAcYtEAgYZTPIApU+kTgTCIxJl2nMW7j - ax+Q1z7g+4MpgG20WD633D4z4dTlDdz+dnLi0rvuvxiwt+dUhrqiML1tyi+Z6EBH - jU4/cLBWev3rYfrlp4x8J9mDte0YKOk3t0wQOHqRetTsIfdtjnFp/Hu3qDmTCWjD - z/g7PPoO/bg/B877J9WBPbL/1hXXFYo88M+2aGlPOgDcFdiOqbLb2DCscohMbbVr - A4mgiy2kwWfIE73qiyV7yyG8FlRvr1iib+jbT3LTGf743utYAAs7HNGuOUObhoyt - jYvBD7ACn35P5YX7KTqvqErwdijxYCaNBCnvmRtmYSaNw9Kv1UJTxc5Vx7YLwIPk - E9KyBgKI7vPOjWBZ27+zOvNycmv1ciNtpALAw4bWtXnhCDVTHaVDy34OkheMzNCg - 2cjcBFzOkMIjcI03KbTQXOFIQGlsTWXGzkNf/zBQ+KksT1MCj+zBXSCvlDASMckg - kef21pGgUqPF14gKGfWX3sV4bjc1vbrRwq6zlG3nMuYqR5MtJJY9eQ== - -----END RSA PRIVATE KEY----- - </PRE -></LI -><LI -><P ->Next, generate a certificate request. - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->./CA.pl -newreq - </B -></TT -> - Using configuration from openssl.cnf - Generating a 1024 bit RSA private key - ..........++++++ - ..............++++++ - writing new private key to 'newreq.pem' - Enter PEM pass phrase: <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><another secret passphrase here></I -></TT -></B -></TT -> - Verifying password - Enter PEM pass phrase: <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><another secret passphrase again></I -></TT -></B -></TT -> - ----- - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [AU]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->SG</I -></TT -></B -></TT -> - State or Province Name (full name) [Some-State]:.<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Locality Name (eg, city) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Organization Name (eg, company) [Internet Widgits Pty Ltd]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->M2Crypto</I -></TT -></B -></TT -> - Organizational Unit Name (eg, section) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Common Name (eg, YOUR name) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->localhost</I -></TT -></B -></TT -> - Email Address []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->admin@server.example.dom</I -></TT -></B -></TT -> - - Please enter the following 'extra' attributes - to be sent with your certificate request - A challenge password []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><enter></I -></TT -></B -></TT -> - An optional company name []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><enter></I -></TT -></B -></TT -> - Request (and private key) is in newreq.pem - </PRE -><P ->The certificate request and private key in <TT -CLASS="FILENAME" ->newreq.pem</TT -> looks like this: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->cat newreq.pem - </B -></TT -> - -----BEGIN RSA PRIVATE KEY----- - Proc-Type: 4,ENCRYPTED - DEK-Info: DES-EDE3-CBC,41B2874DF3D02DD4 - - mg611EoVkLEooSTv+qTM0Ddmm/M1jE/Jy5RD/sc3LSMhuGu9xc26OgsTJmkQuIAh - J/B4lAw8G59VTG6DykeEtrG0rUBx4bggc7PKbFuiN423YjJODWcHvVgnPOzXMQt+ - lY4tPl5+217MRHyx2NsWGrpkQNdu3GeSPOVMl3jeQiaXupONbwQ7rj42+X/VtAJP - W4D1NNwu8aGCPyShsEXHc/fI1WDpphYWke97pOjIZVQESFZOPty5HjIYZux4U+td - W81xODtq2ecJXc8fn2Wpa9y5VD1LT7oJksOuL1+Z04OVaeUe4x0swM17HlBm2kVt - fe/C/L6kN27MwZhE331VjtTjSGl4/gknqQDbLOtqT06f3OISsDJETm2itllyhgzv - C6Fi3N03rGFmKectijC+tws5k+P+HRG6sai33usk8xPokJqA+HYSWPz1XVlpRmv4 - kdjQOdST7ovU62mOTgf3ARcduPPwuzTfxOlYONe5NioO1APVHBrInQwcpLkpOTQR - vI4roIN+b75/nihUWGUJn/nbbBa2Yl0N5Gs1Tyiy9Z+CcRT2TfWKBBFlEUIFl7Mb - J9fTV3DI+k+akbR4il1NkQ8EcSmCr3WpA0I9n0EHI7ZVpVaHxc0sqaPFl8YGdFHq - 1Qk53C/w6+qPpDzT3yKFmG2LZytAAM1czvb6RbNRJJP2ZrpBwn/h99sUTo/yPfxY - nueYmFJDm0uVNtG0icXGNUfSfnjKNTtHPAgyKGetRIC3kgJz/bo2w7EI6iEjBAzK - l5TRm4x6ZJxwuXXMiJCehMMd8TC8ybwWO4AO19B3ebFFeTVsUgxSGA== - -----END RSA PRIVATE KEY----- - -----BEGIN CERTIFICATE REQUEST----- - MIIBnTCCAQYCAQAwXTELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIw - EAYDVQQDEwlsb2NhbGhvc3QxJzAlBgkqhkiG9w0BCQEWGGFkbWluQHNlcnZlci5l - eGFtcGxlLmRvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAr1nYY1Qrll1r - uB/FqlCRrr5nvupdIN+3wF7q915tvEQoc74bnu6b8IbbGRMhzdzmvQ4SzFfVEAuM - MuTHeybPq5th7YDrTNizKKxOBnqE2KYuX9X22A1Kh49soJJFg6kPb9MUgiZBiMlv - tb7K3CHfgw5WagWnLl8Lb+ccvKZZl+8CAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4GB - AHpoRp5YS55CZpy+wdigQEwjL/wSluvo+WjtpvP0YoBMJu4VMKeZi405R7o8oEwi - PdlrrliKNknFmHKIaCKTLRcU59ScA6ADEIWUzqmUzP5Cs6jrSRo3NKfg1bd09D1K - 9rsQkRc9Urv9mRBIsredGnYECNeRaK5R1yzpOowninXC - -----END CERTIFICATE REQUEST----- - </PRE -><P ->Decoding the certificate request gives the following: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl req -text -noout < newreq.pem - </B -></TT -> - Using configuration from /usr/local/pkg/openssl/openssl.cnf - Certificate Request: - Data: - Version: 0 (0x0) - Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: - 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: - 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: - 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: - c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: - 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: - 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: - 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: - 0b:6f:e7:1c:bc:a6:59:97:ef - Exponent: 65537 (0x10001) - Attributes: - a0:00 - Signature Algorithm: md5WithRSAEncryption - 7a:68:46:9e:58:4b:9e:42:66:9c:be:c1:d8:a0:40:4c:23:2f: - fc:12:96:eb:e8:f9:68:ed:a6:f3:f4:62:80:4c:26:ee:15:30: - a7:99:8b:8d:39:47:ba:3c:a0:4c:22:3d:d9:6b:ae:58:8a:36: - 49:c5:98:72:88:68:22:93:2d:17:14:e7:d4:9c:03:a0:03:10: - 85:94:ce:a9:94:cc:fe:42:b3:a8:eb:49:1a:37:34:a7:e0:d5: - b7:74:f4:3d:4a:f6:bb:10:91:17:3d:52:bb:fd:99:10:48:b2: - b7:9d:1a:76:04:08:d7:91:68:ae:51:d7:2c:e9:3a:8c:27:8a: - 75:c2 - </PRE -></LI -><LI -><P ->Now, sign the certificate request: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->./CA.pl -sign - </B -></TT -> - Using configuration from openssl.cnf - Enter PEM pass phrase: <TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><CA's passphrase></I -></TT -></B -></TT -> - Check that the request matches the signature - Signature ok - The Subjects Distinguished Name is as follows - countryName :PRINTABLE:'SG' - organizationName :PRINTABLE:'M2Crypto' - commonName :PRINTABLE:'localhost' - emailAddress :IA5STRING:'admin@server.example.dom' - Certificate is to be certified until Mar 31 02:57:30 2002 GMT (365 days) - Sign the certificate? [y/n]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->y</I -></TT -></B -></TT -> - - - 1 out of 1 certificate requests certified, commit? [y/n]<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->y</I -></TT -></B -></TT -> - Write out database with 1 new entries - Data Base Updated - Signed certificate is in newcert.pem - </PRE -><P -><TT -CLASS="FILENAME" ->newcert.pem</TT -> looks like this: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->cat newcert.pem - </B -></TT -> - Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: md5WithRSAEncryption - Issuer: C=SG, O=DemoCA, CN=DemoCA Certificate Master/Email=certmaster@democa.dom - Validity - Not Before: Mar 31 02:57:30 2001 GMT - Not After : Mar 31 02:57:30 2002 GMT - Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: - 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: - 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: - 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: - c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: - 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: - 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: - 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: - 0b:6f:e7:1c:bc:a6:59:97:ef - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: md5WithRSAEncryption - Issuer: C=SG, O=DemoCA, CN=DemoCA Certificate Master/Email=certmaster@democa.dom - Validity - Not Before: Mar 31 02:57:30 2001 GMT - Not After : Mar 31 02:57:30 2002 GMT - Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: - 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: - 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: - 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: - c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: - 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: - 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: - 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: - 0b:6f:e7:1c:bc:a6:59:97:ef - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - X509v3 Subject Key Identifier: - B3:D6:89:88:2F:B1:15:40:EC:0A:C0:30:35:3A:B7:DA:72:73:1B:4D - X509v3 Authority Key Identifier: - keyid:F9:6A:A6:34:97:6B:BC:BB:5A:17:0D:19:FC:62:21:0B:00:B5:0E:29 - DirName:/C=SG/O=DemoCA/CN=DemoCA Certificate Master/Email=certmaster@democa.dom - serial:00 - - Signature Algorithm: md5WithRSAEncryption - </PRE -></LI -><LI -><P ->In certain situations, e.g., where your certificate and - private key are to be used in an unattended SSL server, you may wish to - not encrypt the private key, i.e., leave the key in the clear. This - decision should be governed by your site's security policy and threat - model, of course. - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl rsa < newkey.pem > newkey2.pem - </B -></TT -> - read RSA key - Enter PEM pass phrase:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><secret passphrase here></I -></TT -></B -></TT -> - writing RSA key - </PRE -><P -><TT -CLASS="FILENAME" ->newkey2.pem</TT -> looks like this: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->cat newkey2.pem - </B -></TT -> - -----BEGIN RSA PRIVATE KEY----- - MIICXgIBAAKBgQCvWdhjVCuWXWu4H8WqUJGuvme+6l0g37fAXur3Xm28RChzvhue - 7pvwhtsZEyHN3Oa9DhLMV9UQC4wy5Md7Js+rm2HtgOtM2LMorE4GeoTYpi5f1fbY - DUqHj2ygkkWDqQ9v0xSCJkGIyW+1vsrcId+DDlZqBacuXwtv5xy8plmX7wIDAQAB - AoGAbAkU8w3W1Qu15Hle1bJSL7GMReoreqeblOBmMAZz4by0l6sXZXJpjWXo86f/ - +dASMYTMPC4ZTYtv06N07AFbjL+kDfqDMTfzQkYMHp1LAq1Ihbq1rHWSBH5n3ekq - KiY8JKpv8DR5Po1iKaXJFuDByGDENJwYbSRSpSK3P+vkWWECQQDkEUE/ZPqqqZkQ - 2iWRPAsCbEID8SAraQl3DdCLYs/GgARfmmj4yUHEwkys9Jo1H8k4BdxugmaUwNi5 - YQ/CVzrXAkEAxNO80ArbGxPUmr11GHG/bGBYj1DUBkHZSc7dgxZdtUCLGNxQnNsg - Iwq3n6j1sUzS3UW6abQ8bivYNOUcMKJAqQJBANQxFaLU4b/NQaODQ3aoBZpAfP9L - 5eFdvbet+7zjt2r5CpikgkwOfAmDuXEltx/8LevY0CllW+nErx9zJgVrwUsCQQCu - 76H5JiznPBDSF2FjgHWqVVdgyW4owY3mU739LHvNBLicN/RN9VPy0Suy8/CqzKT9 - lWPBXzf2k3FuUdNkRlFBAkEAmpXoybuiFR2S5Bma/ax96lVs0/VihhfC1zZP/X/F - Br77+h9dIul+2DnyOl50zu0Sdzst1/7ay4JSDHyiBCMGSQ== - -----END RSA PRIVATE KEY----- - </PRE -></LI -></OL -></DIV -><P ->That's it! The certificate, <TT -CLASS="FILENAME" ->newcert.pem</TT ->, and - the private key - <TT -CLASS="FILENAME" ->newkey.pem</TT -> (encrypted) or - <TT -CLASS="FILENAME" ->newkey2.pem</TT -> (unencrypted) - are now ready to be used. - You may wish to rename the files to more intuitive names. - </P -><P ->You should also keep the CA's certificate <TT -CLASS="FILENAME" ->demo/cacert.pem - </TT -> handy for use when developing and deploying SSL or S/MIME - applications. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="CONCLUSION" ->Conclusion</A -></H1 -><P ->We've walked through the basic steps in the creation of a CA and - certificates using the tools that come with OpenSSL. We did not cover more - advanced topics such as constraining a certificate to be SSL-only or - S/MIME-only. - </P -><P ->There exist several HOWTOs similar to this one on the net. This one - is written specifically to facilitate discussions in my other HOWTOs - on developing SSL and S/MIME applications in - <A -HREF="http://www.python.org" -TARGET="_top" ->Python</A -> using - <A -HREF="http://chandlerproject.org/Projects/MeTooCrypto" -TARGET="_top" ->M2Crypto</A ->. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="ID-KLUDGE" -></A -></H1 -><P -> <TT -CLASS="LITERAL" ->$Id:howto.ca.html 583 2007-10-01 19:23:12Z heikki $</TT -> - </P -></DIV -></DIV -></BODY -></HTML -> diff --git a/doc/howto.ca.rst b/doc/howto.ca.rst new file mode 100644 index 0000000..3c6b460 --- /dev/null +++ b/doc/howto.ca.rst @@ -0,0 +1,368 @@ +.. _howto-ca: + +HOWTO: Creating your own CA with OpenSSL +######################################## + +:author: Pheng Siong Ng <ngps@post1.com> +:copyright: © 2000, 2001 by Ng Pheng Siong. + +Introduction +============ + +This is a HOWTO on creating your own *certification authority* (*CA*) +with OpenSSL. + +I last created a CA about a year ago, when I began work on +`M2Crypto <https://gitlab.com/m2crypto/m2crypto/>`__ and needed +certificates for the SSL bits. I accepted the tools' default +settings then, e.g., certificate validity of 365 days; this meant +that my certificates, including my CA's certificate, have now +expired. + +Since I am using these certificates for M2Crypto's demonstration +programs (and I have forgotten the passphrase to the CA's private +key), I decided to discard the old CA and start afresh. I also +decided to document the process, hence this HOWTO. + +The Procedure +============= + +I use ``CA.pl``, a Perl program written by Steve Hanson and bundled with +OpenSSL. + +The following are the steps to create a CA: + +1. Choose a directory to do your CA work. All commands are executed + within this directory. Let's call the directory ``demo``. + +2. Copy ``CA.pl`` and ``openssl.cnf`` into ``demo``. + +3. Apply the following patch to ``CA.pl``, which allows it to generate a + CA certificate with a validity period of 1095 days, i.e., + 3 years:: + + --- CA.pl.org Sat Mar 31 12:40:13 2001 + +++ CA.pl Sat Mar 31 12:41:15 2001 + @@ -97,7 +97,7 @@ + } else { + print "Making CA certificate ...\n"; + system ("$REQ -new -x509 -keyout " . + - "${CATOP}/private/$CAKEY -out ${CATOP}/$CACERT $DAYS"); + + "${CATOP}/private/$CAKEY -out ${CATOP}/$CACERT -days 1095"); + $RET=$?; + } + } + + +4. Create a new CA like this:: + + ./CA.pl -newca + + A certificate filename (or enter to create) <enter> + + Making CA certificate ... + Using configuration from openssl.cnf + Generating a 1024 bit RSA private key + ............++++++ + ......................++++++ + writing new private key to './demoCA/private/cakey.pem' + Enter PEM pass phrase: <secret passphrase here> + Verifying password - Enter PEM pass phrase: <secret passphrase again> + ----- + You are about to be asked to enter information that will be incorporated + into your certificate request. + What you are about to enter is what is called a Distinguished Name or a DN. + There are quite a few fields but you can leave some blank + For some fields there will be a default value, + If you enter '.', the field will be left blank. + ----- + Country Name (2 letter code) [AU]:SG + State or Province Name (full name) [Some-State]:. + Locality Name (eg, city) []:.. + Organization Name (eg, company) [Internet Widgits Pty Ltd]:DemoCA + Organizational Unit Name (eg, section) []:. + Common Name (eg, YOUR name) []:DemoCA Certificate Master + Email Address []:certmaster@democa.dom + + This creates a new CA in the directory ``demoCA``. The CA's + self-signed certificate is in ``demoCA/cacert.pem`` and its RSA key + pair is in ``demoCA/private/cakey.pem``. + + ``demoCA/private/cakey.pem`` looks like this:: + + cat demoCA/private/cakey.pem + + -----BEGIN RSA PRIVATE KEY----- + Proc-Type: 4,ENCRYPTED + DEK-Info: DES-EDE3-CBC,19973A9DBBB601BA + + eOq9WFScNiI4/UWEUaSnGTKpJv2JYuMD3HwQox2Q3Cd4zGqVjJ6gF3exa5126cKf + X/bMVnwbPpuFZPiAIvaLyCjT6pYeXTBbSzs7/GQnvEOv+nYnDUFWi0Qm92qLk0uy + pFi/M1aWheN3vir2ZlAw+DW0bOOZhj8tC7Co7lMYb0YE271b6/YRPZCwQ3GXAHUJ + +aMYxlUDrK45aCUa/1CZDzTgk7h9cDgx2QJSIvYMYytCfI3zsuZMJS8/4OXLL0bI + lKmAc1dwB3DqGJt5XK4WJesiNfdxeCNEgAcYtEAgYZTPIApU+kTgTCIxJl2nMW7j + ax+Q1z7g+4MpgG20WD633D4z4dTlDdz+dnLi0rvuvxiwt+dUhrqiML1tyi+Z6EBH + jU4/cLBWev3rYfrlp4x8J9mDte0YKOk3t0wQOHqRetTsIfdtjnFp/Hu3qDmTCWjD + z/g7PPoO/bg/B877J9WBPbL/1hXXFYo88M+2aGlPOgDcFdiOqbLb2DCscohMbbVr + A4mgiy2kwWfIE73qiyV7yyG8FlRvr1iib+jbT3LTGf743utYAAs7HNGuOUObhoyt + jYvBD7ACn35P5YX7KTqvqErwdijxYCaNBCnvmRtmYSaNw9Kv1UJTxc5Vx7YLwIPk + E9KyBgKI7vPOjWBZ27+zOvNycmv1ciNtpALAw4bWtXnhCDVTHaVDy34OkheMzNCg + 2cjcBFzOkMIjcI03KbTQXOFIQGlsTWXGzkNf/zBQ+KksT1MCj+zBXSCvlDASMckg + kef21pGgUqPF14gKGfWX3sV4bjc1vbrRwq6zlG3nMuYqR5MtJJY9eQ== + -----END RSA PRIVATE KEY----- + + +5. Next, generate a certificate request:: + + ./CA.pl -newreq + + Using configuration from openssl.cnf + Generating a 1024 bit RSA private key + ..........++++++ + ..............++++++ + writing new private key to 'newreq.pem' + Enter PEM pass phrase: <another secret passphrase here> + Verifying password - Enter PEM pass phrase: <another secret passphrase again> + ----- + You are about to be asked to enter information that will be incorporated + into your certificate request. + What you are about to enter is what is called a Distinguished Name or a DN. + There are quite a few fields but you can leave some blank + For some fields there will be a default value, + If you enter '.', the field will be left blank. + ----- + Country Name (2 letter code) [AU]:SG + State or Province Name (full name) [Some-State]:.. + Locality Name (eg, city) []:. + Organization Name (eg, company) [Internet Widgits Pty Ltd]:M2Crypto + Organizational Unit Name (eg, section) []:. + Common Name (eg, YOUR name) []:localhost + Email Address []:admin@server.example.dom + + Please enter the following 'extra' attributes + to be sent with your certificate request + A challenge password []:<enter> + An optional company name []:<enter> + Request (and private key) is in newreq.pem + +\ + + The certificate request and private key in ``newreq.pem`` looks like + this:: + + cat newreq.pem + + -----BEGIN RSA PRIVATE KEY----- + Proc-Type: 4,ENCRYPTED + DEK-Info: DES-EDE3-CBC,41B2874DF3D02DD4 + + mg611EoVkLEooSTv+qTM0Ddmm/M1jE/Jy5RD/sc3LSMhuGu9xc26OgsTJmkQuIAh + J/B4lAw8G59VTG6DykeEtrG0rUBx4bggc7PKbFuiN423YjJODWcHvVgnPOzXMQt+ + lY4tPl5+217MRHyx2NsWGrpkQNdu3GeSPOVMl3jeQiaXupONbwQ7rj42+X/VtAJP + W4D1NNwu8aGCPyShsEXHc/fI1WDpphYWke97pOjIZVQESFZOPty5HjIYZux4U+td + W81xODtq2ecJXc8fn2Wpa9y5VD1LT7oJksOuL1+Z04OVaeUe4x0swM17HlBm2kVt + fe/C/L6kN27MwZhE331VjtTjSGl4/gknqQDbLOtqT06f3OISsDJETm2itllyhgzv + C6Fi3N03rGFmKectijC+tws5k+P+HRG6sai33usk8xPokJqA+HYSWPz1XVlpRmv4 + kdjQOdST7ovU62mOTgf3ARcduPPwuzTfxOlYONe5NioO1APVHBrInQwcpLkpOTQR + vI4roIN+b75/nihUWGUJn/nbbBa2Yl0N5Gs1Tyiy9Z+CcRT2TfWKBBFlEUIFl7Mb + J9fTV3DI+k+akbR4il1NkQ8EcSmCr3WpA0I9n0EHI7ZVpVaHxc0sqaPFl8YGdFHq + 1Qk53C/w6+qPpDzT3yKFmG2LZytAAM1czvb6RbNRJJP2ZrpBwn/h99sUTo/yPfxY + nueYmFJDm0uVNtG0icXGNUfSfnjKNTtHPAgyKGetRIC3kgJz/bo2w7EI6iEjBAzK + l5TRm4x6ZJxwuXXMiJCehMMd8TC8ybwWO4AO19B3ebFFeTVsUgxSGA== + -----END RSA PRIVATE KEY----- + -----BEGIN CERTIFICATE REQUEST----- + MIIBnTCCAQYCAQAwXTELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIw + EAYDVQQDEwlsb2NhbGhvc3QxJzAlBgkqhkiG9w0BCQEWGGFkbWluQHNlcnZlci5l + eGFtcGxlLmRvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAr1nYY1Qrll1r + uB/FqlCRrr5nvupdIN+3wF7q915tvEQoc74bnu6b8IbbGRMhzdzmvQ4SzFfVEAuM + MuTHeybPq5th7YDrTNizKKxOBnqE2KYuX9X22A1Kh49soJJFg6kPb9MUgiZBiMlv + tb7K3CHfgw5WagWnLl8Lb+ccvKZZl+8CAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4GB + AHpoRp5YS55CZpy+wdigQEwjL/wSluvo+WjtpvP0YoBMJu4VMKeZi405R7o8oEwi + PdlrrliKNknFmHKIaCKTLRcU59ScA6ADEIWUzqmUzP5Cs6jrSRo3NKfg1bd09D1K + 9rsQkRc9Urv9mRBIsredGnYECNeRaK5R1yzpOowninXC + -----END CERTIFICATE REQUEST----- + +\ + + Decoding the certificate request gives the following:: + + openssl req -text -noout < newreq.pem + + Using configuration from /usr/local/pkg/openssl/openssl.cnf + Certificate Request: + Data: + Version: 0 (0x0) + Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: + 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: + 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: + 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: + c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: + 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: + 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: + 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: + 0b:6f:e7:1c:bc:a6:59:97:ef + Exponent: 65537 (0x10001) + Attributes: + a0:00 + Signature Algorithm: md5WithRSAEncryption + 7a:68:46:9e:58:4b:9e:42:66:9c:be:c1:d8:a0:40:4c:23:2f: + fc:12:96:eb:e8:f9:68:ed:a6:f3:f4:62:80:4c:26:ee:15:30: + a7:99:8b:8d:39:47:ba:3c:a0:4c:22:3d:d9:6b:ae:58:8a:36: + 49:c5:98:72:88:68:22:93:2d:17:14:e7:d4:9c:03:a0:03:10: + 85:94:ce:a9:94:cc:fe:42:b3:a8:eb:49:1a:37:34:a7:e0:d5: + b7:74:f4:3d:4a:f6:bb:10:91:17:3d:52:bb:fd:99:10:48:b2: + b7:9d:1a:76:04:08:d7:91:68:ae:51:d7:2c:e9:3a:8c:27:8a: + 75:c2 + +6. Now, sign the certificate request:: + + ./CA.pl -sign + + Using configuration from openssl.cnf + Enter PEM pass phrase: <CA's passphrase> + Check that the request matches the signature + Signature ok + The Subjects Distinguished Name is as follows + countryName :PRINTABLE:'SG' + organizationName :PRINTABLE:'M2Crypto' + commonName :PRINTABLE:'localhost' + emailAddress :IA5STRING:'admin@server.example.dom' + Certificate is to be certified until Mar 31 02:57:30 2002 GMT (365 days) + Sign the certificate? [y/n]:y + + + 1 out of 1 certificate requests certified, commit? [y/n]y + Write out database with 1 new entries + Data Base Updated + Signed certificate is in newcert.pem + +\ + + ``newcert.pem`` looks like this:: + + cat newcert.pem + + Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=SG, O=DemoCA, CN=DemoCA Certificate Master/Email=certmaster@democa.dom + Validity + Not Before: Mar 31 02:57:30 2001 GMT + Not After : Mar 31 02:57:30 2002 GMT + Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: + 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: + 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: + 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: + c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: + 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: + 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: + 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: + 0b:6f:e7:1c:bc:a6:59:97:ef + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=SG, O=DemoCA, CN=DemoCA Certificate Master/Email=certmaster@democa.dom + Validity + Not Before: Mar 31 02:57:30 2001 GMT + Not After : Mar 31 02:57:30 2002 GMT + Subject: C=SG, O=M2Crypto, CN=localhost/Email=admin@server.example.dom + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:af:59:d8:63:54:2b:96:5d:6b:b8:1f:c5:aa:50: + 91:ae:be:67:be:ea:5d:20:df:b7:c0:5e:ea:f7:5e: + 6d:bc:44:28:73:be:1b:9e:ee:9b:f0:86:db:19:13: + 21:cd:dc:e6:bd:0e:12:cc:57:d5:10:0b:8c:32:e4: + c7:7b:26:cf:ab:9b:61:ed:80:eb:4c:d8:b3:28:ac: + 4e:06:7a:84:d8:a6:2e:5f:d5:f6:d8:0d:4a:87:8f: + 6c:a0:92:45:83:a9:0f:6f:d3:14:82:26:41:88:c9: + 6f:b5:be:ca:dc:21:df:83:0e:56:6a:05:a7:2e:5f: + 0b:6f:e7:1c:bc:a6:59:97:ef + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + B3:D6:89:88:2F:B1:15:40:EC:0A:C0:30:35:3A:B7:DA:72:73:1B:4D + X509v3 Authority Key Identifier: + keyid:F9:6A:A6:34:97:6B:BC:BB:5A:17:0D:19:FC:62:21:0B:00:B5:0E:29 + DirName:/C=SG/O=DemoCA/CN=DemoCA Certificate Master/Email=certmaster@democa.dom + serial:00 + + Signature Algorithm: md5WithRSAEncryption + +7. In certain situations, e.g., where your certificate and private key + are to be used in an unattended SSL server, you may wish to not + encrypt the private key, i.e., leave the key in the clear. This + decision should be governed by your site's security policy and threat + model, of course:: + + openssl rsa < newkey.pem > newkey2.pem + + read RSA key + Enter PEM pass phrase:<secret passphrase here> + writing RSA key + + ``newkey2.pem`` looks like this:: + + cat newkey2.pem + + -----BEGIN RSA PRIVATE KEY----- + MIICXgIBAAKBgQCvWdhjVCuWXWu4H8WqUJGuvme+6l0g37fAXur3Xm28RChzvhue + 7pvwhtsZEyHN3Oa9DhLMV9UQC4wy5Md7Js+rm2HtgOtM2LMorE4GeoTYpi5f1fbY + DUqHj2ygkkWDqQ9v0xSCJkGIyW+1vsrcId+DDlZqBacuXwtv5xy8plmX7wIDAQAB + AoGAbAkU8w3W1Qu15Hle1bJSL7GMReoreqeblOBmMAZz4by0l6sXZXJpjWXo86f/ + +dASMYTMPC4ZTYtv06N07AFbjL+kDfqDMTfzQkYMHp1LAq1Ihbq1rHWSBH5n3ekq + KiY8JKpv8DR5Po1iKaXJFuDByGDENJwYbSRSpSK3P+vkWWECQQDkEUE/ZPqqqZkQ + 2iWRPAsCbEID8SAraQl3DdCLYs/GgARfmmj4yUHEwkys9Jo1H8k4BdxugmaUwNi5 + YQ/CVzrXAkEAxNO80ArbGxPUmr11GHG/bGBYj1DUBkHZSc7dgxZdtUCLGNxQnNsg + Iwq3n6j1sUzS3UW6abQ8bivYNOUcMKJAqQJBANQxFaLU4b/NQaODQ3aoBZpAfP9L + 5eFdvbet+7zjt2r5CpikgkwOfAmDuXEltx/8LevY0CllW+nErx9zJgVrwUsCQQCu + 76H5JiznPBDSF2FjgHWqVVdgyW4owY3mU739LHvNBLicN/RN9VPy0Suy8/CqzKT9 + lWPBXzf2k3FuUdNkRlFBAkEAmpXoybuiFR2S5Bma/ax96lVs0/VihhfC1zZP/X/F + Br77+h9dIul+2DnyOl50zu0Sdzst1/7ay4JSDHyiBCMGSQ== + -----END RSA PRIVATE KEY----- + + +That's it! The certificate, ``newcert.pem``, and the private key - +``newkey.pem`` (encrypted) or ``newkey2.pem`` (unencrypted) - are now +ready to be used. You may wish to rename the files to more intuitive +names. + +You should also keep the CA's certificate ``demo/cacert.pem`` handy +for use when developing and deploying SSL or S/MIME applications. + +Conclusion +========== + +We've walked through the basic steps in the creation of a CA and +certificates using the tools that come with OpenSSL. We did not cover +more advanced topics such as constraining a certificate to be SSL-only +or S/MIME-only. + +There exist several HOWTOs similar to this one on the net. This one is +written specifically to facilitate discussions in my other HOWTOs on +developing SSL and S/MIME applications in +`Python <http://www.python.org>`__ using +`M2Crypto <https://gitlab.com/m2crypto/m2crypto/>`__. + diff --git a/doc/howto.smime.html b/doc/howto.smime.html deleted file mode 100644 index 00c5346..0000000 --- a/doc/howto.smime.html +++ /dev/null @@ -1,1573 +0,0 @@ -<HTML -><HEAD -><TITLE ->HOWTO: Programming S/MIME in Python with M2Crypto</TITLE -><META -NAME="GENERATOR" -CONTENT="Modular DocBook HTML Stylesheet Version 1.64 -"></HEAD -><BODY -CLASS="ARTICLE" -BGCOLOR="#FFFFFF" -TEXT="#000000" -LINK="#0000FF" -VLINK="#840084" -ALINK="#0000FF" -><DIV -CLASS="ARTICLE" -><DIV -CLASS="TITLEPAGE" -><H1 -CLASS="TITLE" -><A -NAME="AEN2" ->HOWTO: Programming S/MIME in Python with M2Crypto</A -></H1 -><H3 -CLASS="AUTHOR" -><A -NAME="AEN4" ->Pheng Siong Ng</A -></H3 -><DIV -CLASS="AFFILIATION" -><DIV -CLASS="ADDRESS" -><P -CLASS="ADDRESS" ->ngps@post1.com</P -></DIV -></DIV -><P -CLASS="COPYRIGHT" ->Copyright © 2000, 2001 by Ng Pheng Siong.</P -><DIV -CLASS="REVHISTORY" -><TABLE -WIDTH="100%" -BORDER="0" -><TR -><TH -ALIGN="LEFT" -VALIGN="TOP" -COLSPAN="3" -><B ->Revision History</B -></TH -></TR -><TR -><TD -ALIGN="LEFT" ->Revision $Id$</TD -><TD -ALIGN="LEFT" -></TD -><TD -ALIGN="LEFT" -></TD -></TR -><TR -><TD -ALIGN="LEFT" -COLSPAN="3" -></TD -></TR -></TABLE -></DIV -><HR></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="INTRODUCTION" ->Introduction</A -></H1 -><P -><A -HREF="http://chandlerproject.org/Projects/MeTooCrypto" -TARGET="_top" ->M2Crypto</A -> - is a <A -HREF="http://www.python.org" -TARGET="_top" ->Python</A -> - interface to <A -HREF="http://www.openssl.org" -TARGET="_top" ->OpenSSL</A ->. It makes - available to the Python programmer SSL functionality to implement clients - and servers, S/MIME v2, RSA, DSA, DH, symmetric ciphers, message digests and - HMACs. - </P -><P ->This document demonstrates programming S/MIME with M2Crypto. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="SMIME" ->S/MIME</A -></H1 -><P ->S/MIME - Secure Multipurpose Internet Mail Extensions - [<SPAN -CLASS="CITATION" ->RFC 2311, RFC 2312</SPAN ->] - provides a - consistent way to send and receive secure MIME data. Based on the popular - Internet MIME standard, S/MIME provides the following cryptographic security - services for electronic messaging applications - - <I -CLASS="EMPHASIS" ->authentication</I ->, <I -CLASS="EMPHASIS" ->message integrity </I -> - and <I -CLASS="EMPHASIS" ->non-repudiation of origin </I -> (using <I -CLASS="EMPHASIS" ->digital - signatures</I ->), and <I -CLASS="EMPHASIS" ->privacy </I -> and <I -CLASS="EMPHASIS" ->data - security</I -> (using <I -CLASS="EMPHASIS" ->encryption</I ->). - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="KEYS-AND-CERTIFICATES" ->Keys and Certificates</A -></H1 -><P ->To create an S/MIME-signed message, you need an RSA key pair - (this consists of a public key and a private key) and an X.509 - certificate of said public key. - </P -><P ->To create an S/MIME-encrypted message, you need an X.509 - certificate for each recipient. - </P -><P ->To create an S/MIME-signed <I -CLASS="EMPHASIS" ->and</I -> -encrypted - message, first create a signed message, then encrypt the signed - message with the recipients' certificates. - </P -><P ->You may generate key pairs and obtain certificates by using a - commercial <I -CLASS="EMPHASIS" ->certification authority</I -> service. - </P -><P ->You can also do so using freely-available software. For many - purposes, e.g., automated S/MIME messaging by system administration - processes, this approach is cheap and effective. - </P -><P ->We now work through using OpenSSL to generate key pairs and - certificates. This assumes you have OpenSSL installed properly on your - system. - </P -><P ->First, we generate an X.509 certificate to be used for signing: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl req -newkey rsa:1024 -nodes -x509 -days 365 -out signer.pem - </B -></TT -> - Using configuration from /usr/local/pkg/openssl/openssl.cnf - Generating a 1024 bit RSA private key - ..++++++ - ....................++++++ - writing new private key to 'privkey.pem' - ----- - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [AU]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->SG</I -></TT -></B -></TT -> - State or Province Name (full name) [Some-State]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Locality Name (eg, city) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Organization Name (eg, company) [Internet Widgits Pty Ltd]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->M2Crypto</I -></TT -></B -></TT -> - Organizational Unit Name (eg, section) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Common Name (eg, YOUR name) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->S/MIME Sender</I -></TT -></B -></TT -> - Email Address []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->sender@example.dom</I -></TT -></B -></TT -> - </PRE -><P ->This generates a 1024-bit RSA key pair, unencrypted, into - <TT -CLASS="FILENAME" ->privkey.pem</TT ->; it also generates a self-signed X.509 - certificate for the public key into <TT -CLASS="FILENAME" ->signer.pem</TT ->. The - certificate is valid for 365 days, i.e., a year. - </P -><P ->Let's rename <TT -CLASS="FILENAME" ->privkey.pem</TT -> so that we know it is - a companion of <TT -CLASS="FILENAME" ->signer.pem</TT ->'s: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->mv privkey.pem signer_key.pem</B -></TT -> - </PRE -><P ->To verify the content of <TT -CLASS="FILENAME" ->signer.pem</TT ->, execute the - following: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl x509 -noout -text -in signer.pem - </B -></TT -> - Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: md5WithRSAEncryption - Issuer: C=SG, O=M2Crypto, CN=S/MIME Sender/Email=sender@example.dom - Validity - Not Before: Mar 24 12:56:16 2001 GMT - Not After : Mar 24 12:56:16 2002 GMT - Subject: C=SG, O=M2Crypto, CN=S/MIME Sender/Email=sender@example.dom - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:a9:d6:e2:b5:11:3b:ae:3c:e2:17:31:70:e1:6e: - 01:f4:19:6d:bd:2a:42:36:2b:37:34:e2:83:1d:0d: - 11:2e:b4:99:44:db:10:67:be:97:5f:5b:1a:26:33: - 46:23:2f:95:04:7a:35:da:9d:f9:26:88:39:9e:17: - cd:3e:eb:a8:19:8d:a8:2a:f1:43:da:55:a9:2e:2c: - 65:ed:04:71:42:ce:73:53:b8:ea:7e:c7:f0:23:c6: - 63:c5:5e:68:96:64:a7:b4:2a:94:26:76:eb:79:ea: - e3:4e:aa:82:09:4f:44:87:4a:12:62:b5:d7:1f:ca: - f2:ce:d5:ba:7e:1f:48:fd:b9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - 29:FB:38:B6:BF:E2:40:BB:FF:D5:71:D7:D5:C4:F0:83:1A:2B:C7:99 - X509v3 Authority Key Identifier: - keyid:29:FB:38:B6:BF:E2:40:BB:FF:D5:71:D7:D5:C4:F0:83:1A:2B:C7:99 - DirName:/C=SG/O=M2Crypto/CN=S/MIME Sender/Email=sender@example.dom - serial:00 - - X509v3 Basic Constraints: - CA:TRUE - Signature Algorithm: md5WithRSAEncryption - 68:c8:6b:1b:fa:7c:9a:39:35:76:18:15:c9:fd:89:97:62:db: - 7a:b0:2d:13:dd:97:e8:1b:7a:9f:22:27:83:24:9d:2e:56:ec: - 97:89:3c:ef:16:55:80:5a:18:7c:22:d0:f6:bb:e3:a4:e8:59: - 30:ff:99:5a:93:3e:ea:bc:ee:7f:8d:d6:7d:37:8c:ac:3d:74: - 80:ce:7a:99:ba:27:b9:2a:a3:71:fa:a5:25:ba:47:17:df:07: - 56:96:36:fd:60:b9:6c:96:06:e8:e3:7b:9f:4b:6a:95:71:a8: - 34:fc:fc:b5:88:8b:c4:3f:1e:24:f6:52:47:b2:7d:44:67:d9: - 83:e8 - </PRE -><P ->Next, we generate a self-signed X.509 certificate for the - recipient. Note that <TT -CLASS="FILENAME" ->privkey.pem</TT -> will be - recreated. - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl req -newkey rsa:1024 -nodes -x509 -days 365 -out recipient.pem - </B -></TT -> - Using configuration from /usr/local/pkg/openssl/openssl.cnf - Generating a 1024 bit RSA private key - .....................................++++++ - .................++++++ - writing new private key to 'privkey.pem' - ----- - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [AU]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->SG</I -></TT -></B -></TT -> - State or Province Name (full name) [Some-State]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Locality Name (eg, city) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Organization Name (eg, company) [Internet Widgits Pty Ltd]:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->M2Crypto</I -></TT -></B -></TT -> - Organizational Unit Name (eg, section) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->.</I -></TT -></B -></TT -> - Common Name (eg, YOUR name) []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->S/MIME Recipient</I -></TT -></B -></TT -> - Email Address []:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I ->recipient@example.dom</I -></TT -></B -></TT -> - </PRE -><P ->Again, rename <TT -CLASS="FILENAME" ->privkey.pem</TT ->: - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->mv privkey.pem recipient_key.pem</B -></TT -> - </PRE -><P ->In the examples to follow, S/MIME Sender, - <TT -CLASS="EMAIL" -><<A -HREF="mailto:sender@example.dom" ->sender@example.dom</A ->></TT ->, shall be the sender of S/MIME messages, - while S/MIME Recipient, <TT -CLASS="EMAIL" -><<A -HREF="mailto:recipient@example.dom" ->recipient@example.dom</A ->></TT ->, shall be the - recipient of S/MIME messages. - </P -><P ->Armed with the key pairs and certificates, we are now ready to begin - programming S/MIME in Python. - </P -><DIV -CLASS="NOTE" -><BLOCKQUOTE -CLASS="NOTE" -><P -><B ->Note: </B ->The private keys generated above are - <I -CLASS="EMPHASIS" ->not passphrase-protected</I ->, i.e., they are - <I -CLASS="EMPHASIS" ->in the clear</I ->. Anyone who has access to such a key can - generate S/MIME-signed messages with it, and decrypt S/MIME messages - encrypted to it's corresponding public key. - </P -><P ->We may passphrase-protect the keys, if we so choose. M2Crypto will - prompt the user for the passphrase when such a key is being loaded. - </P -></BLOCKQUOTE -></DIV -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="M2CRYPTO-SMIME" ->M2Crypto.SMIME</A -></H1 -><P ->The Python programmer accesses M2Crypto's S/MIME functionality - through class <TT -CLASS="CLASSNAME" ->SMIME</TT -> in the module - <TT -CLASS="CLASSNAME" ->M2Crypto.SMIME</TT ->. Typically, an <TT -CLASS="CLASSNAME" ->SMIME</TT -> - object is instantiated; the object is then set up for the intended - operation: sign, encrypt, decrypt or verify; finally, the operation is invoked on - the object. - </P -><P -><TT -CLASS="CLASSNAME" ->M2Crypto.SMIME</TT -> makes extensive use of - <TT -CLASS="CLASSNAME" ->M2Crypto.BIO</TT ->: <TT -CLASS="CLASSNAME" ->M2Crypto.BIO</TT -> - is a Python abstraction of the <TT -CLASS="CLASSNAME" ->BIO</TT -> abstraction in - OpenSSL. A commonly used <TT -CLASS="CLASSNAME" ->BIO</TT -> abstraction in M2Crypto is - <TT -CLASS="CLASSNAME" ->M2Crypto.BIO.MemoryBuffer</TT ->, which implements a - memory-based file-like object, similar to Python's own - <TT -CLASS="CLASSNAME" ->StringIO</TT ->. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="SIGN" ->Sign</A -></H1 -><P ->The following code demonstrates how to generate an S/MIME-signed - message. <TT -CLASS="FILENAME" ->randpool.dat</TT -> contains random data which is - used to seed OpenSSL's pseudo-random number generator via M2Crypto. - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, Rand, SMIME - - def makebuf(text): - return BIO.MemoryBuffer(text) - - # Make a MemoryBuffer of the message. - buf = makebuf('a sign of our times') - - # Seed the PRNG. - Rand.load_file('randpool.dat', -1) - - # Instantiate an SMIME object; set it up; sign the buffer. - s = SMIME.SMIME() - s.load_key('signer_key.pem', 'signer.pem') - p7 = s.sign(buf, SMIME.PKCS7_DETACHED) - </PRE -><P -><TT -CLASS="VARNAME" ->p7</TT -> now contains a <I -CLASS="EMPHASIS" ->PKCS #7 signature - blob</I -> wrapped in an <TT -CLASS="CLASSNAME" ->M2Crypto.SMIME.PKCS7</TT -> - object. Note that <TT -CLASS="VARNAME" ->buf</TT -> has been consumed by - <TT -CLASS="FUNCTION" ->sign()</TT -> and has to be recreated if it is to be used - again. - </P -><P ->We may now send the signed message via SMTP. In these examples, we - shall not do so; instead, we'll render the S/MIME output in - mail-friendly format, and pretend that our messages are sent and - received correctly. - </P -><PRE -CLASS="PROGRAMLISTING" -> # Recreate buf. - buf = makebuf('a sign of our times') - - # Output p7 in mail-friendly format. - out = BIO.MemoryBuffer() - out.write('From: sender@example.dom\n') - out.write('To: recipient@example.dom\n') - out.write('Subject: M2Crypto S/MIME testing\n') - s.write(out, p7, buf) - - print(out.read()) - - # Save the PRNG's state. - Rand.save_file('randpool.dat') - </PRE -><P ->Here's the output: - </P -><PRE -CLASS="SCREEN" -> From: sender@example.dom - To: recipient@example.dom - Subject: M2Crypto S/MIME testing - MIME-Version: 1.0 - Content-Type: multipart/signed ; protocol="application/x-pkcs7-signature" ; micalg=sha1 ; boundary="----3C93156FC7B4EBF49FE9C7DB7F503087" - - This is an S/MIME signed message - - ------3C93156FC7B4EBF49FE9C7DB7F503087 - a sign of our times - ------3C93156FC7B4EBF49FE9C7DB7F503087 - Content-Type: application/x-pkcs7-signature; name="smime.p7s" - Content-Transfer-Encoding: base64 - Content-Disposition: attachment; filename="smime.p7s" - - MIIE8AYJKoZIhvcNAQcCoIIE4TCCBN0CAQExCzAJBgUrDgMCGgUAMCIGCSqGSIb3 - DQEHAaAVBBNhIHNpZ24gb2Ygb3VyIHRpbWVzoIIC5zCCAuMwggJMoAMCAQICAQAw - DQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRv - MRYwFAYDVQQDEw1TL01JTUUgU2VuZGVyMSEwHwYJKoZIhvcNAQkBFhJzZW5kZXJA - ZXhhbXBsZS5kb20wHhcNMDEwMzMxMTE0MDMzWhcNMDIwMzMxMTE0MDMzWjBbMQsw - CQYDVQQGEwJTRzERMA8GA1UEChMITTJDcnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBT - ZW5kZXIxITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmRvbTCBnzANBgkq - hkiG9w0BAQEFAAOBjQAwgYkCgYEA5c5Tj1CHTSOxa1q2q0FYiwMWYHptJpJcvtZm - UwrgU5sHrA8OnCM0cDXEj0KPf3cfNjHffB8HWMzI4UEgNmFXQNsxoGZ+iqwxLlNj - y9Mh7eFW/Bjq5hNXbouSlQ0rWBRkoxV64y+t6lQehb32WfYXQbKFxFJSXzSxOx3R - 8YhSPd0CAwEAAaOBtjCBszAdBgNVHQ4EFgQUXOyolL1t4jaBwZFRM7MS8nBLzUow - gYMGA1UdIwR8MHqAFFzsqJS9beI2gcGRUTOzEvJwS81KoV+kXTBbMQswCQYDVQQG - EwJTRzERMA8GA1UEChMITTJDcnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBTZW5kZXIx - ITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmRvbYIBADAMBgNVHRMEBTAD - AQH/MA0GCSqGSIb3DQEBBAUAA4GBAHo3DrCHR86fSTVAvfiXdSswWqKtCEhUHRdC - TLFGl4hDk2GyZxaFuqZwiURz/H7nMicymI2wkz8H/wyHFg8G3BIehURpj2v/ZWXY - eovbgS7EZALVVkDj4hNl/IIHWd6Gtv1UODf7URbxtl3hQ9/eTWITrefT1heuPnar - 8czydsOLMYIBujCCAbYCAQEwYDBbMQswCQYDVQQGEwJTRzERMA8GA1UEChMITTJD - cnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBTZW5kZXIxITAfBgkqhkiG9w0BCQEWEnNl - bmRlckBleGFtcGxlLmRvbQIBADAJBgUrDgMCGgUAoIGxMBgGCSqGSIb3DQEJAzEL - BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTAxMDMzMTExNDUwMlowIwYJKoZI - hvcNAQkEMRYEFOoeRUd8ExIYXfQq8BTFuKWrSP3iMFIGCSqGSIb3DQEJDzFFMEMw - CgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsO - AwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIGAQpU8hFUtLCF6hO2t - ec9EYJ/Imqqiiw+BxWxkUUVT81Vbjwdn9JST6+sztM5JRP2ZW+b4txEjZriYC8f3 - kv95YMTGbIsuWkJ93GrbvqoJ/CxO23r9WWRnZEm/1EZN9ZmlrYqzBTxnNRmP3Dhj - cW8kzZwH+2/2zz2G7x1HxRWH95A= - - ------3C93156FC7B4EBF49FE9C7DB7F503087-- - </PRE -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="VERIFY" ->Verify</A -></H1 -><P ->Assume the above output has been saved into <TT -CLASS="FILENAME" ->sign.p7</TT ->. - Let's now verify the signature: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import SMIME, X509 - - # Instantiate an SMIME object. - s = SMIME.SMIME() - - # Load the signer's cert. - x509 = X509.load_cert('signer.pem') - sk = X509.X509_Stack() - sk.push(x509) - s.set_x509_stack(sk) - - # Load the signer's CA cert. In this case, because the signer's - # cert is self-signed, it is the signer's cert itself. - st = X509.X509_Store() - st.load_info('signer.pem') - s.set_x509_store(st) - - # Load the data, verify it. - p7, data = SMIME.smime_load_pkcs7('sign.p7') - v = s.verify(p7, data) - print(v) - print(data) - print(data.read()) - </PRE -><P ->Here's the output of the above program: - </P -><PRE -CLASS="SCREEN" -> a sign of our times - <M2Crypto.BIO.BIO instance at 0x822012c> - a sign of our times - </PRE -><P ->Suppose, instead of loading <TT -CLASS="FILENAME" ->signer.pem</TT -> above, - we load <TT -CLASS="FILENAME" ->recipient.pem</TT ->. That is, we do a global - substitution of <TT -CLASS="LITERAL" ->recipient.pem</TT -> for - <TT -CLASS="LITERAL" ->signer.pem</TT -> in the above program. Here's the modified - program's output: - </P -><PRE -CLASS="SCREEN" -> Traceback (most recent call last): - File "./verify.py", line 22, in ? - v = s.verify(p7) - File "/usr/local/home/ngps/prog/m2/M2Crypto/SMIME.py", line 205, in verify - raise SMIME_Error, Err.get_error() - M2Crypto.SMIME.SMIME_Error: 312:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:213:Verify error:self signed certificate - </PRE -><P ->As displayed, the error is generated by line 213 of OpenSSL's - <TT -CLASS="FILENAME" ->pk7_smime.c</TT -> (as of OpenSSL 0.9.6); if you are a - C programmer, you may wish to look up the C source to explore OpenSSL's - S/MIME implementation and understand why the error message is worded thus. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="ENCRYPT" ->Encrypt</A -></H1 -><P ->We now demonstrate how to generate an S/MIME-encrypted message: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, Rand, SMIME, X509 - - def makebuf(text): - return BIO.MemoryBuffer(text) - - # Make a MemoryBuffer of the message. - buf = makebuf('a sign of our times') - - # Seed the PRNG. - Rand.load_file('randpool.dat', -1) - - # Instantiate an SMIME object. - s = SMIME.SMIME() - - # Load target cert to encrypt to. - x509 = X509.load_cert('recipient.pem') - sk = X509.X509_Stack() - sk.push(x509) - s.set_x509_stack(sk) - - # Set cipher: 3-key triple-DES in CBC mode. - s.set_cipher(SMIME.Cipher('des_ede3_cbc')) - - # Encrypt the buffer. - p7 = s.encrypt(buf) - - # Output p7 in mail-friendly format. - out = BIO.MemoryBuffer() - out.write('From: sender@example.dom\n') - out.write('To: recipient@example.dom\n') - out.write('Subject: M2Crypto S/MIME testing\n') - s.write(out, p7) - - print(out.read()) - - # Save the PRNG's state. - Rand.save_file('randpool.dat') - </PRE -><P ->Here's the output of the above program: - </P -><PRE -CLASS="SCREEN" -> From: sender@example.dom - To: recipient@example.dom - Subject: M2Crypto S/MIME testing - MIME-Version: 1.0 - Content-Disposition: attachment; filename="smime.p7m" - Content-Type: application/x-pkcs7-mime; name="smime.p7m" - Content-Transfer-Encoding: base64 - - MIIBVwYJKoZIhvcNAQcDoIIBSDCCAUQCAQAxggEAMIH9AgEAMGYwYTELMAkGA1UE - BhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRkwFwYDVQQDExBTL01JTUUgUmVjaXBp - ZW50MSQwIgYJKoZIhvcNAQkBFhVyZWNpcGllbnRAZXhhbXBsZS5kb20CAQAwDQYJ - KoZIhvcNAQEBBQAEgYCBaXZ+qjpBEZwdP7gjfzfAtQitESyMwo3i+LBOw6sSDir6 - FlNDPCnkrTvqDX3Rt6X6vBtTCYOm+qiN7ujPkOU61cN7h8dvHR8YW9+0IPY80/W0 - lZ/HihSRgwTNd7LnxUUcPx8YV1id0dlmP0Hz+Lg+mHf6rqaR//JcYhX9vW4XvjA7 - BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECMN+qya6ADywgBgHr9Jkhwn5Gsdu7BwX - nIQfYTYcdL9I5Sk= - </PRE -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="DECRYPT" ->Decrypt</A -></H1 -><P ->Assume the above output has been saved into <TT -CLASS="FILENAME" ->encrypt.p7</TT ->. - Decrypt the message thusly: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, SMIME, X509 - - # Instantiate an SMIME object. - s = SMIME.SMIME() - - # Load private key and cert. - s.load_key('recipient_key.pem', 'recipient.pem') - - # Load the encrypted data. - p7, data = SMIME.smime_load_pkcs7('encrypt.p7') - - # Decrypt p7. - out = s.decrypt(p7) - - print(out) - </PRE -><P ->Here's the output: - </P -><PRE -CLASS="SCREEN" -> a sign of our times - </PRE -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="SIGN-AND-ENCRYPT" ->Sign and Encrypt</A -></H1 -><P ->Here's how to generate an S/MIME-signed/encrypted message: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, Rand, SMIME, X509 - - def makebuf(text): - return BIO.MemoryBuffer(text) - - # Make a MemoryBuffer of the message. - buf = makebuf('a sign of our times') - - # Seed the PRNG. - Rand.load_file('randpool.dat', -1) - - # Instantiate an SMIME object. - s = SMIME.SMIME() - - # Load signer's key and cert. Sign the buffer. - s.load_key('signer_key.pem', 'signer.pem') - p7 = s.sign(buf) - - # Load target cert to encrypt the signed message to. - x509 = X509.load_cert('recipient.pem') - sk = X509.X509_Stack() - sk.push(x509) - s.set_x509_stack(sk) - - # Set cipher: 3-key triple-DES in CBC mode. - s.set_cipher(SMIME.Cipher('des_ede3_cbc')) - - # Create a temporary buffer. - tmp = BIO.MemoryBuffer() - - # Write the signed message into the temporary buffer. - s.write(tmp, p7) - - # Encrypt the temporary buffer. - p7 = s.encrypt(tmp) - - # Output p7 in mail-friendly format. - out = BIO.MemoryBuffer() - out.write('From: sender@example.dom\n') - out.write('To: recipient@example.dom\n') - out.write('Subject: M2Crypto S/MIME testing\n') - s.write(out, p7) - - print(out.read()) - - # Save the PRNG's state. - Rand.save_file('randpool.dat') - </PRE -><P ->Here's the output of the above program: - </P -><PRE -CLASS="SCREEN" -> From: sender@example.dom - To: recipient@example.dom - Subject: M2Crypto S/MIME testing - MIME-Version: 1.0 - Content-Disposition: attachment; filename="smime.p7m" - Content-Type: application/x-pkcs7-mime; name="smime.p7m" - Content-Transfer-Encoding: base64 - - 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 - </PRE -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="DECRYPT-AND-VERIFY" ->Decrypt and Verify</A -></H1 -><P ->Suppose the above output has been saved into - <TT -CLASS="FILENAME" ->se.p7</TT ->. The following demonstrates how to decrypt and - verify it: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, SMIME, X509 - - # Instantiate an SMIME object. - s = SMIME.SMIME() - - # Load private key and cert. - s.load_key('recipient_key.pem', 'recipient.pem') - - # Load the signed/encrypted data. - p7, data = SMIME.smime_load_pkcs7('se.p7') - - # After the above step, 'data' == None. - # Decrypt p7. 'out' now contains a PKCS #7 signed blob. - out = s.decrypt(p7) - - # Load the signer's cert. - x509 = X509.load_cert('signer.pem') - sk = X509.X509_Stack() - sk.push(x509) - s.set_x509_stack(sk) - - # Load the signer's CA cert. In this case, because the signer's - # cert is self-signed, it is the signer's cert itself. - st = X509.X509_Store() - st.load_info('signer.pem') - s.set_x509_store(st) - - # Recall 'out' contains a PKCS #7 blob. - # Transform 'out'; verify the resulting PKCS #7 blob. - p7_bio = BIO.MemoryBuffer(out) - p7, data = SMIME.smime_load_pkcs7_bio(p7_bio) - v = s.verify(p7) - - print(v) - </PRE -><P ->The output is as follows: - </P -><PRE -CLASS="SCREEN" -> a sign of our times - </PRE -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="SMTP" ->Sending S/MIME messages via SMTP</A -></H1 -><P ->In the above examples, we've assumed that our S/MIME messages - are sent and received automagically. The following is a Python function that - generates S/MIME-signed/encrypted messages and sends them via SMTP: - </P -><PRE -CLASS="PROGRAMLISTING" -> from M2Crypto import BIO, SMIME, X509 - import smtplib, string, sys - - def sendsmime(from_addr, to_addrs, subject, msg, from_key, from_cert=None, to_certs=None, smtpd='localhost'): - - msg_bio = BIO.MemoryBuffer(msg) - sign = from_key - encrypt = to_certs - - s = SMIME.SMIME() - if sign: - s.load_key(from_key, from_cert) - if encrypt: - p7 = s.sign(msg_bio, flags=SMIME.PKCS7_TEXT) - else: - p7 = s.sign(msg_bio, flags=SMIME.PKCS7_TEXT|SMIME.PKCS7_DETACHED) - msg_bio = BIO.MemoryBuffer(msg) # Recreate coz sign() has consumed it. - - if encrypt: - sk = X509.X509_Stack() - for x in to_certs: - sk.push(X509.load_cert(x)) - s.set_x509_stack(sk) - s.set_cipher(SMIME.Cipher('des_ede3_cbc')) - tmp_bio = BIO.MemoryBuffer() - if sign: - s.write(tmp_bio, p7) - else: - tmp_bio.write(msg) - p7 = s.encrypt(tmp_bio) - - out = BIO.MemoryBuffer() - out.write('From: %s\r\n' % from_addr) - out.write('To: %s\r\n' % string.join(to_addrs, ", ")) - out.write('Subject: %s\r\n' % subject) - if encrypt: - s.write(out, p7) - else: - if sign: - s.write(out, p7, msg_bio, SMIME.PKCS7_TEXT) - else: - out.write('\r\n') - out.write(msg) - out.close() - - smtp = smtplib.SMTP() - smtp.connect(smtpd) - smtp.sendmail(from_addr, to_addrs, out.read()) - smtp.quit() - </PRE -><P ->This function sends plain, S/MIME-signed, S/MIME-encrypted, - and S/MIME-signed/encrypted messages, depending on the parameters - <TT -CLASS="PARAMETER" -><I ->from_key</I -></TT -> and <TT -CLASS="PARAMETER" -><I ->to_certs</I -></TT ->. The - function's output interoperates with Netscape Messenger. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="VERIFYING-ORIGIN" ->Verifying origin of S/MIME messages</A -></H1 -><P ->In our examples above that decrypt or verify messages, we skipped - a step: verifying that the <TT -CLASS="LITERAL" ->from</TT -> address of the message - matches the <TT -CLASS="LITERAL" ->email address</TT -> attribute in the sender's - certificate. - </P -><P ->The premise of current X.509 certification practice is that the - CA is supposed to verify your identity, and to issue a certificate with - <TT -CLASS="LITERAL" ->email address</TT -> that matches your actual mail address. - (Verisign's March 2001 failure in identity verification resulting in - Microsoft certificates being issued to spoofers notwithstanding.) - </P -><P ->If you run your own CA, your certification practice is up to you, of - course, and it would probably be part of your security policy. - </P -><P ->Whether your S/MIME messaging application needs to verify the - <TT -CLASS="LITERAL" ->from</TT -> addresses of S/MIME messages depends on your - security policy and your system's threat model, as always. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="NETSCAPE-MESSENGER" ->Interoperating with Netscape Messenger</A -></H1 -><P ->Suppose S/MIME Recipient uses Netscape Messenger. To enable Messenger - to handle S/MIME messages from S/MIME Sender, S/MIME Recipient needs - to configure Messenger with his private key and certificate, as well as S/MIME - Sender's certificate. - </P -><DIV -CLASS="NOTE" -><BLOCKQUOTE -CLASS="NOTE" -><P -><B ->Note: </B ->Configuring Messenger's POP or IMAP settings so that it retrieves - mail correctly is beyond the scope of this HOWTO. - </P -></BLOCKQUOTE -></DIV -><P ->The following steps demonstrate how to import S/MIME Recipient's - private key and certificate for Messenger: - </P -><DIV -CLASS="PROCEDURE" -><OL -TYPE="1" -><LI -><P ->Transform S/MIME Recipient's private key and certificate into - <I -CLASS="EMPHASIS" ->PKCS #12</I -> format. - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl pkcs12 -export -in recipient.pem -inkey recipient_key.pem -name "S/MIME Recipient" -out recipient.p12 - </B -></TT -> - Enter Export Password:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><enter></I -></TT -></B -></TT -> - Verifying password - Enter Export Password:<TT -CLASS="USERINPUT" -><B -><TT -CLASS="REPLACEABLE" -><I -><enter></I -></TT -></B -></TT -> - </PRE -></LI -><LI -><P ->Start Messenger. - </P -></LI -><LI -><P ->Click on the (open) "lock" icon at the bottom left corner of - Messenger's window. This brings up the "Security Info" dialog box. - </P -></LI -><LI -><P ->Click on "Yours" under "Certificates". - </P -></LI -><LI -><P ->Select "Import a certificate", then pick - <TT -CLASS="FILENAME" ->recipient.p12</TT -> from the ensuing file selection dialog - box. - </P -></LI -></OL -></DIV -><P ->Next, you need to import <TT -CLASS="FILENAME" ->signer.pem</TT -> as a CA - certificate, so that Messenger will mark messages signed by S/MIME Sender as - "trusted": - </P -><DIV -CLASS="PROCEDURE" -><OL -TYPE="1" -><LI -><P ->Create a DER encoding of <TT -CLASS="FILENAME" ->signer.pem</TT ->. - </P -><PRE -CLASS="SCREEN" -> <TT -CLASS="USERINPUT" -><B ->openssl x509 -inform pem -outform der -in signer.pem -out signer.der - </B -></TT -> - </PRE -></LI -><LI -><P ->Install <TT -CLASS="FILENAME" ->signer.der</TT -> into Messenger as MIME type - <TT -CLASS="LITERAL" ->application/x-x509-ca-cert</TT ->. You do this by downloading - <TT -CLASS="FILENAME" ->signer.der</TT -> via Navigator from a HTTP or HTTPS server, - with the correct MIME type mapping. (You may use - <TT -CLASS="FILENAME" ->demo/ssl/https_srv.py</TT ->, bundled with M2Crypto, for - this purpose.) Follow the series of dialog boxes to accept - <TT -CLASS="FILENAME" ->signer.der</TT -> as a CA for certifying email users. - </P -></LI -></OL -></DIV -><P ->S/MIME Recipient is now able to decrypt and read S/MIME Sender's - messages with Messenger. Messenger will indicate that S/MIME Sender's - messages are signed, encrypted, or encrypted <I -CLASS="EMPHASIS" ->and</I -> - signed, as the case may be, via the "stamp" icon on the message window's - top right corner. - </P -><P ->Clicking on the "stamp" icon brings you to the Security Info dialog - box. Messenger informs you that the message is, say, encrypted with - 168-bit DES-EDE3-CBC and that it is digitally signed by the private key - corresponding to the public key contained in the certificate - <TT -CLASS="FILENAME" ->signer.pem</TT ->. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="MICROSOFT-OUTLOOK" ->Interoperating with Microsoft Outlook</A -></H1 -><P ->I do not know how to do this, as I do not use Outlook. (Nor do I use - Netscape Messenger, actually. I use Mutt, top dog of MUAs. ;-) Information on - how to configure Outlook with keys and certificates so that it handles - S/MIME mail is gratefully accepted. - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="ZSMIME" ->ZSmime</A -></H1 -><P ->ZSmime is a <A -HREF="http://www.zope.org" -TARGET="_top" ->Zope</A -> - <I -CLASS="EMPHASIS" ->product</I -> that enables Zope to generate - S/MIME-signed/encrypted messages. ZSmime demonstrates how to invoke - M2Crypto in a web application server extension. - </P -><P ->ZSmime has its own <A -HREF="http://sandbox.rulemaker.net/ngps/zope/zsmime/howto.html" -TARGET="_top" ->HOWTO</A -> - explaining its usage. (That HOWTO has some overlap in content with - this document.) - </P -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="RESOURCES" ->Resources</A -></H1 -><P -></P -><UL -><LI -STYLE="list-style-type: opencircle" -><P ->IETF S/MIME Working Group - - <A -HREF="http://www.imc.org/ietf-smime" -TARGET="_top" -> http://www.imc.org/ietf-smime</A -> - </P -></LI -><LI -STYLE="list-style-type: opencircle" -><P ->S/MIME and OpenPGP - - <A -HREF="http://www.imc.org/smime-pgpmime.html" -TARGET="_top" -> http://www.imc.org/smime-pgpmime.html</A -> - </P -></LI -><LI -STYLE="list-style-type: opencircle" -><P ->S/MIME Freeware Library - - <A -HREF="http://www.getronicsgov.com/hot/sfl_home.htm" -TARGET="_top" -> http://www.getronicsgov.com/hot/sfl_home.htm</A -> - </P -></LI -><LI -STYLE="list-style-type: opencircle" -><P ->Mozilla Network Security Services - - <A -HREF="http://www.mozilla.org/projects/security/pkg/nss" -TARGET="_top" -> http://www.mozilla.org/projects/security/pkg/nss</A -> - </P -></LI -><LI -STYLE="list-style-type: opencircle" -><P ->S/MIME Cracking Screen Saver - - <A -HREF="http://www.counterpane.com/smime.html" -TARGET="_top" -> http://www.counterpane.com/smime.html</A -> - </P -></LI -></UL -></DIV -><DIV -CLASS="SECT1" -><HR><H1 -CLASS="SECT1" -><A -NAME="ID-KLUDGE" -></A -></H1 -><P -> <TT -CLASS="LITERAL" ->$Id:howto.smime.html 583 2007-10-01 19:23:12Z heikki $</TT -> - </P -></DIV -></DIV -></BODY -></HTML -> diff --git a/doc/howto.smime.rst b/doc/howto.smime.rst new file mode 100644 index 0000000..dfd7abf --- /dev/null +++ b/doc/howto.smime.rst @@ -0,0 +1,776 @@ +.. _howto-smime: + +HOWTO: Programming S/MIME in Python with M2Crypto +================================================= + +:author: Pheng Siong Ng <ngps@post1.com> +:copyright: © 2000, 2001 by Ng Pheng Siong. + +Introduction +============ + +`M2Crypto <https://gitlab.com/m2crypto/m2crypto/>`__ is a +`Python <http://www.python.org>`__ interface to +`OpenSSL <http://www.openssl.org>`__. It makes available to the Python +programmer SSL functionality to implement clients and servers, S/MIME +v2, RSA, DSA, DH, symmetric ciphers, message digests and HMACs. + +This document demonstrates programming S/MIME with M2Crypto. + +S/MIME +====== + +S/MIME - Secure Multipurpose Internet Mail Extensions [RFC 2311, RFC +2312] - provides a consistent way to send and receive secure MIME data. +Based on the popular Internet MIME standard, S/MIME provides the +following cryptographic security services for electronic messaging +applications - *authentication*, *message integrity* and +*non-repudiation of origin* (using *digital signatures*), and *privacy* +and *data security* (using *encryption*). + +Keys and Certificates +===================== + +To create an S/MIME-signed message, you need an RSA key pair (this +consists of a public key and a private key) and an X.509 certificate of +said public key. + +To create an S/MIME-encrypted message, you need an X.509 certificate for +each recipient. + +To create an S/MIME-signed *and* -encrypted message, first create a +signed message, then encrypt the signed message with the recipients' +certificates. + +You may generate key pairs and obtain certificates by using a commercial +*certification authority* service. + +You can also do so using freely-available software. For many purposes, +e.g., automated S/MIME messaging by system administration processes, +this approach is cheap and effective. + +We now work through using OpenSSL to generate key pairs and +certificates. This assumes you have OpenSSL installed properly on your +system. + +First, we generate an X.509 certificate to be used for signing:: + + openssl req -newkey rsa:1024 -nodes -x509 -days 365 -out signer.pem + + Using configuration from /usr/local/pkg/openssl/openssl.cnf + Generating a 1024 bit RSA private key + ..++++++ + ....................++++++ + writing new private key to 'privkey.pem' + ----- + You are about to be asked to enter information that will be incorporated + into your certificate request. + What you are about to enter is what is called a Distinguished Name or a DN. + There are quite a few fields but you can leave some blank + For some fields there will be a default value, + If you enter '.', the field will be left blank. + ----- + Country Name (2 letter code) [AU]:SG + State or Province Name (full name) [Some-State]:. + Locality Name (eg, city) []:. + Organization Name (eg, company) [Internet Widgits Pty Ltd]:M2Crypto + Organizational Unit Name (eg, section) []:. + Common Name (eg, YOUR name) []:S/MIME Sender + Email Address []:sender@example.dom + + +This generates a 1024-bit RSA key pair, unencrypted, into +``privkey.pem``; it also generates a self-signed X.509 certificate for +the public key into ``signer.pem``. The certificate is valid for 365 +days, i.e., a year. + +Let's rename ``privkey.pem`` so that we know it is a companion of +``signer.pem``'s:: + + mv privkey.pem signer_key.pem + +To verify the content of ``signer.pem``, execute the following:: + + openssl x509 -noout -text -in signer.pem + + Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=SG, O=M2Crypto, CN=S/MIME Sender/Email=sender@example.dom + Validity + Not Before: Mar 24 12:56:16 2001 GMT + Not After : Mar 24 12:56:16 2002 GMT + Subject: C=SG, O=M2Crypto, CN=S/MIME Sender/Email=sender@example.dom + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:a9:d6:e2:b5:11:3b:ae:3c:e2:17:31:70:e1:6e: + 01:f4:19:6d:bd:2a:42:36:2b:37:34:e2:83:1d:0d: + 11:2e:b4:99:44:db:10:67:be:97:5f:5b:1a:26:33: + 46:23:2f:95:04:7a:35:da:9d:f9:26:88:39:9e:17: + cd:3e:eb:a8:19:8d:a8:2a:f1:43:da:55:a9:2e:2c: + 65:ed:04:71:42:ce:73:53:b8:ea:7e:c7:f0:23:c6: + 63:c5:5e:68:96:64:a7:b4:2a:94:26:76:eb:79:ea: + e3:4e:aa:82:09:4f:44:87:4a:12:62:b5:d7:1f:ca: + f2:ce:d5:ba:7e:1f:48:fd:b9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 29:FB:38:B6:BF:E2:40:BB:FF:D5:71:D7:D5:C4:F0:83:1A:2B:C7:99 + X509v3 Authority Key Identifier: + keyid:29:FB:38:B6:BF:E2:40:BB:FF:D5:71:D7:D5:C4:F0:83:1A:2B:C7:99 + DirName:/C=SG/O=M2Crypto/CN=S/MIME Sender/Email=sender@example.dom + serial:00 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: md5WithRSAEncryption + 68:c8:6b:1b:fa:7c:9a:39:35:76:18:15:c9:fd:89:97:62:db: + 7a:b0:2d:13:dd:97:e8:1b:7a:9f:22:27:83:24:9d:2e:56:ec: + 97:89:3c:ef:16:55:80:5a:18:7c:22:d0:f6:bb:e3:a4:e8:59: + 30:ff:99:5a:93:3e:ea:bc:ee:7f:8d:d6:7d:37:8c:ac:3d:74: + 80:ce:7a:99:ba:27:b9:2a:a3:71:fa:a5:25:ba:47:17:df:07: + 56:96:36:fd:60:b9:6c:96:06:e8:e3:7b:9f:4b:6a:95:71:a8: + 34:fc:fc:b5:88:8b:c4:3f:1e:24:f6:52:47:b2:7d:44:67:d9: + 83:e8 + +Next, we generate a self-signed X.509 certificate for the recipient. +Note that ``privkey.pem`` will be recreated:: + + openssl req -newkey rsa:1024 -nodes -x509 -days 365 -out recipient.pem + + Using configuration from /usr/local/pkg/openssl/openssl.cnf + Generating a 1024 bit RSA private key + .....................................++++++ + .................++++++ + writing new private key to 'privkey.pem' + ----- + You are about to be asked to enter information that will be incorporated + into your certificate request. + What you are about to enter is what is called a Distinguished Name or a DN. + There are quite a few fields but you can leave some blank + For some fields there will be a default value, + If you enter '.', the field will be left blank. + ----- + Country Name (2 letter code) [AU]:SG + State or Province Name (full name) [Some-State]:. + Locality Name (eg, city) []:. + Organization Name (eg, company) [Internet Widgits Pty Ltd]:M2Crypto + Organizational Unit Name (eg, section) []:. + Common Name (eg, YOUR name) []:S/MIME Recipient + Email Address []:recipient@example.dom + +Again, rename ``privkey.pem``:: + + mv privkey.pem recipient_key.pem + + +In the examples to follow, S/MIME Sender, ``<sender@example.dom>``, +shall be the sender of S/MIME messages, while S/MIME Recipient, +``<recipient@example.dom>``, shall be the recipient of S/MIME messages. + +Armed with the key pairs and certificates, we are now ready to begin +programming S/MIME in Python. + + **Note:** The private keys generated above are *not + passphrase-protected*, i.e., they are *in the clear*. Anyone who has + access to such a key can generate S/MIME-signed messages with it, + and decrypt S/MIME messages encrypted to it's corresponding public + key. + + We may passphrase-protect the keys, if we so choose. M2Crypto will + prompt the user for the passphrase when such a key is being loaded. + +M2Crypto.SMIME +============== + +The Python programmer accesses M2Crypto's S/MIME functionality through +class ``SMIME`` in the module ``M2Crypto.SMIME``. Typically, an +``SMIME`` object is instantiated; the object is then set up for the +intended operation: sign, encrypt, decrypt or verify; finally, the +operation is invoked on the object. + +``M2Crypto.SMIME`` makes extensive use of ``M2Crypto.BIO``: +``M2Crypto.BIO`` is a Python abstraction of the ``BIO`` abstraction in +OpenSSL. A commonly used ``BIO`` abstraction in M2Crypto is +``M2Crypto.BIO.MemoryBuffer``, which implements a memory-based file-like +object, similar to Python's own ``StringIO``. + +Sign +==== + +The following code demonstrates how to generate an S/MIME-signed +message. ``randpool.dat`` contains random data which is used to seed +OpenSSL's pseudo-random number generator via M2Crypto:: + + from M2Crypto import BIO, Rand, SMIME + + def makebuf(text): + return BIO.MemoryBuffer(text) + + # Make a MemoryBuffer of the message. + buf = makebuf('a sign of our times') + + # Seed the PRNG. + Rand.load_file('randpool.dat', -1) + + # Instantiate an SMIME object; set it up; sign the buffer. + s = SMIME.SMIME() + s.load_key('signer_key.pem', 'signer.pem') + p7 = s.sign(buf, SMIME.PKCS7_DETACHED) + + +``p7`` now contains a *PKCS #7 signature blob* wrapped in an +``M2Crypto.SMIME.PKCS7`` object. Note that ``buf`` has been consumed by +``sign()`` and has to be recreated if it is to be used again. + +We may now send the signed message via SMTP. In these examples, we shall +not do so; instead, we'll render the S/MIME output in mail-friendly +format, and pretend that our messages are sent and received +correctly:: + + # Recreate buf. + buf = makebuf('a sign of our times') + + # Output p7 in mail-friendly format. + out = BIO.MemoryBuffer() + out.write('From: sender@example.dom\n') + out.write('To: recipient@example.dom\n') + out.write('Subject: M2Crypto S/MIME testing\n') + s.write(out, p7, buf) + + print(out.read()) + + # Save the PRNG's state. + Rand.save_file('randpool.dat') + +Here's the output:: + + From: sender@example.dom + To: recipient@example.dom + Subject: M2Crypto S/MIME testing + MIME-Version: 1.0 + Content-Type: multipart/signed ; protocol="application/x-pkcs7-signature" ; micalg=sha1 ; boundary="----3C93156FC7B4EBF49FE9C7DB7F503087" + + This is an S/MIME signed message + + ------3C93156FC7B4EBF49FE9C7DB7F503087 + a sign of our times + ------3C93156FC7B4EBF49FE9C7DB7F503087 + Content-Type: application/x-pkcs7-signature; name="smime.p7s" + Content-Transfer-Encoding: base64 + Content-Disposition: attachment; filename="smime.p7s" + + MIIE8AYJKoZIhvcNAQcCoIIE4TCCBN0CAQExCzAJBgUrDgMCGgUAMCIGCSqGSIb3 + DQEHAaAVBBNhIHNpZ24gb2Ygb3VyIHRpbWVzoIIC5zCCAuMwggJMoAMCAQICAQAw + DQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRv + MRYwFAYDVQQDEw1TL01JTUUgU2VuZGVyMSEwHwYJKoZIhvcNAQkBFhJzZW5kZXJA + ZXhhbXBsZS5kb20wHhcNMDEwMzMxMTE0MDMzWhcNMDIwMzMxMTE0MDMzWjBbMQsw + CQYDVQQGEwJTRzERMA8GA1UEChMITTJDcnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBT + ZW5kZXIxITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmRvbTCBnzANBgkq + hkiG9w0BAQEFAAOBjQAwgYkCgYEA5c5Tj1CHTSOxa1q2q0FYiwMWYHptJpJcvtZm + UwrgU5sHrA8OnCM0cDXEj0KPf3cfNjHffB8HWMzI4UEgNmFXQNsxoGZ+iqwxLlNj + y9Mh7eFW/Bjq5hNXbouSlQ0rWBRkoxV64y+t6lQehb32WfYXQbKFxFJSXzSxOx3R + 8YhSPd0CAwEAAaOBtjCBszAdBgNVHQ4EFgQUXOyolL1t4jaBwZFRM7MS8nBLzUow + gYMGA1UdIwR8MHqAFFzsqJS9beI2gcGRUTOzEvJwS81KoV+kXTBbMQswCQYDVQQG + EwJTRzERMA8GA1UEChMITTJDcnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBTZW5kZXIx + ITAfBgkqhkiG9w0BCQEWEnNlbmRlckBleGFtcGxlLmRvbYIBADAMBgNVHRMEBTAD + AQH/MA0GCSqGSIb3DQEBBAUAA4GBAHo3DrCHR86fSTVAvfiXdSswWqKtCEhUHRdC + TLFGl4hDk2GyZxaFuqZwiURz/H7nMicymI2wkz8H/wyHFg8G3BIehURpj2v/ZWXY + eovbgS7EZALVVkDj4hNl/IIHWd6Gtv1UODf7URbxtl3hQ9/eTWITrefT1heuPnar + 8czydsOLMYIBujCCAbYCAQEwYDBbMQswCQYDVQQGEwJTRzERMA8GA1UEChMITTJD + cnlwdG8xFjAUBgNVBAMTDVMvTUlNRSBTZW5kZXIxITAfBgkqhkiG9w0BCQEWEnNl + bmRlckBleGFtcGxlLmRvbQIBADAJBgUrDgMCGgUAoIGxMBgGCSqGSIb3DQEJAzEL + BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTAxMDMzMTExNDUwMlowIwYJKoZI + hvcNAQkEMRYEFOoeRUd8ExIYXfQq8BTFuKWrSP3iMFIGCSqGSIb3DQEJDzFFMEMw + CgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsO + AwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIGAQpU8hFUtLCF6hO2t + ec9EYJ/Imqqiiw+BxWxkUUVT81Vbjwdn9JST6+sztM5JRP2ZW+b4txEjZriYC8f3 + kv95YMTGbIsuWkJ93GrbvqoJ/CxO23r9WWRnZEm/1EZN9ZmlrYqzBTxnNRmP3Dhj + cW8kzZwH+2/2zz2G7x1HxRWH95A= + + ------3C93156FC7B4EBF49FE9C7DB7F503087-- + + +Verify +====== + +Assume the above output has been saved into ``sign.p7``. Let's now +verify the signature:: + + from M2Crypto import SMIME, X509 + + # Instantiate an SMIME object. + s = SMIME.SMIME() + + # Load the signer's cert. + x509 = X509.load_cert('signer.pem') + sk = X509.X509_Stack() + sk.push(x509) + s.set_x509_stack(sk) + + # Load the signer's CA cert. In this case, because the signer's + # cert is self-signed, it is the signer's cert itself. + st = X509.X509_Store() + st.load_info('signer.pem') + s.set_x509_store(st) + + # Load the data, verify it. + p7, data = SMIME.smime_load_pkcs7('sign.p7') + v = s.verify(p7, data) + print(v) + print(data) + print(data.read()) + +Here's the output of the above program:: + + a sign of our times + <M2Crypto.BIO.BIO instance at 0x822012c> + a sign of our times + +Suppose, instead of loading ``signer.pem`` above, we load +``recipient.pem``. That is, we do a global substitution of +``recipient.pem`` for ``signer.pem`` in the above program. Here's the +modified program's output:: + + Traceback (most recent call last): + File "./verify.py", line 22, in ? + v = s.verify(p7) + File "/usr/local/home/ngps/prog/m2/M2Crypto/SMIME.py", line 205, in verify + raise SMIME_Error, Err.get_error() + M2Crypto.SMIME.SMIME_Error: 312:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:213:Verify error:self signed certificate + + +As displayed, the error is generated by line 213 of OpenSSL's +``pk7_smime.c`` (as of OpenSSL 0.9.6); if you are a C programmer, you +may wish to look up the C source to explore OpenSSL's S/MIME +implementation and understand why the error message is worded thus. + +Encrypt +======= + +We now demonstrate how to generate an S/MIME-encrypted message:: + + from M2Crypto import BIO, Rand, SMIME, X509 + + def makebuf(text): + return BIO.MemoryBuffer(text) + + # Make a MemoryBuffer of the message. + buf = makebuf('a sign of our times') + + # Seed the PRNG. + Rand.load_file('randpool.dat', -1) + + # Instantiate an SMIME object. + s = SMIME.SMIME() + + # Load target cert to encrypt to. + x509 = X509.load_cert('recipient.pem') + sk = X509.X509_Stack() + sk.push(x509) + s.set_x509_stack(sk) + + # Set cipher: 3-key triple-DES in CBC mode. + s.set_cipher(SMIME.Cipher('des_ede3_cbc')) + + # Encrypt the buffer. + p7 = s.encrypt(buf) + + # Output p7 in mail-friendly format. + out = BIO.MemoryBuffer() + out.write('From: sender@example.dom\n') + out.write('To: recipient@example.dom\n') + out.write('Subject: M2Crypto S/MIME testing\n') + s.write(out, p7) + + print(out.read()) + + # Save the PRNG's state. + Rand.save_file('randpool.dat') + +Here's the output of the above program:: + + From: sender@example.dom + To: recipient@example.dom + Subject: M2Crypto S/MIME testing + MIME-Version: 1.0 + Content-Disposition: attachment; filename="smime.p7m" + Content-Type: application/x-pkcs7-mime; name="smime.p7m" + Content-Transfer-Encoding: base64 + + MIIBVwYJKoZIhvcNAQcDoIIBSDCCAUQCAQAxggEAMIH9AgEAMGYwYTELMAkGA1UE + BhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRkwFwYDVQQDExBTL01JTUUgUmVjaXBp + ZW50MSQwIgYJKoZIhvcNAQkBFhVyZWNpcGllbnRAZXhhbXBsZS5kb20CAQAwDQYJ + KoZIhvcNAQEBBQAEgYCBaXZ+qjpBEZwdP7gjfzfAtQitESyMwo3i+LBOw6sSDir6 + FlNDPCnkrTvqDX3Rt6X6vBtTCYOm+qiN7ujPkOU61cN7h8dvHR8YW9+0IPY80/W0 + lZ/HihSRgwTNd7LnxUUcPx8YV1id0dlmP0Hz+Lg+mHf6rqaR//JcYhX9vW4XvjA7 + BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECMN+qya6ADywgBgHr9Jkhwn5Gsdu7BwX + nIQfYTYcdL9I5Sk= + + +Decrypt +======= + +Assume the above output has been saved into ``encrypt.p7``. Decrypt the +message thusly:: + + from M2Crypto import BIO, SMIME, X509 + + # Instantiate an SMIME object. + s = SMIME.SMIME() + + # Load private key and cert. + s.load_key('recipient_key.pem', 'recipient.pem') + + # Load the encrypted data. + p7, data = SMIME.smime_load_pkcs7('encrypt.p7') + + # Decrypt p7. + out = s.decrypt(p7) + + print(out) + +Here's the output:: + + a sign of our times + + +Sign and Encrypt +================ + +Here's how to generate an S/MIME-signed/encrypted message:: + + from M2Crypto import BIO, Rand, SMIME, X509 + + def makebuf(text): + return BIO.MemoryBuffer(text) + + # Make a MemoryBuffer of the message. + buf = makebuf('a sign of our times') + + # Seed the PRNG. + Rand.load_file('randpool.dat', -1) + + # Instantiate an SMIME object. + s = SMIME.SMIME() + + # Load signer's key and cert. Sign the buffer. + s.load_key('signer_key.pem', 'signer.pem') + p7 = s.sign(buf) + + # Load target cert to encrypt the signed message to. + x509 = X509.load_cert('recipient.pem') + sk = X509.X509_Stack() + sk.push(x509) + s.set_x509_stack(sk) + + # Set cipher: 3-key triple-DES in CBC mode. + s.set_cipher(SMIME.Cipher('des_ede3_cbc')) + + # Create a temporary buffer. + tmp = BIO.MemoryBuffer() + + # Write the signed message into the temporary buffer. + s.write(tmp, p7) + + # Encrypt the temporary buffer. + p7 = s.encrypt(tmp) + + # Output p7 in mail-friendly format. + out = BIO.MemoryBuffer() + out.write('From: sender@example.dom\n') + out.write('To: recipient@example.dom\n') + out.write('Subject: M2Crypto S/MIME testing\n') + s.write(out, p7) + + print(out.read()) + + # Save the PRNG's state. + Rand.save_file('randpool.dat') + +Here's the output of the above program:: + + From: sender@example.dom + To: recipient@example.dom + Subject: M2Crypto S/MIME testing + MIME-Version: 1.0 + Content-Disposition: attachment; filename="smime.p7m" + Content-Type: application/x-pkcs7-mime; name="smime.p7m" + Content-Transfer-Encoding: base64 + + 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 + + +Decrypt and Verify +================== + +Suppose the above output has been saved into ``se.p7``. The following +demonstrates how to decrypt and verify it:: + + from M2Crypto import BIO, SMIME, X509 + + # Instantiate an SMIME object. + s = SMIME.SMIME() + + # Load private key and cert. + s.load_key('recipient_key.pem', 'recipient.pem') + + # Load the signed/encrypted data. + p7, data = SMIME.smime_load_pkcs7('se.p7') + + # After the above step, 'data' == None. + # Decrypt p7. 'out' now contains a PKCS #7 signed blob. + out = s.decrypt(p7) + + # Load the signer's cert. + x509 = X509.load_cert('signer.pem') + sk = X509.X509_Stack() + sk.push(x509) + s.set_x509_stack(sk) + + # Load the signer's CA cert. In this case, because the signer's + # cert is self-signed, it is the signer's cert itself. + st = X509.X509_Store() + st.load_info('signer.pem') + s.set_x509_store(st) + + # Recall 'out' contains a PKCS #7 blob. + # Transform 'out'; verify the resulting PKCS #7 blob. + p7_bio = BIO.MemoryBuffer(out) + p7, data = SMIME.smime_load_pkcs7_bio(p7_bio) + v = s.verify(p7) + + print(v) + + +The output is as follows:: + + a sign of our times + + +Sending S/MIME messages via SMTP +================================ + +In the above examples, we've assumed that our S/MIME messages are sent +and received automagically. The following is a Python function that +generates S/MIME-signed/encrypted messages and sends them via +SMTP:: + + from M2Crypto import BIO, SMIME, X509 + import smtplib, string, sys + + def sendsmime(from_addr, to_addrs, subject, msg, from_key, from_cert=None, to_certs=None, smtpd='localhost'): + + msg_bio = BIO.MemoryBuffer(msg) + sign = from_key + encrypt = to_certs + + s = SMIME.SMIME() + if sign: + s.load_key(from_key, from_cert) + if encrypt: + p7 = s.sign(msg_bio, flags=SMIME.PKCS7_TEXT) + else: + p7 = s.sign(msg_bio, flags=SMIME.PKCS7_TEXT|SMIME.PKCS7_DETACHED) + msg_bio = BIO.MemoryBuffer(msg) # Recreate coz sign() has consumed it. + + if encrypt: + sk = X509.X509_Stack() + for x in to_certs: + sk.push(X509.load_cert(x)) + s.set_x509_stack(sk) + s.set_cipher(SMIME.Cipher('des_ede3_cbc')) + tmp_bio = BIO.MemoryBuffer() + if sign: + s.write(tmp_bio, p7) + else: + tmp_bio.write(msg) + p7 = s.encrypt(tmp_bio) + + out = BIO.MemoryBuffer() + out.write('From: %s\r\n' % from_addr) + out.write('To: %s\r\n' % string.join(to_addrs, ", ")) + out.write('Subject: %s\r\n' % subject) + if encrypt: + s.write(out, p7) + else: + if sign: + s.write(out, p7, msg_bio, SMIME.PKCS7_TEXT) + else: + out.write('\r\n') + out.write(msg) + out.close() + + smtp = smtplib.SMTP() + smtp.connect(smtpd) + smtp.sendmail(from_addr, to_addrs, out.read()) + smtp.quit() + + +This function sends plain, S/MIME-signed, S/MIME-encrypted, and +S/MIME-signed/encrypted messages, depending on the parameters +``from_key`` and ``to_certs``. The function's output interoperates with +Netscape Messenger. + +Verifying origin of S/MIME messages +=================================== + +In our examples above that decrypt or verify messages, we skipped a +step: verifying that the ``from`` address of the message matches the +``email address`` attribute in the sender's certificate. + +The premise of current X.509 certification practice is that the CA is +supposed to verify your identity, and to issue a certificate with +``email address`` that matches your actual mail address. (Verisign's +March 2001 failure in identity verification resulting in Microsoft +certificates being issued to spoofers notwithstanding.) + +If you run your own CA, your certification practice is up to you, of +course, and it would probably be part of your security policy. + +Whether your S/MIME messaging application needs to verify the ``from`` +addresses of S/MIME messages depends on your security policy and your +system's threat model, as always. + +Interoperating with Netscape Messenger +====================================== + +Suppose S/MIME Recipient uses Netscape Messenger. To enable Messenger to +handle S/MIME messages from S/MIME Sender, S/MIME Recipient needs to +configure Messenger with his private key and certificate, as well as +S/MIME Sender's certificate. + + **Note:** Configuring Messenger's POP or IMAP settings so that it + retrieves mail correctly is beyond the scope of this HOWTO. + +The following steps demonstrate how to import S/MIME Recipient's private +key and certificate for Messenger: + +1. Transform S/MIME Recipient's private key and certificate into *PKCS + #12* format:: + + openssl pkcs12 -export -in recipient.pem -inkey recipient_key.pem \ + -name "S/MIME Recipient" -out recipient.p12 + + Enter Export Password:<enter> + Verifying password - Enter Export Password:<enter> + +2. Start Messenger. + +3. Click on the (open) "lock" icon at the bottom left corner of + Messenger's window. This brings up the "Security Info" dialog box. + +4. Click on "Yours" under "Certificates". + +5. Select "Import a certificate", then pick ``recipient.p12`` from the + ensuing file selection dialog box. + +Next, you need to import ``signer.pem`` as a CA certificate, so that +Messenger will mark messages signed by S/MIME Sender as "trusted": + +1. Create a DER encoding of ``signer.pem``:: + + openssl x509 -inform pem -outform der -in signer.pem -out signer.der + +2. Install ``signer.der`` into Messenger as MIME type + ``application/x-x509-ca-cert``. You do this by downloading + ``signer.der`` via Navigator from a HTTP or HTTPS server, with the + correct MIME type mapping. (You may use ``demo/ssl/https_srv.py``, + bundled with M2Crypto, for this purpose.) Follow the series of dialog + boxes to accept ``signer.der`` as a CA for certifying email users. + +S/MIME Recipient is now able to decrypt and read S/MIME Sender's +messages with Messenger. Messenger will indicate that S/MIME Sender's +messages are signed, encrypted, or encrypted *and* signed, as the case +may be, via the "stamp" icon on the message window's top right corner. + +Clicking on the "stamp" icon brings you to the Security Info dialog box. +Messenger informs you that the message is, say, encrypted with 168-bit +DES-EDE3-CBC and that it is digitally signed by the private key +corresponding to the public key contained in the certificate +``signer.pem``. + +Interoperating with Microsoft Outlook +===================================== + +I do not know how to do this, as I do not use Outlook. (Nor do I use +Netscape Messenger, actually. I use Mutt, top dog of MUAs. ;-) +Information on how to configure Outlook with keys and certificates so +that it handles S/MIME mail is gratefully accepted. + +ZSmime +====== + +ZSmime is a `Zope <http://www.zope.org>`__ *product* that enables Zope +to generate S/MIME-signed/encrypted messages. ZSmime demonstrates how to +invoke M2Crypto in a web application server extension. + +ZSmime has its own +`HOWTO <http://sandbox.rulemaker.net/ngps/zope/zsmime/howto.html>`__ +explaining its usage. (That HOWTO has some overlap in content with this +document.) + +Resources +========= + +- IETF S/MIME Working Group - http://www.imc.org/ietf-smime + +- S/MIME and OpenPGP - http://www.imc.org/smime-pgpmime.html + +- S/MIME Freeware Library - + http://www.getronicsgov.com/hot/sfl_home.htm + +- Mozilla Network Security Services - + http://www.mozilla.org/projects/security/pkg/nss + +- S/MIME Cracking Screen Saver - http://www.counterpane.com/smime.html diff --git a/doc/howto.ssl.html b/doc/howto.ssl.html deleted file mode 100644 index 340f264..0000000 --- a/doc/howto.ssl.html +++ /dev/null @@ -1,206 +0,0 @@ -<HTML -><HEAD -><TITLE ->HOWTO: Programming SSL in Python with M2Crypto</TITLE -> - -</HEAD> -<BODY -CLASS="ARTICLE" -BGCOLOR="#FFFFFF" -TEXT="#000000" -LINK="#0000FF" -VLINK="#840084" -ALINK="#0000FF" -> - -<DIV -CLASS="TITLEPAGE" -><H1 -CLASS="TITLE" -><A -NAME="AEN2" ->HOWTO: Programming SSL in Python with M2Crypto</A -></H1 -> -<P> -Ng Pheng Siong (ngps@netmemetic.com) and Heikki Toivonen (heikki@osafoundation.org) -</P -><P -CLASS="COPYRIGHT" ->Copyright © 2001, 2002 by Ng Pheng Siong.</P -> -<P -CLASS="COPYRIGHT" ->Portions Copyright © 2006 by Open Source Applications Foundation.</P -> -</DIV> - -<DIV -CLASS="SECT1" -><H1 -CLASS="SECT1" -><A -NAME="INTRODUCTION" ->Introduction</A -></H1 -><P -><A -HREF="http://chandlerproject.org/Projects/MeTooCrypto" -TARGET="_top" ->M2Crypto</A -> - is a <A -HREF="http://www.python.org" -TARGET="_top" ->Python</A -> - interface to <A -HREF="http://www.openssl.org" -TARGET="_top" ->OpenSSL</A ->. It makes - available to the Python programmer SSL functionality to implement clients - and servers, S/MIME v2, RSA, DSA, DH, symmetric ciphers, message digests and - HMACs. - </P -><P ->This document demonstrates programming HTTPS with M2Crypto. - </P -></DIV -> - - -<DIV -CLASS="SECT1" -><H1 -CLASS="SECT1" -><A -NAME="history" ->A bit of history</A -></H1 -> - -<p> M2Crypto was created during the time of Python 1.5, which features - a module httplib providing client-side HTTP functionality. M2Crypto sports - a httpslib based on httplib. - </p> - - <p> - Beginning with version 2.0, Python's socket module provided - (rudimentary) SSL support. Also in the same version, httplib was - enhanced with class HTTPConnection, which is more sophisticated than - the old class HTTP, and HTTPSConnection, which does HTTPS. - </p> - - <p> - Subsequently, M2Crypto.httpslib grew a compatible (but not identical) - class HTTPSConnection. - </p> - - <p> - The primary interface difference between the two HTTPSConnection - classes is that M2Crypto's version accepts an M2Crypto.SSL.Context - instance as a parameter, whereas Python 2.x's SSL support does not - permit Pythonic control of the SSL context. - </p> - - <p> Within the implementations, Python's - <tt>HTTPSConnection</tt> employs a - <tt>FakeSocket</tt> object, which collects all input from - the SSL connection before returning it to the application as a - <tt>StringIO</tt> buffer, whereas M2Crypto's - <tt>HTTPSConnection</tt> uses a buffering - <tt>M2Crypto.BIO.IOBuffer</tt> object that works over the - underlying M2Crypto.SSL.Connection directly. </p> - - <p>Since then M2Crypto has gained a Twisted wrapper that allows securing - Twisted SSL connections with M2Crypto.</p> -</DIV -> - - -<DIV CLASS="SECT1" id="secure" name="secure"> -<H1 CLASS="SECT1">Secure SSL</H1> - -<p>It is recommended that you read the book Network Security with OpenSSL by John Viega, Matt Messier and Pravir Chandra, -ISBN 059600270X.</p> - -<p>Using M2Crypto does not automatically make an SSL connection secure. There are various steps that need to be made -before we can make that claim. Let's see how a simple client can establish a secure connection:</p> - -<pre> -ctx = SSL.Context() -ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9) -if ctx.load_verify_locations('ca.pem') != 1: raise Exception('No CA certs') -s = SSL.Connection(ctx) -s.connect(server_address) -# Normal protocol (for example HTTP) commands follow -</pre> - -<p>The first line creates an SSL context. The defaults allow any SSL version (except SSL version 2 which has known -weaknesses) and sets the allowed ciphers to secure ones.</p> - -<p>The second line tells M2Crypto to perform certificate validation. The flags shown above are typical for clients, -and requires the server to send a certificate. The depth parameter tells how long certificate chains are allowed - -9 is pretty common default, although probably too long in practice.</p> - -<p>The third line loads the allowed root (certificate authority or CA) certificates. -Most Linux distributions come with CA certificates in suitable format. You -could also download the <a href="http://mxr.mozilla.org/seamonkey/source//security/nss/lib/ckfw/builtins/certdata.txt?raw=1">certdata.txt</a> -file from the <a href="http://www.mozilla.org/projects/security/pki/nss/">NSS</a> -project and convert it -with the little M2Crypto utility script <a href="http://svn.osafoundation.org/m2crypto/trunk/demo/x509/certdata2pem.py">demo/x509/certdata2pem.py</a>.</p> - -<p>The fourth line creates an SSL connection object with the secure context.</p> - -<p>The fifth line connects to the server. During this time we perform the last security step: just after connection, but before -exchanging any data, we compare the commonName (or subjectAltName DNS field) field in the certificate the server returned to the -server address we tried to connect to. This happens automatically with SSL.Connection and the Twisted wrapper class, and anything -that uses those. In all other cases you must do the check manually. It is recommended you call the SSL.Checker to do the actual check.</p> - -<p>SSL servers are different in that they typically do not require the client to send a certificate, so there is usually no certificate -checking. Also, it is typically useless to perform host name checking.</p> - -</DIV> - -<DIV CLASS="SECT1"> -<H1 CLASS="SECT1">Code Samples</H1> - -<p>The best samples of how to use the various SSL objects are in the tests directory, and the test_ssl.py file specifically. -There are additional samples in the demo directory, but they are not quaranteed to be up to date.</p> - -<p>NOTE: The tests and demos -may not be secure as is. Use the information above on how to make them secure.</p> -</DIV> - -<DIV -CLASS="SECT1" -><H1 -CLASS="SECT1" -><A -NAME="SSLDUMP" ->ssldump</A -></H1 -> -<P>ssldump "is an SSLv3/TLS network protocol analyser. It identifies - TCP connections on the chosen network interface and attempts to interpret - them as SSLv3/TLS traffic. When it identifies SSLv3/TLS traffic, it - decodes the records and displays them in a textual form to stdout. If - provided with the appropriate keying material, it will also decrypt the - connections and display the application data traffic. - </P> - - <P> - If linked with OpenSSL, ssldump can display certificates in decoded form - and decrypt traffic (provided that it has the appropriate keying - material)." - </P> - - <P>ssldump is written by Eric Rescorla. - </P> -</DIV -> - -</BODY> -</HTML> diff --git a/doc/howto.ssl.rst b/doc/howto.ssl.rst new file mode 100644 index 0000000..ab9b17d --- /dev/null +++ b/doc/howto.ssl.rst @@ -0,0 +1,129 @@ +.. _howto-ssl: + +HOWTO: Programming SSL in Python with M2Crypto +============================================== + +:author: Pheng Siong Ng <ngps@netmemetic.com> and Heikki Toivonen (heikki@osafoundation.org) +:copyright: © 2000, 2001 by Ng Pheng Siong, + portions © 2006 by Open Source Applications Foundation + +Introduction +============ + +`M2Crypto <https://gitlab.com/m2crypto/m2crypto/>`__ is a +`Python <http://www.python.org>`__ interface to +`OpenSSL <http://www.openssl.org>`__. It makes available to the Python +programmer SSL functionality to implement clients and servers, S/MIME +v2, RSA, DSA, DH, symmetric ciphers, message digests and HMACs. + +This document demonstrates programming HTTPS with M2Crypto. + +A bit of history +================ + +M2Crypto was created during the time of Python 1.5, which features a +module httplib providing client-side HTTP functionality. M2Crypto sports +a httpslib based on httplib. + +Beginning with version 2.0, Python's socket module provided +(rudimentary) SSL support. Also in the same version, httplib was +enhanced with class HTTPConnection, which is more sophisticated than the +old class HTTP, and HTTPSConnection, which does HTTPS. + +Subsequently, M2Crypto.httpslib grew a compatible (but not identical) +class HTTPSConnection. + +The primary interface difference between the two HTTPSConnection classes +is that M2Crypto's version accepts an M2Crypto.SSL.Context instance as a +parameter, whereas Python 2.x's SSL support does not permit Pythonic +control of the SSL context. + +Within the implementations, Python's ``HTTPSConnection`` employs a +``FakeSocket`` object, which collects all input from the SSL connection +before returning it to the application as a ``StringIO`` buffer, whereas +M2Crypto's ``HTTPSConnection`` uses a buffering +``M2Crypto.BIO.IOBuffer`` object that works over the underlying +M2Crypto.SSL.Connection directly. + +Since then M2Crypto has gained a Twisted wrapper that allows securing +Twisted SSL connections with M2Crypto. + +Secure SSL +========== + +It is recommended that you read the book Network Security with OpenSSL +by John Viega, Matt Messier and Pravir Chandra, ISBN 059600270X. + +Using M2Crypto does not automatically make an SSL connection secure. +There are various steps that need to be made before we can make that +claim. Let's see how a simple client can establish a secure +connection:: + + ctx = SSL.Context() + ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9) + if ctx.load_verify_locations('ca.pem') != 1: raise Exception('No CA certs') + s = SSL.Connection(ctx) + s.connect(server_address) + # Normal protocol (for example HTTP) commands follow + +The first line creates an SSL context. The defaults allow any SSL +version (except SSL version 2 which has known weaknesses) and sets the +allowed ciphers to secure ones. + +The second line tells M2Crypto to perform certificate validation. The +flags shown above are typical for clients, and requires the server to +send a certificate. The depth parameter tells how long certificate +chains are allowed - 9 is pretty common default, although probably too +long in practice. + +The third line loads the allowed root (certificate authority or CA) +certificates. Most Linux distributions come with CA certificates in +suitable format. You could also download the +`certdata.txt <http://mxr.mozilla.org/seamonkey/source//security/nss/lib/ckfw/builtins/certdata.txt?raw=1>`__ +file from the +`NSS <http://www.mozilla.org/projects/security/pki/nss/>`__ project and +convert it with the little M2Crypto utility script +`demo/x509/certdata2pem.py <http://svn.osafoundation.org/m2crypto/trunk/demo/x509/certdata2pem.py>`__. + +The fourth line creates an SSL connection object with the secure +context. + +The fifth line connects to the server. During this time we perform the +last security step: just after connection, but before exchanging any +data, we compare the commonName (or subjectAltName DNS field) field in +the certificate the server returned to the server address we tried to +connect to. This happens automatically with SSL.Connection and the +Twisted wrapper class, and anything that uses those. In all other cases +you must do the check manually. It is recommended you call the +SSL.Checker to do the actual check. + +SSL servers are different in that they typically do not require the +client to send a certificate, so there is usually no certificate +checking. Also, it is typically useless to perform host name checking. + +Code Samples +============ + +The best samples of how to use the various SSL objects are in the tests +directory, and the test\_ssl.py file specifically. There are additional +samples in the demo directory, but they are not quaranteed to be up to +date. + +NOTE: The tests and demos may not be secure as is. Use the information +above on how to make them secure. + +ssldump +======= + +ssldump "is an SSLv3/TLS network protocol analyser. It identifies TCP +connections on the chosen network interface and attempts to interpret +them as SSLv3/TLS traffic. When it identifies SSLv3/TLS traffic, it +decodes the records and displays them in a textual form to stdout. If +provided with the appropriate keying material, it will also decrypt the +connections and display the application data traffic. + +If linked with OpenSSL, ssldump can display certificates in decoded form +and decrypt traffic (provided that it has the appropriate keying +material)." + +ssldump is written by Eric Rescorla. diff --git a/doc/index.rst b/doc/index.rst index 6241266..a472668 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,8 +1,3 @@ -.. M2Crypto documentation master file, created by - sphinx-quickstart on Thu Apr 20 11:15:12 2017. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - Welcome to M2Crypto's documentation! ==================================== @@ -14,6 +9,18 @@ Contents: M2Crypto +HOWTOs +====== + +* :ref:`howto-ca` + +* :ref:`howto-ssl` + +* :ref:`howto-smime` + +* :ref:`zserverssl-howto` + + Indices and tables ================== |