TrinityCore deprecated the old sha_pass_hash column on the auth table, in favour of the much safer SRP6 method. However, I am unable to properly calculate the verifier in C#/dotnet NOR in PHP using the example provided here. I've looked at examples but it doesn't seem to be working the way that the TrinityCore developers suggest. Does anyone know about SRP6 that might be able to figure out what's wrong in the code? I've also looked at this example but it uses a hardcoded salt? If someone can show me what's wrong in the PHP I might be able to figure out what's wrong with the .NET
The code I tried looks closest to the first example, but I flip my arrays around to be little-endian.
public byte[] CalculateVerifier(string username, string password, byte[] salt)
{
if (BitConverter.IsLittleEndian)
{
return BigInteger.ModPow(
g,
new BigInteger(Hash(salt, Hash(Encoding.UTF8.GetBytes($"{username.ToUpper()}:{password.ToUpper()}")))),
N
).ToByteArray();
}
else
{
return BigInteger.ModPow(
g,
new BigInteger(Hash(salt, Hash(Encoding.UTF8.GetBytes($"{username.ToUpper()}:{password.ToUpper()}")).Reverse().ToArray())),
N
).ToByteArray();
}
}
public bool VerifySRP6Login(string username, string password, byte[] salt, byte[] verifier)
{
// re-calculate the verifier using the provided username + password and the stored salt
byte[] checkVerifier = CalculateSRP6Verifier(username, password, salt);
Console.WriteLine($"{Encoding.ASCII.GetString(verifier)} {verifier.Length} bytes\n{Encoding.ASCII.GetString(checkVerifier)} {checkVerifier.Length} bytes");
Console.WriteLine($"{new BigInteger(verifier)}\n{new BigInteger(checkVerifier)}");
// compare it against the stored verifier
return verifier.SequenceEqual(checkVerifier);
}
public byte[] Hash(byte[] componentOne, byte[] componentTwo)
{
if (componentOne == null) throw new ArgumentNullException(nameof(componentOne));
if (componentTwo == null) throw new ArgumentNullException(nameof(componentTwo));
//WoW expects non-secure SHA1 hashing. SRP6 is deprecated too. We need to do it anyway
using (SHA1 shaProvider = SHA1.Create())
{
//See Jackpoz's Combine function
return shaProvider.ComputeHash(componentOne.Concat(componentTwo).ToArray());
}
}
public byte[] Hash(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
//WoW expects non-secure SHA1 hashing. SRP6 is deprecated too. We need to do it anyway
using (SHA1 shaProvider = SHA1.Create())
{
return shaProvider.ComputeHash(bytes);
}
}
The answer was found in this solution, apparently I wasn't properly making my BigInteger, because the data was an unsigned int and I was treating it as signed.
Related
Goodmorning,
I am trying to implement some new things on a legacy system.
To manage the authentication I need to pass an encrypted string that comes from a nodejs source to a php page and decrypt it and verify it.
The following code is what I have tried to do, the main problem is that I cannot change the nodejs code, even if it is deprecated, so I have to try to implement the correct way to decrypt it in php, the password is the same, the algorithm is aes-256-ctr and there is no padding and initialization vector.
NODE.JS CODE
function encrypt(text) {
var cipher = crypto.createCipher(algorithm, password())
cipher.setAutoPadding(false);
var crypted = cipher.update(text, 'utf8', 'hex')
crypted += cipher.final('hex')
return crypted
}
function decrypt(text) {
var decipher = crypto.createDecipher(algorithm, password())
decipher.setAutoPadding(false);
var dec = decipher.update(text, 'hex', 'utf8')
dec += decipher.final('utf8')
return dec
}
PHP CODE
function decrypt($text)
{
return openssl_decrypt(hex2bin($text), _algorithm_, password(), OPENSSL_NO_PADDING);
}
Somehow these two decrypt works in a different way, what can I do to make the php one behave as the nodejs decrypt?
EDIT: as suggested by #Chris Haas I checked if the conversion by and from hex or utf-8 are the same, and I can confirm that they are the same
var Buffer = require('buffer').Buffer
const buf = Buffer.from('hello world', 'utf8');
console.log(buf.toString('hex'));
echo bin2hex("hello world");
I've been trying to get this for hours now, and I can't find what's wrong. I'm using a php RESTful API that I made to encrypt data using asymmetric encryption.
First, I save my user's public key in the server by exporting it in android:
fun exportPublicKey() : String {
val publicKey = getPublicKey()
return android.util.Base64.encodeToString(
publicKey!!.encoded,
android.util.Base64.NO_WRAP
)
}
This allows me in the PHP server to do that:
$public_key_core = $_POST["public_key"];
$public_key = "-----BEGIN PUBLIC KEY-----\n" . $public_key_core . "\n-----END PUBLIC KEY-----";
I am unsure that's the right way but openssl seems to be "ok" with that key ?
I then tested my keystore in local using both keys, and it works just fine using this code:
Encrypt:
fun encryptAsymmetricData(data: String, usePrivateKey : Boolean = true): ByteArray {
val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
val encryptedBytes: ByteArray
if (usePrivateKey){
cipher.init(Cipher.ENCRYPT_MODE, getPrivateKey())
encryptedBytes = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
} else {
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey())
encryptedBytes= cipher.doFinal(data.toByteArray(Charsets.UTF_8))
}
return encryptedBytes
}
Decrypt:
fun decryptAsymmetricData(data: ByteArray): String{
val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey())
return cipher.doFinal(data).toString(Charsets.UTF_8)
}
Using this works because I do ".toByteArray(Charsets.UTF_8)" on the encryptData result.
Now here's the problem, I use base64 encoding and do the following to encrypt in PHP:
openssl_public_encrypt($token->token, $encrypted_token, $user->public_key);
openssl_public_encrypt($user->id, $encrypted_id, $user->public_key);
[...]
'encrypted_user_id' => base64_encode($encrypted_id),
'encrypted_token' => base64_encode($encrypted_token)
But when I try to decrypt this in Android I'm getting an exception "javax.crypto.IllegalBlockSizeException" caused by this code:
val tokenBA = String(getDecoder().decode(this.encryptedToken), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val userIDBA = String(getDecoder().decode(this.encryptedUserId), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val token = App.encryptionController.decryptAsymmetricData(tokenBA)
val userID = App.encryptionController.decryptAsymmetricData(userIDBA)
(The logic being, I use base64 to send back my data in PHP, so I convert it to UTF8 in Android, then get the associated ByteArray to decrypt it ?)
I know that the encryption works in "local" but it doesn't when using both PHP and KeyStore, so I guess the problem is coming either from the PHP encryption, or from the way I try to decrypt it in android, but I can't seem to find what wrong, could you guys help me there please ?
Thank you by advance!
Ok, after searching and making sure the issue wasn't the public key stored in the PHP server, I found the answer. It was caused by the way to convert the "base64" string in an actual ByteArray in the App. This worked:
val token = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedToken))
val userID = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedUserId))
This is only working because I do the "base64_encode" in the server, for some (bad) reason I thought it was needed to go back to UTF8 to get the ByteArray in the app.
So I am making my custom highscore board in mobile game, using unity engine.
I setup my mysql database, and bought highscore asset from store, it works, but only with english user name. So basically it send user's name, score to .php script.
But I want that script also can receive korean characters as user's nickname. My users will use korean characters too as nickname not only english characters.
How can I achieve this?
Here are codes.
------------------(Highscore.cs at unity side)
WWWForm rsFm = new WWWForm();
rsFm.AddField("name",name);
// at here name field, I want to receive korean characters as well.
rsFm.AddField("tables",tables);
rsFm.AddField("hash",GetHash(name));
WWW rs = new WWW(registerUserURL,rsFm);
yield return rs;
..................
string GetHash(string usedString){ //Create a Hash to send to server
MD5 md5 = MD5.Create();
byte[] bytes = Encoding.ASCII.GetBytes(usedString+secretKey);
byte[] hash = md5.ComputeHash(bytes);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < hash.Length; i++){
sb.Append(hash[i].ToString("x2"));
}
return sb.ToString();
}
RegisterUser.php
<?php
include('ServerConnect.php');
$connection = Connect();//Attempt to Connect to MYSQL Server & DataBase
//Get variables from unity
$name = $_POST['name'];
$date = strtotime(date("Y-m-d"));
$tables = $_POST['tables'];
//Security Check
$hash = $_POST['hash'];
if(CheckKey($hash,$name) == 'fail'){ //Check if hash is valid
echo 'Security Failure'; exit;
}
ServerConnect.php
function CheckKey($hash,$name){ //Check weather Md5 hash matches
global $secretKey;
$checkHash = md5($name.$secretKey);
if(strcmp($checkHash,$hash) == 0){
return 'pass'; //hash matches
}else{
return 'fail'; //hash failed
}
}
When I input korean character and send, console Result says "Security Failure" at above code.
like a user has stated before, you are using the wrong encoding if you will be using out of ascii chars (the case for korean, japanese, etc). You should be using Encoding.UTF8.GetBytes instead of Encoding.ASCII.GetBytes, take a look at https://msdn.microsoft.com/en-us/library/system.security.cryptography.md5%28v=vs.110%29.aspx for the example function GetMd5Hash. If you run ASCII md5, a different md5 will be generated.
The "salt" is the secret key you are using. $secretKey in PHP and secretKey in C#. You should read a little about security if you dont know what salt is, because if you dont you'll think you created a secure(r) system and you havent.
Hope it helps.
I am converting .net project to PHP . But unable to convert the following code:
public static string HashString(string value)
{
SHA1 hasher = new SHA1CryptoServiceProvider();
UTF8Encoding enc = new UTF8Encoding();
byte[] hashInBytes = hasher.ComputeHash(enc.GetBytes(value));
return Convert.ToBase64String(hashInBytes);
}
So far I have done this but result is not same:
function HashString($str) {
return base64_encode(sha1($str));
}
Please help, thanks.
The reason behind the difference is, PHP uses ASCII encoding for hash calculations.
In C#, you can replace UTF8Encoding with ASCIIEncoding in order to have same results.
Finally I found the solution:
This is the final code which is equivalent to .net code:
function HashString($str) {
return base64_encode(sha1($str,true));
}
I have added "true" with sha1 function.
I am using the SHA512 hash to transfer some encrypted data between my app and it's backend. However, I'm having a odd situation and have no idea what might be causing it.
So, I've got following setups tested:
Android 2x SHA512
Android 1x SHA512 -> CryptoJS 1x SHA512
PHP 2x SHA512
So, when I do the first 2x Android hashing, I get the same result as when I do the 1x android -> 1x cryptojs.
However, when I do the PHP 2x, I get the same result as I get on the first Android pass, but the second encryption pass of the PHP is different.
On PHP, I've tried both the hash() and openssl_digest() functions with raw bytes as output.
PHP:
$firstpass = base64_encode(hash('sha512', $enteredPassword, true));
//$firstpass = base64_encode(hash('sha512', $enteredPassword, true));
//$secondpass = base64_encode(openssl_digest($firstpass, 'sha512', true));
$secondpass = base64_encode(hash('sha512', $firstpass, true));
Android:
public static String encryptPassword(String password) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
if (md != null) {
md.update(password.getBytes());
byte byteData[] = md.digest();
String base64 = Base64.encodeToString(byteData, Base64.DEFAULT);
return base64;
}
return password;
}
CryptoJS:
var password = cryptojs.SHA512(req.params.password);
var basepassword = password.toString(cryptojs.enc.Base64);
Why would my first hash be correct and my second not and how could I fix this?
SHA1 is not made for security, don't use it for this.
Grab any implementation of BCrypt and do security right.
As for the different hashes: Most likely an encoding issue related to Strings.