I had the same problem as @User39604, and had to follow VARIOUS advices. Since he doesnt remember the precise path he followed, let me list my path:
check if you have SSL YES using <?php echo phpinfo();?>
if necessary
A. enable ssl on apache sudo a2enmod ssl
B. install openssl sudo apt-get install openssl
C. check if port 443 is open sudo netstat -lp
D. if necessary, change /etc/apache2/ports.conf
, this works
NameVirtualHost *:80
Listen 80
<IfModule mod_ssl.c>
# If you add NameVirtualHost *:443 here, you will also have to change
# the VirtualHost statement in /etc/apache2/sites-available/default-ssl
# to <VirtualHost *:443>
# Server Name Indication for SSL named virtual hosts is currently not
# supported by MSIE on Windows XP.
NameVirtualHost *:443
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
acquire a key and a certificate by
A. paying a Certificating Authority (Comodo, GoDaddy, Verisign) for a pair
B. generating your own* - see below (testing purposes ONLY)
change your configuration (in ubuntu12 /etc/apache2/httpd.conf
- default is an empty file) to include a proper <VirtualHost>
(replace MYSITE.COM
as well as key and cert path/name to point to your certificate and key):
<VirtualHost _default_:443>
ServerName MYSITE.COM:443
SSLEngine on
SSLCertificateKeyFile /etc/apache2/ssl/MYSITE.COM.key
SSLCertificateFile /etc/apache2/ssl/MYSITE.COM.cert
ServerAdmin MYWEBGUY@localhost
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/errorSSL.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/accessSSL.log combined
</VirtualHost>
while many other virtualhost configs wil be available in /etc/apache2/sites-enabled/
and in /etc/apache2/sites-available/
it was /etc/apache2/httpd.conf
that was CRUCIAL to solving all problems.
for further info:
http://wiki.vpslink.com/Enable_SSL_on_Apache2
http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html#selfcert
*generating your own certificate (self-signed) will result in a certificate whose authority the user's browser will not recognize. therefore, the browser will scream bloody murder and the user will have to "understand the risks" a dozen times before the browser actually opens up the page. so, it only works for testing purposes. having said that, this is the HOW-TO:
- goto the apache folder (in ubuntu12
/etc/apache2/
)
- create a folder like
ssl
(or anything that works for you, the name is not a system requirement)
- goto chosen directory
/etc/apache2/ssl
- run
sudo openssl req -new -x509 -nodes -out MYSITE.COM.crt -keyout MYSITE.COM.key
- use
MYSITE.COM.crt
and MYSITE.COM.key
in your <VirtualHost>
tag
name format is NOT under a strict system requirement, must be the same as the file :)
- names like 212-MYSITE.COM.crt
, june2014-Godaddy-MYSITE.COM.crt
should work.
Disclaimer
Before I start I would note @Emanuel Ey's comment. That you would want to consider if this was being done on a production or development server first. For example; if you are using Apache WebServer the HTTPS component can be done from Apache. The only thing you would do differently is pass through the certificate details as options and your server app would then verify the serial number within the app itself.
It is Possible
But it the way it is possible is not considered good programming practice. Unfortunately, it's not accessible from flask.request
and not possible with the Flask package. However, Flask uses Werkzeug and it is possible by patching the werkzeug.serving
package where will be writing your main Flask code. It is not recommended because you may want to update Flask or Werkzeug later and your patch might break and need to be re-factored. i.e. from 0.9 to 1.0.
This provides a solution without using a web server. But I would recommend the web server/environment variable combo. It is cleaner and comparatively good practice.
I have done some testing to see if this is easy to implement. I was able to confirm that this method can work using the latest development codebase 'Werkzeug-0.10_devdev_20141223-py2.7'.
You'll probably want to verify of the serial number (seed number) found in each certificate (and maybe even some other variables). As you may know, the serial is unique to each certificate and is determined during the certificate generation process by you on the server side. It helps to store this along with the clients record and certificate information (where appropriate) in order to verify client certificate serial number later on. Note: It may require alterations between hex and base 10 decimal.
Werkzeug dev_2014122
What I did was to add in the following options to the werkzeug.serving.BaseWSGIServer.__init__
call to wrap_socket()
.
Use these;
server_side=True, ca_certs= '/etc/apache2/ssl/ca.pem', cert_reqs=ssl.CERT_REQUIRED
- ca_certs: Use this to verify against, this is the CA cert used to generate the client certificates)
- ssl.CERT_REQUIRED: require client certificate verification against ca_certs
Note: If the client certificate is does not pass initial verification you will not be able to fetch the client certificate. It will be None.
Then in my Flask test class I patched verify_request
where
def verify_request(self, request, client_address):
cert = request.getpeercert(True)
raw = decoder.decode(cert)[0]
print "Serial Number of your certificate is: % " % str(raw[0][1])
# todo: do checks & if serial no is ok then return true
return True
werkzeug.serving.BaseWSGIServer.verify_request = verify_request
This proved it is possible but you'll probably want to investigate the request handlers of the HTTPServer class that the BaseWSGIServer inherits to find a better way to do a call back or override.
Werkzeug 0.9.X
If you are using Werkzeug 0.9.X I'm assuming you are using the import from OpenSSL import SSL
. see code snippet here. I have not tested this.
Some of the calls you may be interested in for this version would be;
- Context.set_verify(mode, callback)
- Connection.get_peer_certificate()
Clarification
What I do not understand is your reference to sending a CSR during the first handshake. If this is your process of client certificate generation you may want to rethink how you do this in the context of your system and environment. If I could have some more information I could comment further..
Also, 'handshake' in an SSL/TLS context generally refers to the action of creating the secure connection in the first place using an existing certificate. Immediately after handshaking, loosely speaking, a connection is established.
Best Answer
SSLCACertificateFile must contain your client's certification authority certificates plus any intermediate certificate file, all concatenated together.
You also lack SSLCertificateChainFile which must point to a file containing your server's certification authority certificate plus any intermediate certificate file, all concatenated together.
Obviously, the client (browser) must have its own client certificate installed.
note: from 2.4.8 release, as official apache documentation, the SSLCertificateChainFile is OBSOLETE (thanks to ezra-s for his comment). It's now possibile to concatenate Server certificate and CA Intermediate certificates directly into SSLCertificateFile.