Using Python to authenticate against raw username, hash, salt in DB created by ASP.NET roles/membership - asp.net

We have a current application where user login credentials are stored in a SQL Server DB. These are, basically, stored as a plain text username, a password hash, and an associated salt for this hash.
These were all created by built in functions in ASP.NET's membership/role system. Here's a row for a user named 'joe' and a password of 'password':
joe,kDP0Py2QwEdJYtUX9cJABg==,OJF6H4KdxFLgLu+oTDNFodCEfMA=
I've dumped this stuff into a CSV file and I'm attempting to get it into a usable format for Django which stores its passwords in this format:
[algo]$[salt]$[hash]
Where the salt is a plain string and the hash is the hex digest of an SHA1 hash.
So far I've been able to ascertain that ASP is storing these hashes and salts in a base64 format. Those values above decode into binary strings.
We've used reflector to glean how ASP authenticates against these values:
internal string EncodePassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
byte[] inArray = null;
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
if (passwordFormat == 1)
{
HashAlgorithm algorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
if ((algorithm == null) && Membership.IsHashAlgorithmFromMembershipConfig)
{
RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
}
inArray = algorithm.ComputeHash(dst);
}
else
{
inArray = this.EncryptPassword(dst);
}
return Convert.ToBase64String(inArray);
}
Eseentially, pulls in the salt from the DB and b64 decodes it into a binary representation. It does a "GetBytes" on the raw password and then it concatinates them, salt first.
It then runs the SHA1 algorithm on this new string, base64 encodes it, and compares it against the value stored in the database.
I've attempted to write some code to try and reproduce these hashes in Python and I'm failing. I won't be able to use them in Django until I can figure out how this translates over. Here's how I'm testing:
import hashlib
from base64 import b64decode, b64encode
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt)
# Pass in password
m1.update(password_string)
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn't match"
print b64hash
print b64encode(m1.digest())
I'm wondering if anyone can see any flaws in my approach or can suggest an alternate method. Perhaps you can take the algorithms above and the known password and salt above and produce the hash on your system?

It appears python is inserting a byte order marker when you convert a UTF16 string to binary. The .NET byte array contains no BOM, so I did some ghetto python that turns the UTF16 into hex, removes the first 4 characters, then decodes it to binary.
There may be a better way to rip out the BOM, but this works for me!
Here's one that passes:
import hashlib
from base64 import b64decode, b64encode
def utf16tobin(s):
return s.encode('hex')[4:].decode('hex')
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = 'password'.encode("utf16")
password_string = utf16tobin(password_string)
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt + password_string)
# Pass in password
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn't match"
print b64hash
print b64encode(m1.digest())

Two thoughts as to what could be going wrong.
First the code from the reflection has three paths:
If passwordFormat is 0 it returns the password as is.
If passwordFormat is 1 it creates the hash as your python code does.
If passwordFormat is anything other than 0 or 1 it calls this.EncryptPassword()
How do you know you are hashing the password, and not encrypting the password with this.EncryptPassword()? You may need to reverse the EncryptPassword() member function and replicate that. That is unless you have some information which ensures that you are hashing the password and not encrypting it.
Second if it is indeed hashing the password you may want to see what the Encoding.Unicode.GetBytes() function returns for the string "password", as you may be getting something back like:
0x00 0x70 0x00 0x61 0x00 0x73 0x00 0x73 0x00 0x77 0x00 0x6F 0x00 0x72 0x00 0x64
instead of:
0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64
I hope this helps.

Related

What's the Zip Strong Encryption Specification/SecureZip key derivation function?

In the (PK)ZIP specification at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, specifically in the Strong Encryption Specification (SES) section, there is a line on deriving a key from a password:
MasterSessionKey = DeriveKey(SHA1(Password))
What's DeriveKey?
(In WinZip's AES documentation at https://www.winzip.com/en/support/aes-encryption/, they use PBKDF2 with 1000 iterations. I don't see any similar explanation in APPNOTE)
PKWARE implemented a strong encryption in version 5, but did not provide the algorithm of encoding/decoding (Method For Strongly Encrypted .ZIP Files - Patent US 2020/0250329 A1). In this algorithm AES encryption was implemented as part of it. You can define this by strong encryption (bit 6) = yes in General Purpose Flag.
After that WinZip could not use this algo, so it invented another one. You can define this by strong encryption (bit 6) = no in General Purpose Flag and AesExtraFieldRecord with signature 0x990.
As you can see there're two ways to encrypt a zip file. All open source software use the second one. The first one is available only by PKWARE SecureZIP
You can find example of this alogirthm in (7zip) Strong.cpp:35. In java it should look like this:
public static byte[] getMasterKey(String password) {
byte[] data = password.getBytes(StandardCharsets.UTF_8);
byte[] sha1 = DigestUtils.sha1(data);
return DeriveKey(sha1);
}
private static byte[] DeriveKey(byte[] digest) {
byte[] buf = new byte[kDigestSize * 2]; // kDigestSize = 20
DeriveKey2(digest, (byte)0x36, buf, 0);
DeriveKey2(digest, (byte)0x5C, buf, kDigestSize);
return Arrays.copyOfRange(buf, 0, 32);
}
private static void DeriveKey2(byte[] digest, byte c, byte[] dest, int offs) {
byte[] buf = new byte[64];
Arrays.fill(buf, c);
for (int i = 0; i < kDigestSize; i++)
buf[i] ^= digest[i];
byte[] sha1 = DigestUtils.sha1(buf);
System.arraycopy(sha1, 0, dest, offs, sha1.length);
}
Demo:
String password = "JohnDoe";
byte[] masterKey = getMasterKey(password);
The next paragraph 'defines' it
7.2.5.3 The function names and parameter requirements will depend on
the choice of the cryptographic toolkit selected. Almost any
toolkit supporting the reference implementations for each
algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft
CryptoAPI libraries are all known to work well.
I guess it's up to you to decide which of the encryption algorithms you want to use and go from there

HLS. Decrypt fmp4 segment (AES-128)

I want to decrypt fmp4 segment.
This segment was encrypt with HLS Apple Tools (https://developer.apple.com/documentation/http_live_streaming/about_apple_s_http_live_streaming_tools)
METHOD is AES-128
IV is 1d48fc5dee84b5a3e9a428f055e03c2e
I have a key and IV (you can got the key, and segment in google drive https://drive.google.com/drive/folders/1xF-C9EXFvT8qjI--sBB6QMPn8cNW7L-D?usp=sharing)
To decrypt I use Poco library.
This is my code:
Poco::Crypto::Cipher::ByteVec readKey(const std::string& uri) {
Poco::Crypto::Cipher::ByteVec key;
auto stream = Stream::makeStream(uri);
if (stream->open(uri, {})) {
key.resize(KEY_SIZE);
stream->read((char*)&key[0], KEY_SIZE);
}
return key;
}
std::vector<uint8_t> _key = readKey("./unit-tests/resources/cipher-stream/file.key");
std::string ivSrc = "1d48fc5dee84b5a3e9a428f055e03c2e";
Poco::Crypto::Cipher::ByteVec iv {ivSrc.begin(), ivSrc.end()};
Poco::Crypto::CipherKey key("aes-128-cbc", _key, iv);
Poco::Crypto::Cipher::Ptr cipher = Poco::Crypto::CipherFactory::defaultFactory().createCipher(key);
Poco::FileInputStream src("./unit-tests/resources/cipher-stream/fileSequence1.m4s");
Poco::FileOutputStream dst("./unit-tests/resources/cipher-stream/fileSequence1_dec.m4s");
Poco::Crypto::CryptoOutputStream decryptor(dst, cipher->createDecryptor());
Poco::StreamCopier::copyStream(src, decryptor);
// decryptor.close();
src.close();
dst.close();
Problem description:
After decryption I got distorted data. You can see this at the beginning of the file. Please see picture below. On the right side of the image file is distorted.
The correct data you can see on the left side.
You're using the wrong IV; that will lead to the first block (16 bytes) being corrupted. Your IV hex value is 1d48fc5dee84b5a3e9a428f055e03c2e, but you're interpreting that as ASCII. It's using the first 16 bytes of your string and ignoring the rest.
I haven't used Poco in a long time and don't remember if there's a hex parser handy, but that's what you need. Or write the IV directly in hex rather than as an ASCII string.

Parse EC Public key

I an working on ECIES and need to load peer public key.
Load EC Public key
I an using ECDH and need to load peer public key.
When I try to load public key from PEM file , seems no issue
Issue here:
EVP_PKEY * get_peer_key()
{
// base64 certificate data of alice_pub_key.pem
char *buffer= "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjWrT7F97QrSqGrlIgPK8dphNBicNO6gDLfOIMjhF2MiLuuzd7L7BP+bLCuNtKKe/2dOkgPqgXv4BFWqgp6PZXQ=="`
// calculate buffer length
int l = strlen(buffer)
//create bio from buffer
BIO *in = BIO_new_mem_buf(buffer,l)
//gnerate ec key
EC_KEY *eckey = PEM_read_bio_EC_PUBKEY(in,NULL,NULL,NULL)` // ==> FAIL
//need to convert to EVP format
EVP_PKEY *peerKey = EVP_PKEY_new()
//assign ec key evp
if(EVP_PKEY_assign_EC_KEY(peerKey,eckey) != 1 )
printf("\n error hapened");
return peerKey;
}
Works fine:
EVP_PKEY * get_peer_key()
{
//Load PEM format file
char * infile = "alice_pub_key.pem";
//create bio
BIO *in = BIO_new(BIO_s_file());
//read bio file
BIO_read_filename(in , infile);
//create eckey
EC_KEY *eckey = PEM_read_bio_EC_PUBKEY(in,NULL,NULL,NULL); // ==> success
// create peer key
EVP_PKEY *peerKey = EVP_PKEY_new();
//assign public key
if(EVP_PKEY_assign_EC_KEY(peerKey,eckey) != 1 )
printf("\n error hapened");
return peerKey;
}
Can some one suggest whats going wrong while reading base64 data of pem file
There are two ways of solving this:
Creating a PEM using a header and footer line and line breaks (at the 64th character;
Base 64 decoding the text and then handling it by parsing the resulting ASN.1 / DER binary;
I'd prefer the latter, as I abhor adding lines and such, it is error prone at best, and string manipulations should be avoided where possible.
Note that this assumes that the base 64 contains a SubjectPublicKeyInfo structure which I've shown you earlier. Otherwise you may have to find out how to parse a X9.62 structure or just a point.

Replicate CryptDeriveKey in Coldfusion

I'm working on a project to decrypt an AES-128 encrypted string in ColdFusion that is passed as a URL parameter.
The vendor takes a pass phrase and converts it to a valid AES-128 key "using an algorithm equivalent to Microsoft's CryptDeriveKey using the SHA-1 hash function." I need to replicate this generatedKey in ColdFusion so I can use the value in my decrypt() call.
When using CryptDeriveKey you pass the encryption type, the Hash type, the block length and a 0 iv array and it returns the Hash. Source: Generating a Key from a Password
// generate an RC2 key
byte[] iv = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
byte[] key = cdk.CryptDeriveKey(“RC2”, “SHA1”, 128, iv);
In the vendor's test tool, the pass phrase "test1234" results in a Hash of:
A6455C7A24BC5E869B0DDF647238F5DA
I found the genAESKeyFromPW() UDF, which seems to be the closest, but requires a salt which CryptDeriveKey does not use. I have also tried the code below. However, it is not working as the Hash() is not creating a valid AES-128 key:
<cfset generatedKey = Hash('test1234', 'SHA-1')>
<cfset decrypted=decrypt(encryptedString, generatedKey, 'AES/CBC/PKCS7Padding', 'Base64', '0')>
What steps do I need to replicate CryptDeriveKey function?
Update:
The vendor provided this C# example of the decryption:
public static byte[] AesDecryptBytes(byte[] cipherText, byte[] key)
{
byte[] IV = new byte[16];
AesManaged aes = new AesManaged();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = aes.CreateDecryptor(key, IV);
byte[] plainBytes;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(cipherText, 0, cipherText.Length);
}
plainBytes = memoryStream.ToArray();
}
return plainBytes;
}
From what I have read in this documentation, it sounds like the function essentially hashes the password binary then does an XOR with two arrays. I am not 100%, but I do not think the method they described is the same as PBKDF1 or PBKDF2.
Let n be the required derived key length, in bytes. The derived key is
the first n bytes of the hash value after the hash computation has
been completed by CryptDeriveKey. If the hash is not a member of the
SHA-2 family and the required key is for either 3DES or AES, the key
is derived as follows:
Form a 64-byte buffer by repeating the constant 0x36 64 times. Let k be the length of the hash value that is represented by the input
parameter hBaseData. Set the first k bytes of the buffer to the result
of an XOR operation of the first k bytes of the buffer with the hash
value that is represented by the input parameter hBaseData.
Form a 64-byte buffer by repeating the constant 0x5C 64 times. Set the first k bytes of the buffer to the result of an XOR operation of
the first k bytes of the buffer with the hash value that is
represented by the input parameter hBaseData.
Hash the result of step 1 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData
parameter.
Hash the result of step 2 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData
parameter.
Concatenate the result of step 3 with the result of step 4.
Use the first n bytes of the result of step 5 as the derived key.
CF Key Generation
Start by hashing the password and converting it into binary with binaryDecode:
hBaseData = binaryDecode(hash("test1234", "SHA1"), "hex");
Build and populate the two buffers with the specified constants:
// 0x36 (i.e. 54 decimal)
buff1 = listToArray(repeatString("54,", 64));
// 0x5C (i.e. 92 decimal)
buff2 = listToArray(repeatString("92,", 64));
Then do a bitwise XOR, storing the results in the buffer:
for (k = 1; k <= arrayLen(hBaseData); k++) {
buff1[k] = BitXOR( buff1[k], hBaseData[k]);
buff2[k] = BitXOR( buff2[k], hBaseData[k]);
}
Next hash() both buffers and concatenate the results:
hash1 = hash( javacast("byte[]", buff1), "SHA1");
hash2 = hash( javacast("byte[]", buff2), "SHA1");
combined = hash1 & hash2;
Finally, extract the first n bytes (16 == 128 bits / 8) as the new key. Since CF's hash() function returns hexadecimal (always two characters per byte) string functions can be used here.
keySize = 128 / 8;
newKey = left(combined, keySize *2);
Result: A6455C7A24BC5E869B0DDF647238F5DA
Decrypting in CF
Before you can decrypt, a few important notes:
CF's encrypt/decrypt functions expect keys to be encoded as base64. The generated key above is in hexadecimal format. So it must be converted first:
"PKCS7Padding" is not valid for CF/Java. Instead use PKCS5Padding.
"CBC" mode always requires an IV. The IV is a binary array whose length is the same as the algorithm's block size (AES block size = 16 bytes). It must be "...the same value [used to encrypt] to successfully decrypt the data." Based on the descriptions in your API, your IV should be all zeros. (This is not good in practice, but is okay for a test case).
For more details, see Strong encryption in ColdFusion
Example:
encrypted = "1lqcm0Jiy4Rs29tz2jpuoQ==";
newKeyHex = "A6455C7A24BC5E869B0DDF647238F5DA";
keyBase64 = binaryEncode(binaryDecode(newKeyHex, "hex"), "base64");
iv = javacast("byte[]", [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
decrypted = decrypt(encrypted, keyBase64, "AES/CBC/PKCS5Padding", "Base64", iv);
writeOutput("<br>"& decrypted);
Result: recordID=000001

obfuscate password in qt

I want to store a password (no hash) on my disk. it's nothing sensitive but i just don't want it in plaintext on my disk.
what i tried till now is:
converting the string in binary and XOR it with the binary of a key.
bool ok = true;
QByteArray qbaPW("mypass");
long long intPW = qbaPW.toHex().toLongLong( &ok, 16 );
QString binPW = QString::number( intPW, 2);
but the thing is, that it only works with short passwords. if they are too long intPW gets too big for longlong. any ideas how can avoid that thing?
cheers
A QByteArray is like a char array[len] in C. You can access individual members and do whatever you please with them. For example:
QByteArray const key("mykey");
QByteArray password("password");
for (int ik = 0, ip = 0;
ip < password.length();
++ ip, ik = (ik+1 < key.length() ? ik+1 : 0)) {
password[ip] = password[ip] ^ key[ik];
}
Since this just XORs with the key, you repeat this procedure to decrypt the password. A good key will be generated randomly and will be longer than the longest password you envisage (say 64 characters).
Do note that this method is only reasonably safe if the users are explicitly informed not to reuse any other password in your application - otherwise you're essentially leaking passwords that are supposed to be secure.

Resources