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')) {
295 $fps =
$metadata->getArrayizeString($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
');
$metadata['__DYNAMIC:1__']
An exception for terminatinating execution or to throw for unit testing.
static getSecretSalt()
Retrieve the secret salt.
static getCertPath($path)
Resolves a path that may be relative to the cert-directory.
static pwHash($password, $algorithm, $salt=null)
This function hashes a password with a given algorithm.
static loadPublicKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Get public key or certificate from metadata.
static der2pem($der, $type='CERTIFICATE')
Convert data from DER to PEM encoding.
static aesEncrypt($data)
Encrypt data using AES-256-CBC and the system-wide secret salt as key.
static loadPrivateKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='', $full_path=false)
Load a private key from metadata.
static aesDecrypt($ciphertext)
Decrypt data using AES-256-CBC and the system-wide secret salt as key.
static pem2der($pem)
Convert from PEM to DER encoding.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.