| | 37 | * Hashes a password using a loop of sha1 hashes and a salt, making rainbow table attacks infeasible |
|---|
| | 38 | * |
|---|
| | 39 | * @param string $password The password to hash |
|---|
| | 40 | * @return string An 80 character string of the Flourish fingerprint, salt and hashed password |
|---|
| | 41 | */ |
|---|
| | 42 | static public function hashPassword($password) |
|---|
| | 43 | { |
|---|
| | 44 | $salt = self::randomString(10); |
|---|
| | 45 | |
|---|
| | 46 | return self::hashWithSalt($password, $salt); |
|---|
| | 47 | } |
|---|
| | 48 | |
|---|
| | 49 | |
|---|
| | 50 | /** |
|---|
| | 51 | * Performs a large iteration of hashing a string with a salt |
|---|
| | 52 | * |
|---|
| | 53 | * @param string $source The string to hash |
|---|
| | 54 | * @param string $salt The salt for the hash |
|---|
| | 55 | * @return string An 80 character string of the Flourish fingerprint, salt and hashed password |
|---|
| | 56 | */ |
|---|
| | 57 | static private function hashWithSalt($source, $salt) |
|---|
| | 58 | { |
|---|
| | 59 | $sha1 = sha1($salt . $source); |
|---|
| | 60 | for ($i = 0; $i < 1000; $i++) { |
|---|
| | 61 | $sha1 = sha1($sha1 . (($i % 2 == 0) ? $source : $salt)); |
|---|
| | 62 | } |
|---|
| | 63 | |
|---|
| | 64 | return 'fCryptography::password_hash#' . $salt . '#' . $sha1; |
|---|
| | 65 | } |
|---|
| | 66 | |
|---|
| | 67 | |
|---|
| | 68 | /** |
|---|
| | 69 | * Decrypts ciphertext encrypted using public-key encryption via {@link fCryptography::publicKeyEncrypt()} |
|---|
| | 70 | * |
|---|
| | 71 | * A public key (X.509 certificate) is required for encryption and a |
|---|
| | 72 | * private key (PEM) is required for decryption. |
|---|
| | 73 | * |
|---|
| | 74 | * @throws fValidationException |
|---|
| | 75 | * |
|---|
| | 76 | * @param string $ciphertext The content to be decrypted |
|---|
| | 77 | * @param string $private_key_file The path to a PEM-encoded private key |
|---|
| | 78 | * @param string $password The password for the private key |
|---|
| | 79 | * @return string The decrypted plaintext |
|---|
| | 80 | */ |
|---|
| | 81 | static public function publicKeyDecrypt($ciphertext, $private_key_file, $password) |
|---|
| | 82 | { |
|---|
| | 83 | self::verifyPublicKeyEnvironment(); |
|---|
| | 84 | |
|---|
| | 85 | if (!file_exists($private_key_file)) { |
|---|
| | 86 | fCore::toss( |
|---|
| | 87 | 'fProgrammerException', |
|---|
| | 88 | fGrammar::compose( |
|---|
| | 89 | 'The path to the PEM-encoded private key specified, %s, is not valid', |
|---|
| | 90 | fCore::dump($private_key_file) |
|---|
| | 91 | ) |
|---|
| | 92 | ); |
|---|
| | 93 | } |
|---|
| | 94 | if (!is_readable($private_key_file)) { |
|---|
| | 95 | fCore::toss( |
|---|
| | 96 | 'fEnvironmentException', |
|---|
| | 97 | fGrammar::compose( |
|---|
| | 98 | 'The PEM-encoded private key specified, %s, is not readable', |
|---|
| | 99 | fCore::dump($private_key_file) |
|---|
| | 100 | ) |
|---|
| | 101 | ); |
|---|
| | 102 | } |
|---|
| | 103 | |
|---|
| | 104 | $private_key = file_get_contents($private_key_file); |
|---|
| | 105 | $private_key_resource = openssl_pkey_get_private($private_key, $password); |
|---|
| | 106 | |
|---|
| | 107 | if ($private_key_resource === FALSE) { |
|---|
| | 108 | fCore::toss( |
|---|
| | 109 | 'fValidationException', |
|---|
| | 110 | fGrammar::compose( |
|---|
| | 111 | 'The private key password specified does not appear to be valid for the private key', |
|---|
| | 112 | fCore::dump($primary_key_file) |
|---|
| | 113 | ) |
|---|
| | 114 | ); |
|---|
| | 115 | } |
|---|
| | 116 | |
|---|
| | 117 | $elements = explode('#', $ciphertext); |
|---|
| | 118 | |
|---|
| | 119 | // We need to make sure this ciphertext came from here, otherwise we are gonna have issues decrypting it |
|---|
| | 120 | if (sizeof($elements) != 4 || $elements[0] != 'fCryptography::public') { |
|---|
| | 121 | fCore::toss( |
|---|
| | 122 | 'fProgrammerException', |
|---|
| | 123 | fGrammar::compose( |
|---|
| | 124 | 'The ciphertext provided does not appear to have been encrypted using %s', |
|---|
| | 125 | __CLASS__ . '::publicKeyEncrypt()' |
|---|
| | 126 | ) |
|---|
| | 127 | ); |
|---|
| | 128 | } |
|---|
| | 129 | |
|---|
| | 130 | $encrypted_key = base64_decode($elements[1]); |
|---|
| | 131 | $ciphertext = base64_decode($elements[2]); |
|---|
| | 132 | $provided_hmac = $elements[3]; |
|---|
| | 133 | |
|---|
| | 134 | $plaintext = ''; |
|---|
| | 135 | openssl_open($ciphertext, $plaintext, $encrypted_key, $private_key_resource); |
|---|
| | 136 | openssl_free_key($private_key_resource); |
|---|
| | 137 | |
|---|
| | 138 | $hmac = hash_hmac('sha1', $encrypted_key . $ciphertext, $plaintext); |
|---|
| | 139 | |
|---|
| | 140 | // By verifying the HMAC we ensure the integrity of the data |
|---|
| | 141 | if ($hmac != $provided_hmac) { |
|---|
| | 142 | fCore::toss( |
|---|
| | 143 | 'fValidationException', |
|---|
| | 144 | fGrammar::compose( |
|---|
| | 145 | 'The ciphertext provided appears to have been tampered with or corrupted' |
|---|
| | 146 | ) |
|---|
| | 147 | ); |
|---|
| | 148 | } |
|---|
| | 149 | |
|---|
| | 150 | return $plaintext; |
|---|
| | 151 | } |
|---|
| | 152 | |
|---|
| | 153 | |
|---|
| | 154 | /** |
|---|
| | 155 | * Encrypts the passed data using public key encryption via OpenSSL |
|---|
| | 156 | * |
|---|
| | 157 | * A public key (X.509 certificate) is required for encryption and a |
|---|
| | 158 | * private key (PEM) is required for decryption. |
|---|
| | 159 | * |
|---|
| | 160 | * @param string $plaintext The content to be encrypted |
|---|
| | 161 | * @param string $public_key_file The path to an X.509 public key certificate |
|---|
| | 162 | * @return string A base-64 encoded result containing a Flourish fingerprint and suitable for decryption using {@link publicKeyDecrypt()} |
|---|
| | 163 | */ |
|---|
| | 164 | static public function publicKeyEncrypt($plaintext, $public_key_file) |
|---|
| | 165 | { |
|---|
| | 166 | self::verifyPublicKeyEnvironment(); |
|---|
| | 167 | |
|---|
| | 168 | if (!file_exists($public_key_file)) { |
|---|
| | 169 | fCore::toss( |
|---|
| | 170 | 'fProgrammerException', |
|---|
| | 171 | fGrammar::compose( |
|---|
| | 172 | 'The path to the X.509 certificate specified, %s, is not valid', |
|---|
| | 173 | fCore::dump($public_key_file) |
|---|
| | 174 | ) |
|---|
| | 175 | ); |
|---|
| | 176 | } |
|---|
| | 177 | if (!is_readable($public_key_file)) { |
|---|
| | 178 | fCore::toss( |
|---|
| | 179 | 'fEnvironmentException', |
|---|
| | 180 | fGrammar::compose( |
|---|
| | 181 | 'The X.509 certificate specified, %s, can not be read', |
|---|
| | 182 | fCore::dump($public_key_file) |
|---|
| | 183 | ) |
|---|
| | 184 | ); |
|---|
| | 185 | } |
|---|
| | 186 | |
|---|
| | 187 | $public_key = file_get_contents($public_key_file); |
|---|
| | 188 | $public_key_resource = openssl_pkey_get_public($public_key); |
|---|
| | 189 | |
|---|
| | 190 | $ciphertext = ''; |
|---|
| | 191 | $encrypted_keys = array(); |
|---|
| | 192 | openssl_seal($plaintext, $ciphertext, $encrypted_keys, array($public_key_resource)); |
|---|
| | 193 | openssl_free_key($public_key_resource); |
|---|
| | 194 | |
|---|
| | 195 | $hmac = hash_hmac('sha1', $encrypted_keys[0] . $ciphertext, $plaintext); |
|---|
| | 196 | |
|---|
| | 197 | return 'fCryptography::public#' . base64_encode($encrypted_keys[0]) . '#' . base64_encode($ciphertext) . '#' . $hmac; |
|---|
| | 198 | } |
|---|
| | 199 | |
|---|
| | 200 | |
|---|
| | 201 | /** |
|---|
| | 202 | * Generates a random number using mt_rand() - make sure {@link seedRandom()} has been called |
|---|
| | 203 | * |
|---|
| | 204 | * @param integer $min The minimum number to return |
|---|
| | 205 | * @param integer $max The maximum number to return |
|---|
| | 206 | * @return integer The psuedo-random number |
|---|
| | 207 | */ |
|---|
| | 208 | static public function random($min=NULL, $max=NULL) |
|---|
| | 209 | { |
|---|
| | 210 | self::seedRandom(); |
|---|
| | 211 | if ($min !== NULL || $max !== NULL) { |
|---|
| | 212 | return mt_rand($min, $max); |
|---|
| | 213 | } |
|---|
| | 214 | return mt_rand(); |
|---|
| | 215 | } |
|---|
| | 216 | |
|---|
| | 217 | |
|---|
| | 218 | /** |
|---|
| 91 | | } |
|---|
| 92 | | |
|---|
| 93 | | |
|---|
| 94 | | /** |
|---|
| 95 | | * Hashes a password using a loop of sha1 hashes and a salt, making rainbow table attacks infeasible |
|---|
| 96 | | * |
|---|
| 97 | | * @param string $password The password to hash |
|---|
| 98 | | * @return string An 80 character string of the Flourish fingerprint, salt and hashed password |
|---|
| 99 | | */ |
|---|
| 100 | | static public function hashPassword($password) |
|---|
| 101 | | { |
|---|
| 102 | | $salt = self::generateRandomString(10); |
|---|
| 103 | | |
|---|
| 104 | | return self::hashWithSalt($password, $salt); |
|---|
| 105 | | } |
|---|
| 106 | | |
|---|
| 107 | | |
|---|
| 108 | | /** |
|---|
| 109 | | * Performs a large iteration of hashing a string with a salt |
|---|
| 110 | | * |
|---|
| 111 | | * @param string $source The string to hash |
|---|
| 112 | | * @param string $salt The salt for the hash |
|---|
| 113 | | * @return string An 80 character string of the Flourish fingerprint, salt and hashed password |
|---|
| 114 | | */ |
|---|
| 115 | | static private function hashWithSalt($source, $salt) |
|---|
| 116 | | { |
|---|
| 117 | | $sha1 = sha1($salt . $source); |
|---|
| 118 | | for ($i = 0; $i < 1000; $i++) { |
|---|
| 119 | | $sha1 = sha1($sha1 . (($i % 2 == 0) ? $source : $salt)); |
|---|
| 120 | | } |
|---|
| 121 | | |
|---|
| 122 | | return 'fCryptography::password_hash#' . $salt . '#' . $sha1; |
|---|
| 123 | | } |
|---|
| 124 | | |
|---|
| 125 | | |
|---|
| 126 | | /** |
|---|
| 127 | | * Decrypts ciphertext encrypted using public-key encryption via {@link fCryptography::publicKeyEncrypt()} |
|---|
| 128 | | * |
|---|
| 129 | | * A public key (X.509 certificate) is required for encryption and a |
|---|
| 130 | | * private key (PEM) is required for decryption. |
|---|
| 131 | | * |
|---|
| 132 | | * @throws fValidationException |
|---|
| 133 | | * |
|---|
| 134 | | * @param string $ciphertext The content to be decrypted |
|---|
| 135 | | * @param string $private_key_file The path to a PEM-encoded private key |
|---|
| 136 | | * @param string $password The password for the private key |
|---|
| 137 | | * @return string The decrypted plaintext |
|---|
| 138 | | */ |
|---|
| 139 | | static public function publicKeyDecrypt($ciphertext, $private_key_file, $password) |
|---|
| 140 | | { |
|---|
| 141 | | self::verifyPublicKeyEnvironment(); |
|---|
| 142 | | |
|---|
| 143 | | if (!file_exists($private_key_file)) { |
|---|
| 144 | | fCore::toss( |
|---|
| 145 | | 'fProgrammerException', |
|---|
| 146 | | fGrammar::compose( |
|---|
| 147 | | 'The path to the PEM-encoded private key specified, %s, is not valid', |
|---|
| 148 | | fCore::dump($private_key_file) |
|---|
| 149 | | ) |
|---|
| 150 | | ); |
|---|
| 151 | | } |
|---|
| 152 | | if (!is_readable($private_key_file)) { |
|---|
| 153 | | fCore::toss( |
|---|
| 154 | | 'fEnvironmentException', |
|---|
| 155 | | fGrammar::compose( |
|---|
| 156 | | 'The PEM-encoded private key specified, %s, is not readable', |
|---|
| 157 | | fCore::dump($private_key_file) |
|---|
| 158 | | ) |
|---|
| 159 | | ); |
|---|
| 160 | | } |
|---|
| 161 | | |
|---|
| 162 | | $private_key = file_get_contents($private_key_file); |
|---|
| 163 | | $private_key_resource = openssl_pkey_get_private($private_key, $password); |
|---|
| 164 | | |
|---|
| 165 | | if ($private_key_resource === FALSE) { |
|---|
| 166 | | fCore::toss( |
|---|
| 167 | | 'fValidationException', |
|---|
| 168 | | fGrammar::compose( |
|---|
| 169 | | 'The private key password specified does not appear to be valid for the private key', |
|---|
| 170 | | fCore::dump($primary_key_file) |
|---|
| 171 | | ) |
|---|
| 172 | | ); |
|---|
| 173 | | } |
|---|
| 174 | | |
|---|
| 175 | | $elements = explode('#', $ciphertext); |
|---|
| 176 | | |
|---|
| 177 | | // We need to make sure this ciphertext came from here, otherwise we are gonna have issues decrypting it |
|---|
| 178 | | if (sizeof($elements) != 4 || $elements[0] != 'fCryptography::public') { |
|---|
| 179 | | fCore::toss( |
|---|
| 180 | | 'fProgrammerException', |
|---|
| 181 | | fGrammar::compose( |
|---|
| 182 | | 'The ciphertext provided does not appear to have been encrypted using %s', |
|---|
| 183 | | __CLASS__ . '::publicKeyEncrypt()' |
|---|
| 184 | | ) |
|---|
| 185 | | ); |
|---|
| 186 | | } |
|---|
| 187 | | |
|---|
| 188 | | $encrypted_key = base64_decode($elements[1]); |
|---|
| 189 | | $ciphertext = base64_decode($elements[2]); |
|---|
| 190 | | $provided_hmac = $elements[3]; |
|---|
| 191 | | |
|---|
| 192 | | $plaintext = ''; |
|---|
| 193 | | openssl_open($ciphertext, $plaintext, $encrypted_key, $private_key_resource); |
|---|
| 194 | | openssl_free_key($private_key_resource); |
|---|
| 195 | | |
|---|
| 196 | | $hmac = hash_hmac('sha1', $encrypted_key . $ciphertext, $plaintext); |
|---|
| 197 | | |
|---|
| 198 | | // By verifying the HMAC we ensure the integrity of the data |
|---|
| 199 | | if ($hmac != $provided_hmac) { |
|---|
| 200 | | fCore::toss( |
|---|
| 201 | | 'fValidationException', |
|---|
| 202 | | fGrammar::compose( |
|---|
| 203 | | 'The ciphertext provided appears to have been tampered with or corrupted' |
|---|
| 204 | | ) |
|---|
| 205 | | ); |
|---|
| 206 | | } |
|---|
| 207 | | |
|---|
| 208 | | return $plaintext; |
|---|
| 209 | | } |
|---|
| 210 | | |
|---|
| 211 | | |
|---|
| 212 | | /** |
|---|
| 213 | | * Encrypts the passed data using public key encryption via OpenSSL |
|---|
| 214 | | * |
|---|
| 215 | | * A public key (X.509 certificate) is required for encryption and a |
|---|
| 216 | | * private key (PEM) is required for decryption. |
|---|
| 217 | | * |
|---|
| 218 | | * @param string $plaintext The content to be encrypted |
|---|
| 219 | | * @param string $public_key_file The path to an X.509 public key certificate |
|---|
| 220 | | * @return string A base-64 encoded result containing a Flourish fingerprint and suitable for decryption using {@link publicKeyDecrypt()} |
|---|
| 221 | | */ |
|---|
| 222 | | static public function publicKeyEncrypt($plaintext, $public_key_file) |
|---|
| 223 | | { |
|---|
| 224 | | self::verifyPublicKeyEnvironment(); |
|---|
| 225 | | |
|---|
| 226 | | if (!file_exists($public_key_file)) { |
|---|
| 227 | | fCore::toss( |
|---|
| 228 | | 'fProgrammerException', |
|---|
| 229 | | fGrammar::compose( |
|---|
| 230 | | 'The path to the X.509 certificate specified, %s, is not valid', |
|---|
| 231 | | fCore::dump($public_key_file) |
|---|
| 232 | | ) |
|---|
| 233 | | ); |
|---|
| 234 | | } |
|---|
| 235 | | if (!is_readable($public_key_file)) { |
|---|
| 236 | | fCore::toss( |
|---|
| 237 | | 'fEnvironmentException', |
|---|
| 238 | | fGrammar::compose( |
|---|
| 239 | | 'The X.509 certificate specified, %s, can not be read', |
|---|
| 240 | | fCore::dump($public_key_file) |
|---|
| 241 | | ) |
|---|
| 242 | | ); |
|---|
| 243 | | } |
|---|
| 244 | | |
|---|
| 245 | | $public_key = file_get_contents($public_key_file); |
|---|
| 246 | | $public_key_resource = openssl_pkey_get_public($public_key); |
|---|
| 247 | | |
|---|
| 248 | | $ciphertext = ''; |
|---|
| 249 | | $encrypted_keys = array(); |
|---|
| 250 | | openssl_seal($plaintext, $ciphertext, $encrypted_keys, array($public_key_resource)); |
|---|
| 251 | | openssl_free_key($public_key_resource); |
|---|
| 252 | | |
|---|
| 253 | | $hmac = hash_hmac('sha1', $encrypted_keys[0] . $ciphertext, $plaintext); |
|---|
| 254 | | |
|---|
| 255 | | return 'fCryptography::public#' . base64_encode($encrypted_keys[0]) . '#' . base64_encode($ciphertext) . '#' . $hmac; |
|---|
| 256 | | } |
|---|
| 257 | | |
|---|
| 258 | | |
|---|
| 259 | | /** |
|---|
| 260 | | * Generates a random number using mt_rand() - make sure {@link seedRandom()} has been called |
|---|
| 261 | | * |
|---|
| 262 | | * @param integer $min The minimum number to return |
|---|
| 263 | | * @param integer $max The maximum number to return |
|---|
| 264 | | * @return integer The psuedo-random number |
|---|
| 265 | | */ |
|---|
| 266 | | static public function random($min=NULL, $max=NULL) |
|---|
| 267 | | { |
|---|
| 268 | | self::seedRandom(); |
|---|
| 269 | | if ($min !== NULL || $max !== NULL) { |
|---|
| 270 | | return mt_rand($min, $max); |
|---|
| 271 | | } |
|---|
| 272 | | return mt_rand(); |
|---|