From 13b7d1883dd87ffe462159735157ea972e8fb1f2 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Fri, 6 Jun 2008 08:30:03 +0000
Subject: [PATCH] Add passphrase support to private key loading.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@613 44740490-163a-0410-bde0-09ae8108e29a
---
 config-templates/config.php                   |  1 +
 .../source/simplesamlphp-advancedfeatures.xml | 15 +++++++---
 docs/source/simplesamlphp-idp.xml             |  8 +++++
 docs/source/simplesamlphp-sp.xml              | 18 +++++++++++
 lib/SimpleSAML/Bindings/SAML20/HTTPPost.php   |  7 +++++
 .../Bindings/SAML20/HTTPRedirect.php          |  8 +++++
 lib/SimpleSAML/Bindings/Shib13/HTTPPost.php   | 11 +++++--
 lib/SimpleSAML/Metadata/Signer.php            | 30 ++++++++++++++++---
 www/admin/metadata.php                        | 10 +++----
 9 files changed, 92 insertions(+), 16 deletions(-)

diff --git a/config-templates/config.php b/config-templates/config.php
index d9fa2dde5..812e93985 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -382,6 +382,7 @@ $config = array (
 	 * if those aren't set, signing of metadata will fail.
 	 */
 	'metadata.sign.privatekey' => NULL,
+	'metadata.sign.privatekey_pass' => NULL,
 	'metadata.sign.certificate' => NULL,
 
 
diff --git a/docs/source/simplesamlphp-advancedfeatures.xml b/docs/source/simplesamlphp-advancedfeatures.xml
index 26bd79974..d8d2f7429 100644
--- a/docs/source/simplesamlphp-advancedfeatures.xml
+++ b/docs/source/simplesamlphp-advancedfeatures.xml
@@ -369,7 +369,7 @@ foreach($_SERVER as $key=&gt;$value) {
     <title>Metadata signing</title>
 
     <para>simpleSAMLphp supports signing of the metadata it generates.
-    Metadata signing is configured by three options:</para>
+    Metadata signing is configured by four options:</para>
 
     <itemizedlist>
       <listitem>
@@ -384,6 +384,13 @@ foreach($_SERVER as $key=&gt;$value) {
         file must exist in in the <literal>cert</literal> directory.</para>
       </listitem>
 
+      <listitem>
+        <para><literal>metadata.sign.privatekey_pass</literal>: Passphrase
+        which should be used to open the private key. This parameter is
+        optional, and should be left out if the private key is
+        unencrypted.</para>
+      </listitem>
+
       <listitem>
         <para><literal>metadata.sign.certificate</literal>: Name of the file
         with the certificate which matches the private key. This file must
@@ -400,9 +407,9 @@ foreach($_SERVER as $key=&gt;$value) {
     <para>There is also an additional fallback for the private key and the
     certificate. If <literal>metadata.sign.privatekey</literal> and
     <literal>metadata.sign.certificate</literal> isn't configured,
-    simpleSAMLphp will use the <literal>privatekey</literal> and
-    <literal>certificate</literal> options in the metadata for the
-    SP/IdP.</para>
+    simpleSAMLphp will use the <literal>privatekey</literal>,
+    <literal>privatekey_pass</literal> and <literal>certificate</literal>
+    options in the metadata for the SP/IdP.</para>
   </section>
 
 
diff --git a/docs/source/simplesamlphp-idp.xml b/docs/source/simplesamlphp-idp.xml
index 2469347cf..689f26b4a 100644
--- a/docs/source/simplesamlphp-idp.xml
+++ b/docs/source/simplesamlphp-idp.xml
@@ -401,6 +401,14 @@ openssl x509 -req -days 60 -in server2.csr -signkey server2.key -out server2.crt
         <title>Optional metadata fields</title>
 
         <glosslist>
+          <glossentry>
+            <glossterm>privatekey_pass</glossterm>
+
+            <glossdef>
+              <para>Passphrase for the private key.</para>
+            </glossdef>
+          </glossentry>
+
           <glossentry>
             <glossterm>requireconsent</glossterm>
 
diff --git a/docs/source/simplesamlphp-sp.xml b/docs/source/simplesamlphp-sp.xml
index f21449f92..51d272805 100644
--- a/docs/source/simplesamlphp-sp.xml
+++ b/docs/source/simplesamlphp-sp.xml
@@ -326,6 +326,15 @@
               generate SAML 2.0 Metadata for export to the IdP.</para>
             </glossdef>
           </glossentry>
+
+          <glossentry>
+            <glossterm>privatekey_pass</glossterm>
+
+            <glossdef>
+              <para>Optional field with the passphrase for the private
+              key.</para>
+            </glossdef>
+          </glossentry>
         </glosslist>
 
         <example>
@@ -560,6 +569,15 @@
               key.</para>
             </glossdef>
           </glossentry>
+
+          <glossentry>
+            <glossterm>privatekey_pass</glossterm>
+
+            <glossdef>
+              <para>Optional field with the passphrase for the private
+              key.</para>
+            </glossdef>
+          </glossentry>
         </glosslist>
 
         <example>
diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPPost.php b/lib/SimpleSAML/Bindings/SAML20/HTTPPost.php
index 24f1bd8b3..ed9ee715a 100644
--- a/lib/SimpleSAML/Bindings/SAML20/HTTPPost.php
+++ b/lib/SimpleSAML/Bindings/SAML20/HTTPPost.php
@@ -118,6 +118,13 @@ class SimpleSAML_Bindings_SAML20_HTTPPost {
 		/* create new XMLSecKey using RSA-SHA-1 and type is private key */
 		$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
 		
+		/* Set the passphrase which should be used to open the key, if this attribute is
+		 * set in the metadata.
+		 */
+		if(array_key_exists('privatekey_pass', $idpmd)) {
+			$objKey->passphrase = $idpmd['privatekey_pass'];
+		}
+
 		/* load the private key from file - last arg is bool if key in file (TRUE) or is string (FALSE) */
 		$objKey->loadKey($privatekey,TRUE);
 				
diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
index 52436bd3e..2bf82072d 100644
--- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
+++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php
@@ -52,6 +52,14 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect {
 		$query = $query . "&" . "SigAlg=" . urlencode($algURI);
 		
 		$xmlseckey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
+
+		/* Set the passphrase which should be used to open the key, if this attribute is
+		 * set in the metadata.
+		 */
+		if(array_key_exists('privatekey_pass', $md)) {
+			$xmlseckey->passphrase = $md['privatekey_pass'];
+		}
+
 		$xmlseckey->loadKey($privatekey,TRUE);
         $signature = $xmlseckey->signData($query);
                 
diff --git a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
index 189a9ee3c..37adb633a 100644
--- a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
+++ b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
@@ -118,9 +118,14 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
 
 		/* create new XMLSecKey using RSA-SHA-1 and type is private key */
 		$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
-		
-		//$objKey->passphrase = '1234';
-		
+
+		/* Set the passphrase which should be used to open the key, if this attribute is
+		 * set in the metadata.
+		 */
+		if(array_key_exists('privatekey_pass', $idpmd)) {
+			$objKey->passphrase = $idpmd['privatekey_pass'];
+		}
+
 		/* load the private key from file - last arg is bool if key in file (TRUE) or is string (FALSE) */
 		#$objKey->loadKey($privatekey_pem,false);
 		$objKey->loadKey($privatek,false);
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
index eacf55cbe..8d433aea0 100644
--- a/lib/SimpleSAML/Metadata/Signer.php
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -16,7 +16,7 @@ class SimpleSAML_Metadata_Signer {
 	 * @param $config  Our SimpleSAML_Configuration instance.
 	 * @param $entityMetadata  The metadata of the entity.
 	 * @param $type  A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
-	 * @return An associative array with the keys 'privatekey' and 'certificate'.
+	 * @return An associative array with the keys 'privatekey', 'certificate', and optionally 'privatekey_pass'.
 	 */
 	private static function findKeyCert($config, $entityMetadata, $type) {
 
@@ -32,10 +32,17 @@ class SimpleSAML_Metadata_Signer {
 					' the ' . $type . ' "' . $entityMetadata['entityid'] . '". If one of' .
 					' these options is specified, then the other must also be specified.');
 			}
-			return array(
+
+			$ret = array(
 				'privatekey' => $entityMetadata['metadata.sign.privatekey'],
 				'certificate' => $entityMetadata['metadata.sign.certificate']
 				);
+
+			if(array_key_exists('metadata.sign.privatekey_pass', $entityMetadata)) {
+				$ret['privatekey_pass'] = $entityMetadata['metadata.sign.privatekey_pass'];
+			}
+
+			return $ret;
 		}
 
 		/* Then we look for default values in the global configuration. */
@@ -48,7 +55,14 @@ class SimpleSAML_Metadata_Signer {
 					' configuration. If one of these options is specified, then the other'.
 					' must also be specified.');
 			}
-			return array('privatekey' => $privatekey, 'certificate' => $certificate);
+			$ret = array('privatekey' => $privatekey, 'certificate' => $certificate);
+
+			$privatekey_pass = $config->getValue('metadata.sign.privatekey_pass', NULL);
+			if($privatekey_pass !== NULL) {
+				$ret['privatekey_pass'] = $privatekey_pass;
+			}
+
+			return $ret;
 		}
 
 		/* As a last resort we attempt to use the privatekey and certificate option from the metadata. */
@@ -63,11 +77,16 @@ class SimpleSAML_Metadata_Signer {
 					' from this entity.');
 			}
 
-			return array(
+			$ret = array(
 				'privatekey' => $entityMetadata['privatekey'],
 				'certificate' => $entityMetadata['certificate']
 				);
 
+			if(array_key_exists('privatekey_pass', $entityMetadata)) {
+				$ret['privatekey_pass'] = $entityMetadata['privatekey_pass'];
+			}
+
+			return $ret;
 		}
 
 		throw new Exception('Could not find what key & certificate should be used to sign the metadata' .
@@ -149,6 +168,9 @@ class SimpleSAML_Metadata_Signer {
 
 		/* Load the private key. */
 		$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
+		if(array_key_exists('privatekey_pass', $keyCertFiles)) {
+			$objKey->passphrase = $keyCertFiles['privatekey_pass'];
+		}
 		$objKey->loadKey($keyData, FALSE);
 
 		/* Get the EntityDescriptor node we should sign. */
diff --git a/www/admin/metadata.php b/www/admin/metadata.php
index 765f6174f..ebf0972f0 100644
--- a/www/admin/metadata.php
+++ b/www/admin/metadata.php
@@ -29,7 +29,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'host'),
-				array('request.signing','certificate','privatekey', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
+				array('request.signing','certificate','privatekey', 'privatekey_pass', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.saml20-sp-hosted'] = $results;
@@ -52,7 +52,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'host', 'privatekey', 'certificate', 'auth'),
-				array('requireconsent','request.signing', 'authority', 'attributemap', 'attributealter', 'userid.attribute', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
+				array('requireconsent','request.signing', 'privatekey_pass', 'authority', 'attributemap', 'attributealter', 'userid.attribute', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.saml20-idp-hosted'] = $results;
@@ -89,7 +89,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'SingleSignOnService', 'certFingerprint'),
-				array('name', 'description', 'base64attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
+				array('name', 'description', 'base64attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.shib13-idp-remote'] = $results;
@@ -102,7 +102,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'host', 'privatekey', 'certificate', 'auth'),
-				array('requireconsent', 'authority')
+				array('requireconsent', 'authority', 'privatekey_pass')
 			);
 		}
 		$et->data['metadata.shib13-idp-hosted'] = $results;
@@ -112,7 +112,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'AssertionConsumerService'),
-				array('base64attributes', 'audience', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
+				array('base64attributes', 'audience', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.shib13-sp-remote'] = $results;
-- 
GitLab