Saturday, December 17. 2011
Client Certificates In PHP / Oracle Iplanet Web Server
These days I have been working in a customer with the same issue which was discussing in the last series of this blog: client certificate login (custom solution). The big difference here was that this customer is working with PHP (last version 5.3) and Oracle Iplanet Web Server 7.0 (formerly Sun Java System Web Server, formerly Sun ONE Web Server, formerly Iplanet Web Server and now included in Oracle Fusion Middleware WebTier). This web server is the last version of the Sun web server which comes from the time of Sun and Netscape alliance. It is curious how just when I decided to publish the series this topic has come out to me again and again.
PHP when is integrated with Apache (the most common situation) uses some environment variables (SSL_CLIENT_CERT, SSL_CLIENT_S_DN and so on) that mod_ssl (module for Apache Web Server to get SSL communication -HTTPS- which relies on OpenSSL) provides to PHP in $_SERVER array. These vars contain some info about the SSL communication in general and the client certificate in particular, some of them are different parts or fields of the cert and SSL_CLIENT_CERT is the whole certificate, PEM format (see StdEnvVars option in the documentation for further info). As always the developer team has its way environment, windows + Apache 2.2 + PHP 5.2, while the pre and production environment of the customer is Solaris 10 + Iplanet 7.0 + PHP 5.3. Obviously the application did not work when it was tested in pre-production (what a strange thing!). The variables that mod_ssl should inject were not there, which was really confusing for the developers, of course the fact that the web server was an Iplanet and not an Apache did not matter much to them.
When PHP is compiled against the Iplanet Web Server it uses old NSAPI (Netscape Server Application Programming Interface) to interact with the http daemon. Typical PHP compilation (very short in modules) for this environment is like this:
./configure --prefix=/opt/php-5.3 --with-nsapi=/opt/SUNWwbsvr7 \ --enable-ftp --with-pear --enable-pdo --with-openssl
The /opt/SUNWwbsvr7 directory is where the Iplanet Web Server is installed, the installtion needs sample applications component because include files are in there. Then you need to configure some files to make the web server process PHP files (see this wiki page for compilation and configuration details).
If you check the NSAPI developer guide, the client certificate is placed as a request variable named auth-cert. This var contains the certificate in DER format but encoded into text with BASE64. The nsapi.c file in PHP source registers this variable in its own environment with another name CLIENT_CERT. So you can access the certificate (DER + BASE64) like this $_SERVER['CLIENT_CERT']. Then the certificate can be parsed using some PHP/SSL methods. This sample PHP file performs exactly what I said.
<?php // header to plain/text header("content-type: text/plain"); // print server vars print("SERVER vars:\n"); print_r($_SERVER); // get the headers print("\n\nHEADERS:\n"); print_r(getallheaders()); // print certificate parsed by SSL $pem = "-----BEGIN CERTIFICATE-----\n" . $_SERVER['CLIENT_CERT'] . "\n-----END CERTIFICATE-----\n"; $cert = openssl_x509_parse($pem); print("\n\nCertificate parsed by OpenSSL:\n"); print_r($cert); // print some variables from the cert print("\n\nGetting some data from the parsed cert:\n"); print("name: " . $cert['name'] . "\n"); print("subject->CN: " . $cert['subject']['CN'] . "\n"); print("validFrom: " . $cert['validFrom'] . "\n"); ?>
As you see the server variable is converted into SSL PEM format (as the certificate is already in BASE64, setting the header and footer line is the only step to transform it into PEM) and then it is parsed and stored into an array by openssl_x509_parse function. With this array any field of the certificate can be retrieved easily.
When finally the application went to production I realized that the PHP web server was behind a proxy (another difference, this time an architectural one). In this case the certificate is not received in the commented way, in this architecture all the SSL stuff (certificate exchange process included) is managed by the proxy and the client certificate is usually forwarded to the backend server using a header (proxy-auth-cert in case of iPlanet Web Proxy Server). In this solution getallheaders PHP function should be used to get the certificate from the specified header and construct the PEM variable exactly in the same way I did in the previous example.
So with PHP and Oracle Iplanet Web Server (any NSAPI server in general although I do not know anyone else) you obviously cannot use mod_ssl/Apache environment variables, there is another one called CLIENT_CERT which is the certificate itself in DER + BASE64 (behind a proxy the certificate is usually placed in a header). Once this fact is known it is easy to find a way of getting any certificate field with PHP code (I used a PHP/SSL function to parse the certificate into an array of variables). Please if you are a developer, do not start coding before all the versions, software stack and architecture are perfectly defined, you usually will work twice or even more (and you will drive a lot of people crazy too, people like me).
Never lose hope!
Comments