25 private static function _aesDecrypt($ciphertext, $secret)
27 if (!is_string($ciphertext)) {
28 throw new \InvalidArgumentException(
29 'Input parameter "$ciphertext" must be a string with more than 48 characters.' 33 $len = mb_strlen($ciphertext,
'8bit');
35 throw new \InvalidArgumentException(
36 'Input parameter "$ciphertext" must be a string with more than 48 characters.' 39 if (!function_exists(
"openssl_decrypt")) {
40 throw new \SimpleSAML_Error_Exception(
"The openssl PHP module is not loaded.");
44 $key = openssl_digest($secret,
'sha512');
46 $hmac = mb_substr($ciphertext, 0, 32,
'8bit');
47 $iv = mb_substr($ciphertext, 32, 16,
'8bit');
48 $msg = mb_substr($ciphertext, 48, $len - 48,
'8bit');
51 if (self::secureCompare(hash_hmac(
'sha256', $iv.$msg, substr(
$key, 64, 64),
true), $hmac)) {
52 $plaintext = openssl_decrypt(
56 defined(
'OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : 1,
60 if ($plaintext !==
false) {
65 throw new \SimpleSAML_Error_Exception(
"Failed to decrypt ciphertext.");
99 private static function _aesEncrypt(
$data, $secret)
101 if (!is_string(
$data)) {
102 throw new \InvalidArgumentException(
'Input parameter "$data" must be a string.');
105 if (!function_exists(
"openssl_encrypt")) {
106 throw new \SimpleSAML_Error_Exception(
'The openssl PHP module is not loaded.');
110 $key = openssl_digest($secret,
'sha512');
113 $iv = openssl_random_pseudo_bytes(16);
117 $ciphertext = openssl_encrypt(
121 defined(
'OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : 1,
125 if ($ciphertext ===
false) {
126 throw new \SimpleSAML_Error_Exception(
"Failed to encrypt plaintext.");
130 return hash_hmac(
'sha256', $iv.$ciphertext, substr(
$key, 64, 64),
true).$iv.$ciphertext;
162 return "-----BEGIN ".$type.
"-----\n".
163 chunk_split(base64_encode($der), 64,
"\n").
164 "-----END ".$type.
"-----\n";
197 if (!is_bool($required) || !is_string($prefix) || !is_bool($full_path)) {
198 throw new \InvalidArgumentException(
'Invalid input parameters.');
201 $file = $metadata->
getString($prefix.
'privatekey', null);
202 if ($file === null) {
205 throw new \SimpleSAML_Error_Exception(
'No private key found in metadata.');
215 $data = @file_get_contents($file);
216 if (
$data ===
false) {
217 throw new \SimpleSAML_Error_Exception(
'Unable to load private key from file "'.$file.
'"');
224 if ($metadata->
hasValue($prefix.
'privatekey_pass')) {
225 $ret[
'password'] = $metadata->
getString($prefix.
'privatekey_pass');
267 if (!is_bool($required) || !is_string($prefix)) {
268 throw new \InvalidArgumentException(
'Invalid input parameters.');
274 if ($key[
'type'] !==
'X509Certificate') {
277 if ($key[
'signing'] !==
true) {
280 $certData = $key[
'X509Certificate'];
281 $pem =
"-----BEGIN CERTIFICATE-----\n".
282 chunk_split($certData, 64).
283 "-----END CERTIFICATE-----\n";
284 $certFingerprint = strtolower(sha1(base64_decode($certData)));
287 'certData' => $certData,
289 'certFingerprint' => array($certFingerprint),
293 } elseif ($metadata->
hasValue($prefix.
'certFingerprint')) {
298 foreach ($fps as &$fp) {
299 assert(is_string($fp));
300 $fp = strtolower(str_replace(
':',
'', $fp));
307 return array(
'certFingerprint' => $fps);
312 throw new \SimpleSAML_Error_Exception(
'No public key / certificate found in metadata.');
330 $begin =
"-----BEGIN ";
332 $lines = explode(
"\n", $pem);
333 $last = count($lines) - 1;
335 if (strpos($lines[0], $begin) !== 0) {
336 throw new \InvalidArgumentException(
"pem2der: input is not encoded in PEM format.");
339 if (strpos($lines[$last],
$end) !== 0) {
340 throw new \InvalidArgumentException(
"pem2der: input is not encoded in PEM format.");
342 unset($lines[$last]);
344 return base64_decode(implode($lines));
367 if (!is_string($algorithm) || !is_string(
$password)) {
368 throw new \InvalidArgumentException(
'Invalid input parameters.');
372 if (in_array(strtolower($algorithm), hash_algos(),
true)) {
373 $alg_str =
'{'.str_replace(
'SHA1',
'SHA', $algorithm).
'}';
375 return $alg_str.base64_encode($hash);
379 if ($salt === null) {
381 $bytes = ($algorithm ==
'SSHA1') ? 4 : 8;
382 $salt = openssl_random_pseudo_bytes($bytes);
385 if ($algorithm[0] ==
'S' && in_array(substr(strtolower($algorithm), 1), hash_algos(),
true)) {
386 $alg = substr(strtolower($algorithm), 1);
387 $alg_str =
'{'.str_replace(
'SSHA1',
'SSHA', $algorithm).
'}';
389 return $alg_str.base64_encode($hash.$salt);
392 throw new \SimpleSAML_Error_Exception(
'Hashing algorithm \''.strtolower($algorithm).
'\' is not supported
'); 407 public static function secureCompare($known, $user) 409 if (function_exists('hash_equals
')) { 410 // use hash_equals() if available (PHP >= 5.6) 411 return hash_equals($known, $user); 414 // compare manually in constant time 415 $len = mb_strlen($known, '8bit
'); // see mbstring.func_overload 416 if ($len !== mb_strlen($user, '8bit
')) { 417 return false; // length differs 420 for ($i = 0; $i < $len; $i++) { 421 $diff |= ord($known[$i]) ^ ord($user[$i]); 423 // if all the bytes in $a and $b are identical, $diff should be equal to 0 440 public static function pwValid($hash, $password) 442 if (!is_string($hash) || !is_string($password)) { 443 throw new \InvalidArgumentException('Invalid
input parameters.
'); 446 // match algorithm string (e.g. '{SSHA256}
', '{MD5}
') 447 if (preg_match('/^{(.*?)}(.*)$/
', $hash, $matches)) { 448 // LDAP compatibility 449 $alg = preg_replace('/^(S?SHA)$/
', '${1}1
', $matches[1]); 452 if (in_array(strtolower($alg), hash_algos(), true)) { 453 return self::secureCompare($hash, self::pwHash($password, $alg)); 457 if ($alg[0] === 'S
' && in_array(substr(strtolower($alg), 1), hash_algos(), true)) { 458 $php_alg = substr(strtolower($alg), 1); 460 // get hash length of this algorithm to learn how long the salt is 461 $hash_length = strlen(hash($php_alg, '', true)); 462 $salt = substr(base64_decode($matches[2]), $hash_length); 463 return self::secureCompare($hash, self::pwHash($password, $alg, $salt)); 466 return $hash === $password; 469 throw new \SimpleSAML_Error_Exception('Hashing algorithm \
''.strtolower($alg).
'\' is not supported
'); static pwHash($password, $algorithm, $salt=null)
This function hashes a password with a given algorithm.
static aesEncrypt($data)
Encrypt data using AES-256-CBC and the system-wide secret salt as key.
static der2pem($der, $type='CERTIFICATE')
Convert data from DER to PEM encoding.
hasValue($name)
Check whether a key in the configuration exists or not.
static aesDecrypt($ciphertext)
Decrypt data using AES-256-CBC and the system-wide secret salt as key.
static getSecretSalt()
Retrieve the secret salt.
$metadata['__DYNAMIC:1__']
static loadPrivateKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='', $full_path=false)
Load a private key from metadata.
static pem2der($pem)
Convert from PEM to DER encoding.
static loadPublicKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Get public key or certificate from metadata.
getArrayizeString($name, $default=self::REQUIRED_OPTION)
This function retrieves a configuration option with a string or an array of strings.
getPublicKeys($use=null, $required=false, $prefix='')
Get public key from metadata.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
static getCertPath($path)
Resolves a path that may be relative to the cert-directory.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.