From 9f3f55f0a1e90dc14cca7607562fc32731461e80 Mon Sep 17 00:00:00 2001 From: Jaime Perez Crespo <jaime.perez@uninett.no> Date: Thu, 16 Apr 2015 13:10:02 +0200 Subject: [PATCH] Move loadPublicKey() and loadPrivateKey() in SimpleSAML_Utilities to SimpleSAML_Utils_Crypto. Mark the old ones as deprecated and schedule them for removal in 2.0. --- lib/SimpleSAML/Bindings/Shib13/HTTPPost.php | 4 +- lib/SimpleSAML/Utilities.php | 14 +- lib/SimpleSAML/Utils/Crypto.php | 136 ++++++++++++++++++++ modules/adfs/www/idp/metadata.php | 6 +- modules/saml/lib/Message.php | 12 +- modules/saml/www/idp/certs.php | 6 +- modules/saml/www/sp/metadata.php | 4 +- www/saml2/idp/metadata.php | 6 +- www/shib13/idp/metadata.php | 4 +- 9 files changed, 165 insertions(+), 27 deletions(-) diff --git a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php index 4a7b54ae8..7cb87a8f7 100644 --- a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php +++ b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php @@ -29,8 +29,8 @@ class SimpleSAML_Bindings_Shib13_HTTPPost { SimpleSAML_Utilities::validateXMLDocument($response, 'saml11'); - $privatekey = SimpleSAML_Utilities::loadPrivateKey($idpmd, TRUE); - $publickey = SimpleSAML_Utilities::loadPublicKey($idpmd, TRUE); + $privatekey = SimpleSAML_Utils_Crypto::loadPrivateKey($idpmd, TRUE); + $publickey = SimpleSAML_Utils_Crypto::loadPublicKey($idpmd, TRUE); $responsedom = new DOMDocument(); $responsedom->loadXML(str_replace ("\r", "", $response)); diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index fc949067e..996158360 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -1371,6 +1371,7 @@ class SimpleSAML_Utilities { * array. Defaults to ''. * @return array|NULL Public key or certificate data, or NULL if no public key or * certificate was found. + * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::loadPublicKey() instead. */ public static function loadPublicKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') { assert('is_bool($required)'); @@ -1441,6 +1442,7 @@ class SimpleSAML_Utilities { * @param string $prefix The prefix which should be used when reading from the metadata * array. Defaults to ''. * @return array|NULL Extracted private key, or NULL if no private key is present. + * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::loadPrivateKey() instead. */ public static function loadPrivateKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') { assert('is_bool($required)'); @@ -1585,8 +1587,8 @@ class SimpleSAML_Utilities { /** * Input is single value or array, returns an array. - * - * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Arrays::arrayize() instead. + * + * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Arrays::arrayize() instead. */ public static function arrayize($data, $index = 0) { if (is_array($data)) { @@ -1933,8 +1935,8 @@ class SimpleSAML_Utilities { * * @param string $filename The name of the file. * @param string $data The data we should write to the file. - * - * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML_Utils_System::writeFile() instead. + * + * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML_Utils_System::writeFile() instead. */ public static function writeFile($filename, $data, $mode=0600) { assert('is_string($filename)'); @@ -2244,7 +2246,7 @@ class SimpleSAML_Utilities { * * @param string $clear Data to encrypt. * @return array The encrypted data and IV. - * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::aesEncrypt() instead. + * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::aesEncrypt() instead. */ public static function aesEncrypt($clear) { assert('is_string($clear)'); @@ -2281,7 +2283,7 @@ class SimpleSAML_Utilities { * @param $data Encrypted data. * @param $iv IV of encrypted data. * @return string The decrypted data. - * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::aesDecrypt() instead. + * @deprecated This function will be removed in SSP 2.0. Please use SimpleSAML_Utils_Crypto::aesDecrypt() instead. */ public static function aesDecrypt($encData) { assert('is_string($encData)'); diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php index b70fb991e..565c93e0a 100644 --- a/lib/SimpleSAML/Utils/Crypto.php +++ b/lib/SimpleSAML/Utils/Crypto.php @@ -89,6 +89,142 @@ class SimpleSAML_Utils_Crypto return $iv.$data; } + + /** + * Load a private key from metadata. + * + * This function loads a private key from a metadata array. It looks for the following elements: + * - 'privatekey': Name of a private key file in the cert-directory. + * - 'privatekey_pass': Password for the private key. + * + * It returns and array with the following elements: + * - 'PEM': Data for the private key, in PEM-format. + * - 'password': Password for the private key. + * + * @param SimpleSAML_Configuration $metadata The metadata array the private key should be loaded from. + * @param bool $required Whether the private key is required. If this is true, a + * missing key will cause an exception. Defaults to false. + * @param string $prefix The prefix which should be used when reading from the metadata + * array. Defaults to ''. + * + * @return array|NULL Extracted private key, or NULL if no private key is present. + * @throws SimpleSAML_Error_Exception If no private key is found in the metadata, or it was not possible to load it. + * + * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no> + * @author Olav Morken, UNINETT AS <olav.morken@uninett.no> + */ + public static function loadPrivateKey(SimpleSAML_Configuration $metadata, $required = false, $prefix = '') + { + if (!is_bool($required) || !is_string($prefix)) { + throw new SimpleSAML_Error_Exception('Invalid input parameters.'); + } + + $file = $metadata->getString($prefix.'privatekey', null); + if ($file === null) { + // no private key found + if ($required) { + throw new SimpleSAML_Error_Exception('No private key found in metadata.'); + } else { + return null; + } + } + + $file = SimpleSAML_Utilities::resolveCert($file); + $data = @file_get_contents($file); + if ($data === false) { + throw new SimpleSAML_Error_Exception('Unable to load private key from file "'.$file.'"'); + } + + $ret = array( + 'PEM' => $data, + ); + + if ($metadata->hasValue($prefix.'privatekey_pass')) { + $ret['password'] = $metadata->getString($prefix.'privatekey_pass'); + } + + return $ret; + } + + /** + * Get public key or certificate from metadata. + * + * This function implements a function to retrieve the public key or certificate from a metadata array. + * + * It will search for the following elements in the metadata: + * - 'certData': The certificate as a base64-encoded string. + * - 'certificate': A file with a certificate or public key in PEM-format. + * - 'certFingerprint': The fingerprint of the certificate. Can be a single fingerprint, or an array of multiple + * valid fingerprints. + * + * This function will return an array with these elements: + * - 'PEM': The public key/certificate in PEM-encoding. + * - 'certData': The certificate data, base64 encoded, on a single line. (Only present if this is a certificate.) + * - 'certFingerprint': Array of valid certificate fingerprints. (Only present if this is a certificate.) + * + * @param SimpleSAML_Configuration $metadata The metadata. + * @param bool $required Whether the private key is required. If this is TRUE, a missing key + * will cause an exception. Default is FALSE. + * @param string $prefix The prefix which should be used when reading from the metadata array. + * Defaults to ''. + * + * @return array|NULL Public key or certificate data, or NULL if no public key or certificate was found. + * + * @throws SimpleSAML_Error_Exception If no private key is found in the metadata, or it was not possible to load it. + * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no> + * @author Olav Morken, UNINETT AS <olav.morken@uninett.no> + * @author Lasse Birnbaum Jensen + */ + public static function loadPublicKey(SimpleSAML_Configuration $metadata, $required = false, $prefix = '') + { + assert('is_bool($required)'); + assert('is_string($prefix)'); + + $keys = $metadata->getPublicKeys(null, false, $prefix); + if ($keys !== null) { + foreach ($keys as $key) { + if ($key['type'] !== 'X509Certificate') { + continue; + } + if ($key['signing'] !== true) { + continue; + } + $certData = $key['X509Certificate']; + $pem = "-----BEGIN CERTIFICATE-----\n". + chunk_split($certData, 64). + "-----END CERTIFICATE-----\n"; + $certFingerprint = strtolower(sha1(base64_decode($certData))); + + return array( + 'certData' => $certData, + 'PEM' => $pem, + 'certFingerprint' => array($certFingerprint), + ); + } + // no valid key found + } elseif ($metadata->hasValue($prefix.'certFingerprint')) { + // we only have a fingerprint available + $fps = $metadata->getArrayizeString($prefix.'certFingerprint'); + + // normalize fingerprint(s) - lowercase and no colons + foreach ($fps as &$fp) { + assert('is_string($fp)'); + $fp = strtolower(str_replace(':', '', $fp)); + } + + // We can't build a full certificate from a fingerprint, and may as well return an array with only the + //fingerprint(s) immediately. + return array('certFingerprint' => $fps); + } + + // no public key/certificate available + if ($required) { + throw new SimpleSAML_Error_Exception('No public key / certificate found in metadata.'); + } else { + return null; + } + } + /** * This function hashes a password with a given algorithm. * diff --git a/modules/adfs/www/idp/metadata.php b/modules/adfs/www/idp/metadata.php index 8dcd6bab8..40ddfa547 100644 --- a/modules/adfs/www/idp/metadata.php +++ b/modules/adfs/www/idp/metadata.php @@ -20,7 +20,7 @@ try { $availableCerts = array(); $keys = array(); - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, FALSE, 'new_'); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, FALSE, 'new_'); if ($certInfo !== NULL) { $availableCerts['new_idp.crt'] = $certInfo; $keys[] = array( @@ -34,7 +34,7 @@ try { $hasNewCert = FALSE; } - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE); $availableCerts['idp.crt'] = $certInfo; $keys[] = array( 'type' => 'X509Certificate', @@ -44,7 +44,7 @@ try { ); if ($idpmeta->hasValue('https.certificate')) { - $httpsCert = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE, 'https.'); + $httpsCert = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE, 'https.'); assert('isset($httpsCert["certData"])'); $availableCerts['https.crt'] = $httpsCert; $keys[] = array( diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php index 0d8efe143..6e36d3623 100644 --- a/modules/saml/lib/Message.php +++ b/modules/saml/lib/Message.php @@ -21,11 +21,11 @@ class sspmod_saml_Message { $dstPrivateKey = $dstMetadata->getString('signature.privatekey', NULL); if ($dstPrivateKey !== NULL) { - $keyArray = SimpleSAML_Utilities::loadPrivateKey($dstMetadata, TRUE, 'signature.'); - $certArray = SimpleSAML_Utilities::loadPublicKey($dstMetadata, FALSE, 'signature.'); + $keyArray = SimpleSAML_Utils_Crypto::loadPrivateKey($dstMetadata, TRUE, 'signature.'); + $certArray = SimpleSAML_Utils_Crypto::loadPublicKey($dstMetadata, FALSE, 'signature.'); } else { - $keyArray = SimpleSAML_Utilities::loadPrivateKey($srcMetadata, TRUE); - $certArray = SimpleSAML_Utilities::loadPublicKey($srcMetadata, FALSE); + $keyArray = SimpleSAML_Utils_Crypto::loadPrivateKey($srcMetadata, TRUE); + $certArray = SimpleSAML_Utils_Crypto::loadPublicKey($srcMetadata, FALSE); } $algo = $dstMetadata->getString('signature.algorithm', NULL); @@ -281,7 +281,7 @@ class sspmod_saml_Message { $keys = array(); /* Load the new private key if it exists. */ - $keyArray = SimpleSAML_Utilities::loadPrivateKey($dstMetadata, FALSE, 'new_'); + $keyArray = SimpleSAML_Utils_Crypto::loadPrivateKey($dstMetadata, FALSE, 'new_'); if ($keyArray !== NULL) { assert('isset($keyArray["PEM"])'); @@ -294,7 +294,7 @@ class sspmod_saml_Message { } /* Find the existing private key. */ - $keyArray = SimpleSAML_Utilities::loadPrivateKey($dstMetadata, TRUE); + $keyArray = SimpleSAML_Utils_Crypto::loadPrivateKey($dstMetadata, TRUE); assert('isset($keyArray["PEM"])'); $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private')); diff --git a/modules/saml/www/idp/certs.php b/modules/saml/www/idp/certs.php index 328cda4d2..5db029c50 100644 --- a/modules/saml/www/idp/certs.php +++ b/modules/saml/www/idp/certs.php @@ -17,13 +17,13 @@ $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted'); switch($_SERVER['PATH_INFO']) { case '/new_idp.crt': - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, FALSE, 'new_'); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, FALSE, 'new_'); break; case '/idp.crt': - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE); break; case '/https.crt': - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE, 'https.'); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE, 'https.'); break; default: throw new SimpleSAML_Error_NotFound('Unknown certificate.'); diff --git a/modules/saml/www/sp/metadata.php b/modules/saml/www/sp/metadata.php index 6547db14c..72d2cdd66 100644 --- a/modules/saml/www/sp/metadata.php +++ b/modules/saml/www/sp/metadata.php @@ -91,7 +91,7 @@ foreach ($assertionsconsumerservices as $services) { $metaArray20['AssertionConsumerService'] = $eps; $keys = array(); -$certInfo = SimpleSAML_Utilities::loadPublicKey($spconfig, FALSE, 'new_'); +$certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($spconfig, FALSE, 'new_'); if ($certInfo !== NULL && array_key_exists('certData', $certInfo)) { $hasNewCert = TRUE; @@ -107,7 +107,7 @@ if ($certInfo !== NULL && array_key_exists('certData', $certInfo)) { $hasNewCert = FALSE; } -$certInfo = SimpleSAML_Utilities::loadPublicKey($spconfig); +$certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($spconfig); if ($certInfo !== NULL && array_key_exists('certData', $certInfo)) { $certData = $certInfo['certData']; diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php index de515e806..2707adae6 100644 --- a/www/saml2/idp/metadata.php +++ b/www/saml2/idp/metadata.php @@ -22,7 +22,7 @@ try { $availableCerts = array(); $keys = array(); - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, FALSE, 'new_'); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, FALSE, 'new_'); if ($certInfo !== NULL) { $availableCerts['new_idp.crt'] = $certInfo; $keys[] = array( @@ -36,7 +36,7 @@ try { $hasNewCert = FALSE; } - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE); $availableCerts['idp.crt'] = $certInfo; $keys[] = array( 'type' => 'X509Certificate', @@ -46,7 +46,7 @@ try { ); if ($idpmeta->hasValue('https.certificate')) { - $httpsCert = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE, 'https.'); + $httpsCert = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE, 'https.'); assert('isset($httpsCert["certData"])'); $availableCerts['https.crt'] = $httpsCert; $keys[] = array( diff --git a/www/shib13/idp/metadata.php b/www/shib13/idp/metadata.php index e04345e20..ee35c6f6f 100644 --- a/www/shib13/idp/metadata.php +++ b/www/shib13/idp/metadata.php @@ -21,7 +21,7 @@ try { $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'shib13-idp-hosted'); $keys = array(); - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, FALSE, 'new_'); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, FALSE, 'new_'); if ($certInfo !== NULL) { $keys[] = array( 'type' => 'X509Certificate', @@ -31,7 +31,7 @@ try { ); } - $certInfo = SimpleSAML_Utilities::loadPublicKey($idpmeta, TRUE); + $certInfo = SimpleSAML_Utils_Crypto::loadPublicKey($idpmeta, TRUE); $keys[] = array( 'type' => 'X509Certificate', 'signing' => TRUE, -- GitLab