What is this password format?
From my research it looks like a 64 length 150000 itteration pbkdf2 sha256 encrypted key
If i needed to write something in PHP to create users how would I go about doing so?
pbkdf2:sha256:150000$5gmU8sPF$fd67586ce17773c31d8b68711e11fdc2c1c9b7e183e702b039f52498742b665e
Many Thanks
Henry
Instead of 150K of iteration, that could be reduced to 1-10K iteration.
/* generate 44 bytes password hash
using hash key + multiple salts
*/
function hash_password($password, $key, $salts, $iteration = 1000) {
$hash = $password;
if (is_string($salts)) {
$salts = (Object)[$key => $salts];
}
foreach ($salts as $k => $salt) {
$hash = hash_pbkdf2('sha256', $hash, $k.$salt, 1000, 0, true));
}
return base64_encode(hash_pbkdf2('sha256', $hash, $key, $iteration, 0, true));
}
/* usage:
$hash = hash_password('passw#rd', 'hashkey', (Object)[ 'gen' => 'OO7', ...]);
*/
Related
I am trying to have AES encryption on the server side, and decryption on the client side. I have followed an example where CryptoJS is used on the client side for encryption and SubtleCrypto on the client side as well for decryption, but in my case I have the encryption and decryption separated.
Suppose I have the following encryption function within React Native:
const encrypt = (str: string) => {
const iv = crypto.randomBytes(12);
const myHexToken = "0x...."
const cipher = crypto.createCipheriv('aes-256-gcm', myHexToken.slice(0,32), iv)
let encrypted = cipher.update(str, 'utf8', 'hex')
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag();
return {
message: encrypted,
tag: tag.toString('hex'),
iv: iv.toString('hex'),
};
};
This json is then posted to the client through a webview postMessage.
The client side has the following javascript injected:
var myHexToken = "0x....";
window.addEventListener("message", async function (event) {
var responseData = JSON.parse(event.data);
try {
var decryptedData = await decrypt(responseData.iv, responseData.message, responseData.tag);
} catch (e) {
alert(e);
}
// ...
How can I decrypt responseData.message within the WebView through SubtleCrypto of the Web Crypto API?
I have tried various things with the following methods, but I keep getting "OperationalError":
function fromHex(hexString) {
return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function fromBase64(base64String) {
return Uint8Array.from(window.atob(base64String), c => c.charCodeAt(0));
}
async function importKey(rawKey) {
var key = await crypto.subtle.importKey(
"raw",
rawKey,
"AES-GCM",
true,
["encrypt", "decrypt"]
);
return key;
}
async function decrypt(iv, data, tag) {
var rawKey = fromHex(myHexToken.slice(0,32));
var iv = fromHex(iv);
var ciphertext = str2ab(data + tag);
var cryptoKey = await importKey(rawKey)
var decryptedData = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
cryptoKey,
ciphertext
)
var decoder = new TextDecoder();
var plaintext = decoder.decode(decryptedData);
return plaintext;
}
UPDATE 1: Added the getAuthTag implementation server side. Changed IV to have length of 12 bytes. Attempt to concatenate ciphertext and tag client side.
I have verified that "myHexToken" is the same both client and server side. Also, the return values of the server side "encrypt()" method are correctly sent to the client.
In the WebCrypto code the key must not be hex decoded with fromHex(), but must be converted to an ArrayBuffer with str2ab().
Also, the concatenation of ciphertext and tag must not be converted to an ArrayBuffer with str2ab(), but must be hex decoded with fromHex().
With these fixes decryption works:
Test:
For the test, the following hex encoded key and plaintext are used on the NodeJS side:
const myHexToken = '000102030405060708090a0b0c0d0e0ff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff';
const plaintext = "The quick brown fox jumps over the lazy dog";
const encryptedData = encrypt(plaintext);
console.log(encryptedData);
This results e.g. in the following output:
{
message: 'cc4beae785cda5c9413f49cf9449a6ae17fdc0f7435b9a8fd954602bdb4f4b825793f6b561c0d9a709007c',
tag: '046c8e56bbd13db2faed82d1b19c665e',
iv: '11f87b0eaf006373ae8bc94d'
}
The ciphertext created this way can be successfully decrypted with the fixed JavaScript code:
(async () => {
function fromHex(hexString) {
return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
async function importKey(rawKey) {
var key = await crypto.subtle.importKey(
"raw",
rawKey,
"AES-GCM",
true,
["encrypt", "decrypt"]
);
return key;
}
async function decrypt(iv, data, tag) {
//var rawKey = fromHex(myHexToken.slice(0,32)); // Fix 1
var rawKey = str2ab(myHexToken.slice(0,32));
var iv = fromHex(iv);
//var ciphertext = str2ab(data + tag); // Fix 2
var ciphertext = fromHex(data + tag);
var cryptoKey = await importKey(rawKey)
var decryptedData = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
cryptoKey,
ciphertext
);
var decoder = new TextDecoder();
var plaintext = decoder.decode(decryptedData);
return plaintext;
}
var myHexToken = '000102030405060708090a0b0c0d0e0ff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'
var data = {
message: 'cc4beae785cda5c9413f49cf9449a6ae17fdc0f7435b9a8fd954602bdb4f4b825793f6b561c0d9a709007c',
tag: '046c8e56bbd13db2faed82d1b19c665e',
iv: '11f87b0eaf006373ae8bc94d'
}
var plaintext = await decrypt(data.iv, data.message, data.tag);
console.log(plaintext);
})();
A remark about the key: In the posted NodeJS code, const myHexToken = "0x...." is set. It's not clear to me if the 0x prefix is just supposed to symbolize a hex encoded string, or is really contained in the string. If the latter, it should actually be removed before the implicit UTF-8 encoding (by createCiperiv()). In case of a hex decoding it must be removed anyway.
In the posted example a valid hex encoded 32 bytes key is used (i.e. without 0x prefix).
With regard to the key encoding, also note the following:
The conversion of the key from a hex encoded string by a UTF-8 (or ASCII) encoding results in only half of the key being considered, in the example: 000102030405060708090a0b0c0d0e0f. This reduces security, because the value range per byte is reduced from 256 to 16 values.
In order for the entire key to be considered, the correct conversion on the NodeJS side would be: Buffer.from(myHexToken, 'hex') and on the WebCrypto side: var rawKey = fromHex(myHexToken).
Because of its implicit UTF8 encoding crypto.createCipheriv(..., myHexToken.slice(0,32), ...) creates a 32 bytes key and is functionally identical to str2ab(myHexToken.slice(0,32)) only as long as the characters in the substring myHexToken.slice(0,32) correspond to ASCII characters (which is true for a hex encoded string).
for the GDPR I'm trying to encrypt and decrypt my user's personal data from my Sequelize Users model. My username and email fields must be unique.
I tried to use the afterValidate and the beforeValidate hooks, but on validation, Sequelize can't recognize if the fields are unique or not because they already are encrypted, and a crypt is always different.
How do you handle this?
I think you are using random initVector and Securitykey,which will be different in different times. So you have to use a const value which can be saved in a config file.
Ex :
initVectorString: 'xx567876567898765',
securitykeyString:
'Zzzz345678909876545678984567890',
After saving in the config file, you can have a encrypt and decrypt function as follows
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const initVector = Buffer.from(initVectorString, 'hex');
const Securitykey = Buffer.from(securitykeyString, 'hex');
function encryptData(data) {
try {
const cipher = crypto.createCipheriv(algorithm, Securitykey, initVector);
let encryptedData = cipher.update(data, 'utf-8', 'hex');
encryptedData += cipher.final('hex');
return encryptedData;
} catch (error) {}
}
function decryptData(data) {
try {
const decipher = crypto.createDecipheriv(
algorithm,
Securitykey,
initVector
);
let decryptedData = decipher.update(data, 'hex', 'utf-8');
decryptedData += decipher.final('utf8');
return decryptedData;
} catch (error) {}
}
I should like to insert the return token from api into the .env in when after i want pass it header in
<!-- language: php -->
class GuzzleController extends Controller
{
public function getToken()
{
$client = new Client();
$request = $client->request('POST', 'http://192.168.53.27:1996/api/login/',
[
'form_params' => [
'user_name' => 'userName',
'password' => 'Passs',
]
]);
return json_decode((string)$request->getBody(), true);
}
}
As same question has been answere here;
This method should save new value to your .env file
private function setEnvironmentValue($envKey, $envValue)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, PHP_EOL, $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
$str = substr($str, 0, -1);
$fp = fopen($envFile, 'w');
fwrite($fp, $str);
fclose($fp);
}
usage
$this->setEnvironmentValue('DEPLOY_SERVER', 'forge#122.11.244.10');
My finder from Auth has conditions that I need to access $this->request but I don't have access for that on UsersTable.
AppController::initialize
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth',
]
]
]);
UsersTable
public function findAuth(Query $query, array $options)
{
$query
->select([
'Users.id',
'Users.name',
'Users.username',
'Users.password',
])
->where(['Users.is_active' => true]); // If I had access to extra data passed I would use here.
return $query;
}
I need pass extra data from AppController to finder auth since I don't have access to $this->request->data on UsersTable.
Update
People are saying on comments that is a bad design so I will explain exactly what I need.
I have a table users but each user belongs to a gym.
The username(email) is unique only to a particular gym so I can have a example#domain.comfrom gym_id 1 and another example#domain.com from gym_id 2.
On login page I have the gym_slug to tell to auth finder which gym the user username that I provided belongs.
To my knowledge, there is no way to do this by passing it into the configuration in 3.1. This might be a good idea submit on the cakephp git hub as a feature request.
There are ways to do it by creating a new authentication object that extends base authenticate and then override _findUser and _query. Something like this:
class GymFormAuthenticate extends BaseAuthenticate
{
/**
* Checks the fields to ensure they are supplied.
*
* #param \Cake\Network\Request $request The request that contains login information.
* #param array $fields The fields to be checked.
* #return bool False if the fields have not been supplied. True if they exist.
*/
protected function _checkFields(Request $request, array $fields)
{
foreach ([$fields['username'], $fields['password'], $fields['gym']] as $field) {
$value = $request->data($field);
if (empty($value) || !is_string($value)) {
return false;
}
}
return true;
}
/**
* Authenticates the identity contained in a request. Will use the `config.userModel`, and `config.fields`
* to find POST data that is used to find a matching record in the `config.userModel`. Will return false if
* there is no post data, either username or password is missing, or if the scope conditions have not been met.
*
* #param \Cake\Network\Request $request The request that contains login information.
* #param \Cake\Network\Response $response Unused response object.
* #return mixed False on login failure. An array of User data on success.
*/
public function authenticate(Request $request, Response $response)
{
$fields = $this->_config['fields'];
if (!$this->_checkFields($request, $fields)) {
return false;
}
return $this->_findUser(
$request->data[$fields['username']],
$request->data[$fields['password']],
$request->data[$fields['gym']],
);
}
/**
* Find a user record using the username,password,gym provided.
*
* Input passwords will be hashed even when a user doesn't exist. This
* helps mitigate timing attacks that are attempting to find valid usernames.
*
* #param string $username The username/identifier.
* #param string|null $password The password, if not provided password checking is skipped
* and result of find is returned.
* #return bool|array Either false on failure, or an array of user data.
*/
protected function _findUser($username, $password = null, $gym = null)
{
$result = $this->_query($username, $gym)->first();
if (empty($result)) {
return false;
}
if ($password !== null) {
$hasher = $this->passwordHasher();
$hashedPassword = $result->get($this->_config['fields']['password']);
if (!$hasher->check($password, $hashedPassword)) {
return false;
}
$this->_needsPasswordRehash = $hasher->needsRehash($hashedPassword);
$result->unsetProperty($this->_config['fields']['password']);
}
return $result->toArray();
}
/**
* Get query object for fetching user from database.
*
* #param string $username The username/identifier.
* #return \Cake\ORM\Query
*/
protected function _query($username, $gym)
{
$config = $this->_config;
$table = TableRegistryget($config['userModel']);
$options = [
'conditions' => [$table->aliasField($config['fields']['username']) => $username, 'gym' => $gym]
];
if (!empty($config['scope'])) {
$options['conditions'] = array_merge($options['conditions'], $config['scope']);
}
if (!empty($config['contain'])) {
$options['contain'] = $config['contain'];
}
$query = $table->find($config['finder'], $options);
return $query;
}
}
For more information see this: Creating Custom Authentication Objects
I know this is an old question but I thought I would post the finder I am using in one of our SaaS apps built on Cakephp 3. Does it follow DRY etc probably not. To say everything can be done X or Y way ..... you always have to bend the rules. In this case depending on the URL (xdomain.com or ydomain.com) our app figures out who the customer is and changes layouts etc. Also the user based is tied to Email & site_id much like yours
public function findAuth(\Cake\ORM\Query $query, array $options) {
$query
->select([
'Users.id',
'Users.email',
'Users.password',
'Users.site_id',
'Users.firstname',
'Users.lastname'])
->where([
'Users.active' => 1,
'Users.site_id'=> \Cake\Core\Configure::read('site_id')
]);
return $query;
}
Anyway hope it helps someone
In this tutorial , where are the following values coming from?
password (OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4)
keyPassword (OBF:1u2u1wml1z7s1z7a1wnl1u2g)
trustPassword (OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4)
Someone (ack_ of the Norn Iron Hacker Scene) made a Python script to reverse the Jetty password obfuscation. Useful when you need to export the keystore to other programs.
# Jetty Deobfuscation Tool
from __future__ import print_function
import sys
def deobfuscate_jetty(ciphertext):
plaintext = ""
for i in range(0, len(ciphertext), 4):
t = ciphertext[i:i + 4]
i0 = int(t, 36)
i1, i2 = divmod(i0, 256)
x = (i1 + i2 - 254) >> 1
plaintext += chr(x)
return plaintext
if __name__ == '__main__':
if len(sys.argv) == 2:
print(deobfuscate_jetty(sys.argv[1]))
else:
print("Jetty Deobfuscation Tool v1.0")
print("%s <string>" % sys.argv[0])
exit(1)
The passwords prefixed with OBF: come from Jetty's own system for obfuscating passwords. There is more documentation here: http://wiki.eclipse.org/Jetty/Howto/Secure_Passwords
Note that this is obfuscated and not encrypted. It just prevents a human from reading it quickly:
In some cases such as keystore passwords and digest authentication,
the system must retrieve the original password, which requires the
obfuscation method. The drawback of the obfuscation algorithm is that
it protects passwords from casual viewing only.
You could put them in clear too, it wouldn't change much.
In this case, the password, keyPassword and trustPassword are respectively the passwords for the key store, the key password (that should be optional if it's the same as the key store password) and the trust store password. These are the ones you set when you create these keystores.
This was driving me kind of crazy too. Here's a script that you can use to generate the various passwords. The script works with this particular version of jetty: jetty-hightide-8.1.10.v20130312, but can be modified through the JETTY_VER variable.
jetty-passwd.sh
#!/bin/bash
# url: http://wiki.eclipse.org/Jetty/Howto/Secure_Passwords
# set -x
if [ $# -ne 2 ]; then
echo -e "\nUSAGE: `basename $0`: <user> <password>\n";
exit 0;
fi
JETTY_VER=8.1.10.v20130312
JETTY_HOME=/opt/jetty-hightide-$JETTY_VER
java -cp $JETTY_HOME/lib/jetty-util-${JETTY_VER}.jar org.eclipse.jetty.util.security.Password $1 $2
example run
% ./jetty-passwd.sh me blah
blah
OBF:1t2x1toq1to41t39
MD5:6f1ed002ab5595859014ebf0951522d9
CRYPT:me/DjMjPzbKG.
The following function is an ES6 port of the Python function by Thilo. It could be used to deobfuscate a password on a Node server.
I also added an obfuscation method that I adapted from: arthepsy/deobf/jetty.obf.py
In addition, I added some mocha/chai tests to run through random passwords verify that the obfuscate/deobfuscate methods are symetric.
const
clipText = (str, length) => `${str.slice(0, length)}…`,
fill = (size, fn) => new Array(size).fill(0).map((_, i) => fn ? fn(i) : i);
/** test.js */
const main = () => {
const
generator = new PasswordGenerator({ symbols: true, length: 16 }),
passwords = fill(100, () => generator.next());
mocha.setup('bdd');
chai.should();
describe('Test JettyUtil', () =>
passwords.forEach(pw => {
const
ciphertext = JettyUtil.obfuscate(pw),
plaintext = JettyUtil.deobfuscate(ciphertext);
it(clipText(`${pw} → ${ciphertext}`, 64), () =>
pw.should.equal(plaintext))
}));
mocha.run();
};
/** jetty-util.js */
const
OBF_PREFIX = 'OBF:',
divmod = (m, n) => [ Math.trunc(m / n), m % n ],
unpack = (str) => str.split('').map(c => c.charCodeAt(0) & 0xFF),
chunk = (str, size) => str.match(new RegExp(`.{1,${size}}`, 'g'));
class JettyUtil {
static deobfuscate(ciphertext) {
return chunk(ciphertext.slice(OBF_PREFIX.length), 4)
.reduce((plaintext, i0) => {
const [ i1, i2 ] = divmod(parseInt(i0, 36), 256);
return plaintext + String.fromCharCode((i1 + i2 - 254) >> 1);
}, '');
}
static obfuscate(plaintext) {
return unpack(plaintext).reduce((ciphertext, b1, index, bytes) => {
const b2 = bytes[bytes.length - (index + 1)],
[ i1, i2 ] = [ 127 + b1 + b2, 127 + b1 - b2 ];
return ciphertext + (i1 * 256 + i2).toString(36).padStart(4, '0');
}, OBF_PREFIX);
}
}
// export default JettyUtil;
/** password-generator.js */
const Alphabet = {
UPPERCASE : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
LOWERCASE : 'abcdefghijklmnopqrstuvwxyz',
NUMBERS : '0123456789',
SYMBOLS : ' !"#$%&\'()*+,-./:;<=>?#[\]^_`{|}~'
};
class PasswordGenerator {
constructor(config) {
this.opts = { ...PasswordGenerator.defaultOptions, ...config };
this.alphabet = Object.entries(this.opts)
.map(([k, v]) => v === true ? Alphabet[k.toUpperCase()] : null)
.filter(v => v != null)
.join('');
}
next() {
return fill(this.opts.length, () => rando(this.alphabet)).join('');
}
}
PasswordGenerator.defaultOptions = {
uppercase : true,
lowercase : true,
numbers : true,
symbols : false,
length : 12
};
// export default PasswordGenerator;
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://randojs.com/2.0.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/7.2.0/mocha.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/7.2.0/mocha.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.2.0/chai.min.js"></script>
<div id="mocha"></div>
Here is my original response:
const divmod = (m, n) => [ ~~(m / n), m % n ];
const deobfuscate = (ciphertext) => {
if (!ciphertext.startsWith('OBF:')) return null;
let plaintext = '';
for (let offset = 4; offset < ciphertext.length; offset += 4) {
const i0 = parseInt(ciphertext.slice(offset, offset + 4), 36);
const [ i1, i2 ] = divmod(i0, 256);
plaintext += String.fromCharCode((i1 + i2 - 254) >> 1);
}
return plaintext;
};
const pwList = [
'OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4', // storepwd
'OBF:1u2u1wml1z7s1z7a1wnl1u2g', // keypwd
];
pwList.forEach(pw => console.log(deobfuscate(pw)));