I'm using Crypto++ to generate a MD4-Hash from a given password. But the generated hash doesn't seem to be correct. I think I'm misusing the CryptoPP functions somewhere.
CryptoPP::Weak1::MD4 hash2;
byte digest2[CryptoPP::Weak1::MD4::DIGESTSIZE];
hash.CalculateDigest(digest2, (byte*)password, strlen(password));
CryptoPP::HexEncoder encoder2;
std::string output2;
encoder2.Attach(new CryptoPP::StringSink(output2));
encoder2.Put(digest,sizeof(digest2));
encoder2.MessageEnd();
printf("END %s \n", output2.c_str());
My variable password contains the value "test". The printed output is:
END 3CC942AE509EC070B2548515E00F8CE8
The expected value, tested by some MD4 Hash Generators, is:
db346d691d7acc4dc2625db19f9e3f52
Any ideas?
Okay, I found the solution by myself. It doesn't work the way I posted above.
Here the correct code, it may be useful for someone else:
std::string value;
CryptoPP::Weak1::MD4 hashmd4;
CryptoPP::StringSource (password, true,
new CryptoPP::HashFilter( hashmd4,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(value)
)
)
);
hash.CalculateDigest(digest2, (byte*)password, strlen(password));
This should be:
hash2.CalculateDigest(digest2, (byte*)password, strlen(password));
That is, hash2, not hash.
encoder2.Put(digest,sizeof(digest2));
This should be:
encoder2.Put(digest2,sizeof(digest2));
That is, digest2, not digest.
The expected value, tested by some MD4 Hash Generators, is:
db346d691d7acc4dc2625db19f9e3f52
Yep, that's what I get using the code you posted after the typos were fixed.
Related
Trying to create user using from golang using library "google.golang.org/api/admin/directory/v1"
As docs sad here https://developers.google.com/admin-sdk/directory/reference/rest/v1/users#User hashFunction could be MD5, DES, SHA2-256, SHA2-512, so i'm writing code:
hash := sha512.Sum512([]byte("random password value))
encoded := hex.EncodeToString(hash[:])
payload := &admin.User{
Password: "$6$" + encoded,
HashFunction: "SHA2-512"
}
This results: googleapi: Error 400: Invalid Hash Function Name, invalid
So how to understand, whats wrong with hash function name ?
So, i found here https://developers.google.com/resources/api-libraries/documentation/admin/directory_v1/python/latest/admin_directory_v1.users.html#insert that right now only supported SHA-1, crypt and MD5, so documentation is wrong, and for SHA2 hashes use string "crypt" as hashFunction value and add to password corresponding crypt prefix ( for example for SHA2-512 add $6$ to hex encoded value )
I made a test JWT using something like the following code
String jwt = Jwts.builder()
.setHeaderParam("typ", "jwt")
.setId("myid")
.setIssuer("ExampleIssuer")
.setSubject("JohnDoe")
.setIssuedAt(Date.from(LocalDateTime.now().toInstant(ZoneOffset.ofHours(-4))))
.setExpiration(Date.from(LocalDateTime.now().toInstant(ZoneOffset.ofHours(-4)).plusSeconds(600)))
.claim("perms",perms)
.signWith(SignatureAlgorithm.HS512, "SECRET")
.compact();
"perms" is a custom claim, which contains an ArrayList of Strings (permissions).
So when I receive the JWT back, I use the following code
try{
Jwt<?, ?> claims = Jwts.parser().setSigningKey("SECRET").parse(jwt);
System.out.println(claims.getBody().toString());
} catch (SignatureException e){
//Error
}
And I get something like
{jti=myid, iss=ExampleIssuer, sub=JohnDoe, iat=1495678299, exp=1495678899, perms=[CREATE, VIEW]}
My question is: is this the correct (intended) way to get the claims back? It seems from now I will need to parse the result with a custom method, but I think somehow that is not the intended way.
Thank you.`
I found a solution, not sure if the intended one, but it works. I need to use
Claims claims = new DefaultClaims();
try{
claims = Jwts.parser().setSigningKey("SECRET").parseClaimsJws(jwt).getBody();
} catch (SignatureException e){
//Signature error
}
I can use Map methods on claims, but also the built-in methods to recover the individual claims:
String jti = claims.getId();
String iss = claims.getIssuer();
String sub = claims.getSubject();
String iat = claims.getIssuedAt().toString();
String exp = claims.getExpiration().toString();
#SuppressWarnings("unchecked")
ArrayList<String> perms = (ArrayList<String>) claims.get("perms");
I think I can suppress the warning on the unchecked casting because since I created the custom claim with the same value class, I know what to expect on it. Now the claims in the token are parsed correctly into variables I can work with.
I am new to Python and am trying to get my ahead around parsing SSE client code. I am using the SSE Client library. My code is very basic and follows the sample exactly. Here it is:
from sseclient import SSEClient
devID = "xxx"
AToken = "xxx"
sparkURL = 'https://api.spark.io/v1/devices/' + devID + '/events/?access_token=' + AToken
messages = SSEClient(sparkURL)
for msg in messages:
print(msg)
print(type(msg))
The code runs without a problem and I see some blank lines and SSE data coming through. Here is the sample output:
<class 'sseclient.Event'>
{"data":"0 days, 0:54:43","ttl":"60","published_at":"2015-04-09T22:43:52.084Z","coreid":"xxxx"}
<class 'sseclient.Event'>
<class 'sseclient.Event'>
{"data":"0 days, 0:55:3","ttl":"60","published_at":"2015-04-09T22:44:12.092Z","coreid":"xxx"}
<class 'sseclient.Event'>
The actual output above looks like a dictionary, but its type is "sseclient.Event". I am trying to figure out how to parse the output so I can pull out one of the fields and nothing I have tried has worked.
Sorry if this is basic questions, but can someone provide some simple guidance on how I would either convert the entire output to a dictionary or perhaps just pull out one of the fields?
Thank you in advance!
I figured this out. In case anyone else experiences the same problem, here is how I got it to work. The key was using msg.data and not just msg. I then converted the out using the JSON library and am good to go.
messages = SSEClient(sparkURL)
for msg in messages:
outputMsg = msg.data
if type(outputMsg) is not str:
outputJS = json.loads(outputMsg)
FilterName = "data"
#print( FilterName, outputJS[FilterName] )
print(outputJS[FilterName])
I am currently rewriting some custom perl-code used in the OTRS-ticketing-system, which is used to create SQL-like queries. Yes, there are probably better ways of escaping input, but let's not go into this...
$Param{PostMasterSearch} contains an email-adress like test'test#domain.tld (Note the ').
my $PostMasterSearch = $Param{PostMasterSearch};
$PostMasterSearch =~ s/'//gms;
$Self->{LogObject}->Log(
Priority => 'error',
Message => "XXXXX: $PostMasterSearch",
);
$SQLExt .= " $Field LIKE '$PostMasterSearch'";
So my expectation would be, that I'll find a log-message saying XXXXX: testtest#domain.tld and part of a SQL-query that goes like Email LIKE 'testtest#domain.tld'.
But in reality, I only get the log-message - the SQL-query-string is for whatever reason Email LIKE 'test'test#domain.tld'.
Screwing with the last line of the code to be like
$SQLExt .= " $Field LIKE '$PostMasterSearch' X";
doesn't make any sense - but returns the string Email LIKE 'testtest#domain.tld' X.
Any hints on why $PostMasterSearch is still containing that ' that should have been long gone? Or a hint on how to concatenate the $SQLExt with the '-less version of $PostMasterSearch?
OK, now this is the part where it gets embarrassing...
Turns out, that this script contains the very same code multiple times and executes it multiple times...
So the code posted above actually works and doesn't return any error (like it should). The error-message is caused by the next occurence of the code, that hasn't been patched yet.
tl;dr: I probably should trash that script and rewrite it from scratch.
Is there any way to authenticate users from other web apps using the DNN logins?
We have a main site that is using DNN and user logins are stored in the asp net membership table. From what I have been reading, the passwords are encrypted using the machine key and then salted. I see where this info is, but can't seem to encrypt passwords correctly using this method.
I'm trying with a Coldfusion web application on the same server where our DNN site is, but it doesn't want to work. You'd think it would be strait forward with the ColdFusion encryption function:
Encrypt(passwordstring, key [, algorithm, encoding, IVorSalt, iterations])
No matter what I try, I never get a matching value.
Any help, insight or pointing me in the right direction would be greatly appreciated!
(Edit: Original answer did not work in all cases. Substantially revised ...)
From what I have read, DNN uses an "SHA1" hash by default. The thread #barnyr posted shows it simply hashes the concatenated salt and password, but with a few twists.
DNN uses UTF-16LE to extract the password bytes, rather than CF's typical UTF-8.
It also extracts the salt and password bytes separately, which may produce different results than just decoding everything as a single string, which is what hash() does. (See demo below)
Given that CF9's Hash function does not accept binary (supported in CF11), I do not think it is possible to duplicate the results with native CF functions alone. Instead I would suggest decoding the strings into binary, then using java directly:
Code:
<cfscript>
thePassword = "DT!#12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
// hash binary using java
MessageDigest = createObject("java", "java.security.MessageDigest").getInstance("SHA-1");
MessageDigest.update(dataBytes);
theBase64Hash = binaryEncode(MessageDigest.digest(), "base64");
WriteOutput("theBase64Hash= "& theBase64Hash &"<br/>");
</cfscript>
Demo of Differences:
<cfscript>
theEncoding = "UTF-16LE";
thePassword = "DT!#12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract the bytes SEPARATELY
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, theEncoding );
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
separateBytes = ArrayUtils.addAll( saltBytes, passBytes );
// concatenate first, THEN extract the bytes
theSalt = charsetEncode( binaryDecode(base64Salt, "base64"), theEncoding );
concatenatedBytes = charsetDecode( theSalt & thePassword, theEncoding );
// these are the raw bytes BEFORE hashing
WriteOutput("separateBytes= "& arrayToList(separateBytes, "|") &"<br>");
WriteOutput("concatenatedBytes"& arrayToList(concatenatedBytes, "|") );
</cfscript>
Results:
separateBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|77|-40|-14|104|68|0|84|0|33|0|64|0|49|0|50|0
concatenatedBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|-3|-1|68|0|84|0|33|0|64|0|49|0|50|0
Most likely the password is not encrypted, it is hashed. Hashing is different from encrypting, because it is not reversible.
You would not use ColdFusion's encrypt() function for this, you would use its hash() function.
So the questions you'll need to answer to figure out how to hash the passwords in CF to be able to auth against the DNN users are:
What algorithm is DNN using to hash the passwords?
How is the salt being used with the password prior to hashing?
Is DNN iterating over the hash X number of times to improve security?
All of those questions must be answered to determine how CF must use the hash() function in combination with the salt and user-submitted passwords.
I'll make some assumptions to provide an answer.
If we assume that noiteration is being done and that the salt is simply being appended to the password prior to using SHA1 to hash the password, then you'd be able to reproduce the hash digest like this:
<cfset hashDigest = hash(FORM.usersubmittedPassword & saltFromDB, "SHA1") />
(Posting a new response to keep the "encrypted" process separate from "hashing")
For "encrypted" keys, the DNN side uses the standard algorithms ie DES, 3DES or AES - depending on your machineKey settings. But with a few differences you need to match in your CF code. Without knowing your actual settings, I will assume you are using the default 3DES for now.
Data To Encrypt
The encrypted value is a combination of the salt and password. But as with hashing, DNN uses UTF-16LE. Unfortunately, ColdFusion's Encrypt() function always assumes UTF-8, which will produce a very different result. So you need to use the EncryptBinary function instead.
// sample valus
plainPassword = "password12345";
base64Salt = "x7le6CBSEvsFeqklvLbMUw==";
hexDecryptKey = "303132333435363738393031323334353637383930313233";
// first extract the bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(plainPassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
Encryption Algorithm
With block ciphers, ColdFusion defaults to ECB mode. (See Strong Encryption in ColdFusion) Whereas .NET defaults to CBC mode, which requires an additional IV value. So you must adjust your CF code to match.
// convert DNN hex key to base64 for ColdFusion
base64Key = binaryEncode(binaryDecode( hexDecryptKey, "hex"), "base64");
// create an IV and intialize it with all zeroes
// block size: 16 => AES, 8=> DES or TripleDES
blockSize = 8;
iv = javacast("byte[]", listToArray(repeatString("0,", blocksize)));
// encrypt using CBC mode
bytes = encryptBinary(dataBytes, base64Key, "DESede/CBC/PKCS5Padding", iv);
// result: WBAnoV+7cLVI95LwVQhtysHb5/pjqVG35nP5Zdu7T/Cn94Sd8v1Vk9zpjQSFGSkv
WriteOutput("encrypted password="& binaryEncode( bytes, "base64" ));