Please consider the following code:
SHA512 shaM = new SHA512Managed();
byte[] data = shaM.ComputeHash(Encoding.UTF8.GetBytes("password"));
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
string stringyHash = sBuilder.ToString();
I am using the above method to generate a SHA512 hashed password for an android device using Xamarin. However when I output stringyHash I am given a completely different hash to what I expect when using an online hashing service such as:
http://www.convertstring.com/Hash/SHA512
The difference for hashing the string "password" between the online tool and my Xamarin implementation is as follows:
Xamarin:
cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
Online tool:
B109F3BBBC244EB82441917ED06D618B9008DD09B3BEFD1B5E07394C706A8BB980B1D7785E5976EC049B46DF5F1326AF5A2EA6D103FD07C95385FFAB0CACBC86
I am relatively new to Xamarin and have little experience with hashing as a concept, so if someone can explain why the two are different, and how to adjust my code to make sure I am producing a correct hash, it would be appreciated.
cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
Is the SHA-512 hash of a zero length string:
byte[] data = shaM.ComputeHash(Encoding.UTF8.GetBytes(""));
So there is an issue with whatever you feed into your Xamarin method.
Related
How to calculate SHA512/256 or SHA512/224 without using external library?
In .Net 6, SHA512 hash can be calculated(documentation). Here is my example:
public string GetHashStringSHA512(string data)
{
using (SHA512 sha512 = SHA512.Create())
{
byte[] bytes = sha512.ComputeHash(Encoding.UTF8.GetBytes(data));
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}
}
As noted in the comments, it appears that the .Net library has not implemented SHA512/256 or SHA512/224.
To calculate SHA512/256 or SHA512/224 without using external library, the specification would need to be implemented. There's a document on the Cryptology ePrint Archive that includes some sample code. See also the NIST example. There are a variety of open source solutions as well to use as a starting point for your own code, such as the SHA512 library at wolfSSL that includes both SHA512/256 and SHA512/224.
I have a node server and and passing up usernames and passwords from unity. here is what I have so far. I am still trying to learn and understand encryption and there are so many types and Im just confused. The code below will successfully encrypt and decrypt the string. Is this code a good code to use for something like this or is there a better alternative? What type of encryption is this actually using? How would I decrypt this on node js? Any additional example, links, or comments would be much appreciated. Thanks!
public string encrypt(string toEncrypt) {
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "ThisIsAKey"; // This is the key used to encrypt and decrypt can be anything.
var provider = new RSACryptoServiceProvider(cspParams);
byte[] tempencryptedBytes = provider.Encrypt(System.Text.Encoding.UTF8.GetBytes(toEncrypt), true);
string encrypted = Convert.ToBase64String(tempencryptedBytes); // convert to base64string for storage
Debug.Log("encrypted: " + encrypted);
// Get the value stored in RegString and decrypt it using the key.
return encrypted;
}
public string decrypt(string toDecrypt) {
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "ThisIsAKey"; // This is the key used to encrypt and decrypt can be anything.
var provider = new RSACryptoServiceProvider(cspParams);
string decrypted = System.Text.Encoding.UTF7.GetString(provider.Decrypt(Convert.FromBase64String(toDecrypt), true));
Debug.Log("decrypted: " + decrypted);
return decrypted;
}
EDIT: SHA256 code that i used added here. It doesnt output the correct string value.
SHA256 sha256 = SHA256Managed.Create();
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("randy");
byte[] hash = sha256.ComputeHash(bytes);
string result = "";
for (int i = 0; i < hash.Length; i++) {
result += String.Format("{0:x2}", i);
}
Debug.Log("hash: " + result);
string result2 = Convert.ToBase64String(hash);
Debug.Log("hash: " + result2);
If something is good to be used depends on the context.
If you need to pass a username / password combination then RSA encryption may indeed be used, preferably in addition to TLS transport security for the connection. If you just need to verify a username or password then you may need a password hash (or PBKDF) instead. The .NET version of PBKDF2 can be found in this badly named class.
Even if all the cryptographic algorithms are secure then your system may still not be secure. For instance, if you cannot trust the public key that you are encrypting with then you may be encrypting with a public key of an attacker.
So your code is using this specific encrypt call using a boolean to select the encryption algorithm. As the boolean is set to true that means that RSA with OAEP is being used, using the default SHA-1 hash function internally (which is secure for OAEP encryption, even if SHA-1 isn't). It's better to use the newer call where you can specify the padding without the boolean anti-pattern. In that case you can also specify the internal hash function to be used.
RSA with OAEP is specified in PKCS#1 v2.2, which is specified in turn in RFC 8017. This will even specify the byte order to be used (RSA operates on numbers in the end, which can be encoded to bytes in different ways). As long as you use a compliant library in any runtime and know how to encode / decode the plaintext and ciphertext (when using text) then you should be able to decrypt using any runtime that implements RSA with OAEP, if you have the matching private key of course.
As a general rule, passwords shouldn't be decryptable. You should hash the password (using something like SHA256), then compare that to a stored hash in your Node.js code. Never store or transfer passwords plaintext or in a method that can be converted back to the original password.
In C#, hashing will look something like:
string toHash = "PasswordToBehashed";
SHA256 sha = new SHA256();
byte[] tempencryptedBytes = sha.ComputeHash(Encoding.UTF8.GetBytes(toHash));
For reference, see the SHA256 class and an example using MD5 instead of SHA256.
(I've already been thru a lot of Stackoverflow/google results trying to find a fix for this.)
I am validating JWTs signed with RS256 using the default C# JwtSecurityTokenHandler. In some cases, the validation fails when it shouldn't. Concretely, tokens from a given Authorization Server validate properly while tokens form another Authorization Server won't.
BUT... Using the same JWTs and RSA Certificates on JWT.IO validates ALL the tokens succesfully. This is the part that makes me believe that there's something wrong/unusual in the C# implementation. I am also able to validate the same JWTs using the same Certificates using the oidc-client JavaScript library. The one place where the validation sometimes fails is in C#.
I traced the error down to JwtSecurityTokenHandler's ValidateSignature method. Searching the original github code and googling about RSA, I came down with this bare-bone method which allows me to reproduce the problem in a plain console app:
static void ValidateJWT(string token, string modulus, string exponent)
{
string tokenStr = token;
JwtSecurityToken st = new JwtSecurityToken(tokenStr);
string[] tokenParts = tokenStr.Split('.');
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(
new RSAParameters()
{
Modulus = FromBase64Url(modulus),
Exponent = FromBase64Url(exponent)
});
SHA256 sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + '.' + tokenParts[1]));
RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
rsaDeformatter.SetHashAlgorithm("SHA256");
var valid = rsaDeformatter.VerifySignature(hash, FromBase64Url(tokenParts[2]));
Console.WriteLine(valid); // sometimes false when it should be true
}
private static byte[] FromBase64Url(string base64Url)
{
string padded = base64Url.Length % 4 == 0
? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
string base64 = padded.Replace("_", "/")
.Replace("-", "+");
return Convert.FromBase64String(base64);
}
It is from that RSACryptoServiceProvider and using RSAKeys from here (https://gist.github.com/therightstuff/aa65356e95f8d0aae888e9f61aa29414) that I was able to Export the Public Key that allows me to validate JWTs successfully on JWT.IO.
string publicKey = RSAKeys.ExportPublicKey(rsa);
I can't provide actual JWTs to this post (they expire anyways), but does anyone knows of a crypto behavior specific to C# that could explain these validation errors, which don't happen in JavaScript nor on JWT.IO ?
And if so, any solution for this?
Thanks,
Martin
https://www.rfc-editor.org/rfc/rfc7518#section-6.3.1.1
Note that implementers have found that some cryptographic libraries
prefix an extra zero-valued octet to the modulus representations they
return, for instance, returning 257 octets for a 2048-bit key, rather
than 256. Implementations using such libraries will need to take
care to omit the extra octet from the base64url-encoded
representation.
In the case of one of the tokens you provided on a copy of this issue elsewhere, the decode of the modulus includes a prefixed 0x00 byte. This causes downstream problems. But you can fix their non-conformance.
byte[] modulusBytes = FromBase64Url(modulus);
if (modulusBytes[0] == 0)
{
byte[] tmp = new byte[modulusBytes.Length - 1];
Buffer.BlockCopy(modulusBytes, 1, tmp, 0, tmp.Length);
modulusBytes = tmp;
}
It looks like RS256 treats the signature as opaque bytes, so it will encode it as-is. So you probably don't need this correction (though it's where my investigation started):
byte[] sig = FromBase64Url(tokenParts[2]);
if (sig.Length < modulusBytes.Length)
{
byte[] tmp = new byte[modulusBytes.Length];
Buffer.BlockCopy(sig, 0, tmp, tmp.Length - sig.Length, sig.Length);
sig = tmp;
}
I have encrypt a string in c# with md5 hash and stored it to mysql database with code below.
function getMd5Hash(string md5)
{
string md5;
MD5 md5hash = new MD5CryptoServiceProvider();
md5hash.ComputeHash(ASCIIEncoding.ASCII.GetBytes(pass));
byte[] result = md5hash.Hash;
StringBuilder strbuilder = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
strbuilder.Append(result[i].ToString("x2"));
}
md5 = strbuilder.ToString();
}
//salt is ten random character
string pass = getMd5Hash(getMd5Hash("fermentasi")+salt);
Now, how can I hashing same string in PHP to get match value with my function in c# ?
I have been searching but didn't find a solution so far.
Thanks for Helping and sorry for my bad english :)
I recently came across this same issue and found a pretty good resource here. We were also trying to encrypt an item from a PHP form, store it in MySQL and hopefully decrypt it in (and for use in) a C# application. It uses AES which is much more secure than MD5 as well. Here is a snipet of my PHP code
function encrypt($text) {
$iv = "45287112549354892144548565456541";
$key = "anjueolkdiwpoida";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv]);
$crypttext64=base64_encode($crypttext);
return $crypttext64;
}
function decrypt ($text) {
$iv = "45287112549354892144548565456541";
$key = "anjueolkdiwpoida";
$crypttext64=base64_decode($text);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256 , $key, $crypttext64 , MCRYPT_MODE_CBC, $key );
return $decrypted;
}
So all you need to do is call encrypt($text_to_encrypt) before storing it in the database, and you can call decrypt($encrypted_text) to take it back out. However, as I was only on the PHP side, you might need to refer to the previous link to see what goes into decrypting it on the C# side. Apologies for only a half-answer.
And this will only work if you have control of the C# too and you have the option of moving away from the MD5.
I've got the following code sample in Java, and I need to re-enact it in C#:
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pkcs8PrivateKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);
Signature sign = Signature.getInstance("MD5withRSA");
sign.initSign(privKey);
sign.update(data);
byte[] signature = sign.sign();
Is it possible with the standard .Net Crypto API, or should I use BouncyCastle?
Thanks,
b.
Another way is to use CNG (Cryptography Next Generation), along with the Security.Cryptography DLL from CodePlex
Then you can write:
byte[] dataToSign = Encoding.UTF8.GetBytes("Data to sign");
using (CngKey signingKey = CngKey.Import(pkcs8PrivateKey, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsa = new RSACng(signingKey))
{
rsa.SignatureHashAlgorithm = CngAlgorithm.MD5;
return rsa.SignData(dataToSign);
}
Updated thanks to Simon Mourier: with .Net 4.6, you no longer need a separate library
I am running into a very similar problem trying to create a native C# tool for packing Chrome extensions (using SHA1, not MD5, but that's not a big difference). I believe I have tried literally every possible solution for .Net: System.Security.Cryptography, BouncyCastle, OpenSSL.Net and Chilkat RSA.
The best solution is probably Chilkat; their interface is the cleanest and most straightforward, it's well-supported and well-documented, and there are a million examples. For instance, here's some code using their library that does something very close to what you want: http://www.example-code.com/csharp/rsa_signPkcs8.asp. However, it's not free (though $150 is not unreasonable, seeing as I have burned 2 days trying to figure this out, and I make a bit more than $75 a day!).
As a free alternative, JavaScience offers up a number of crypto utilities in source form for multiple languages (including C#/.Net) at http://www.jensign.com/JavaScience/cryptoutils/index.html. The one that's most salient to what you are trying to do is opensslkey (http://www.jensign.com/opensslkey/index.html), which will let you generate a RSACryptoServiceProvider from a .pem file. You can then use that provider to sign your code:
string pemContents = new StreamReader("pkcs8privatekey.pem").ReadToEnd();
var der = opensslkey.DecodePkcs8PrivateKey(pemContents);
RSACryptoServiceProvider rsa = opensslkey.DecodePrivateKeyInfo(der);
signature = rsa.SignData(data, new MD5CryptoServiceProvider());
You can use this code . At the first you should download "BouncyCastle.Crypto.dll" from http://www.bouncycastle.org/csharp/ .
/// <summary>
/// MD5withRSA Signing
/// https://www.vrast.cn
/// keyle_xiao 2017.1.12
/// </summary>
public class MD5withRSASigning
{
public Encoding encoding = Encoding.UTF8;
public string SignerSymbol = "MD5withRSA";
public MD5withRSASigning() { }
public MD5withRSASigning(Encoding e, string s)
{
encoding = e;
SignerSymbol = s;
}
private AsymmetricKeyParameter CreateKEY(bool isPrivate, string key)
{
byte[] keyInfoByte = Convert.FromBase64String(key);
if (isPrivate)
return PrivateKeyFactory.CreateKey(keyInfoByte);
else
return PublicKeyFactory.CreateKey(keyInfoByte);
}
public string Sign(string content, string privatekey)
{
ISigner sig = SignerUtilities.GetSigner(SignerSymbol);
sig.Init(true, CreateKEY(true, privatekey));
var bytes = encoding.GetBytes(content);
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
/* Base 64 encode the sig so its 8-bit clean */
var signedString = Convert.ToBase64String(signature);
return signedString;
}
public bool Verify(string content, string signData, string publickey)
{
ISigner signer = SignerUtilities.GetSigner(SignerSymbol);
signer.Init(false, CreateKEY(false, publickey));
var expectedSig = Convert.FromBase64String(signData);
/* Get the bytes to be signed from the string */
var msgBytes = encoding.GetBytes(content);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
return signer.VerifySignature(expectedSig);
}
}
This SO question answers the PKCS#8 part of your code. The rest of the .NET RSA classes are a bizarre jumble of partially overlapping classes that are very difficult to fathom. It certainly appears that signature support is in either of the RSACryptoServiceProvider and/or RSAPKCS1SignatureFormatter classes.
Disclaimer: I know Java and cryptography, but my knowledge of C# and .NET is very limited. I am writing here only under the influence of my Google-fu skills.
Assuming that you could decode a PKCS#8-encoded RSA private key, then, from what I read on MSDN, the rest of the code should look like this:
byte[] hv = MD5.Create().ComputeHash(data);
RSACryptoServiceProvider rsp = new RSACryptoServiceProvider();
RSAParameters rsp = new RSAParameters();
// here fill rsp fields by decoding pkcs8PrivateKey
rsp.ImportParameters(key);
RSAPKCS1SignatureFormatter rf = new RSAPKCS1SignatureFormatter(rsp);
rf.SetHashAlgorithm("MD5");
byte[] signature = rf.CreateSignature(hv);
The relevant classes are in the System.Security.Cryptography namespace.
As for the PKCS#8 key blob decoding (i.e. filling in the rsp fields), I found this page which describes a command-line utility in C# which can perform that job. The source code is provided and is a single C# file. From what I read in it, that code decodes the PKCS#8 file "manually"; indirectly, this should mean that raw .NET (2.0) does not have facilities for PKCS#8 key file decoding (otherwise the author of that tool would not have went to the trouble of implementing such decoding). For your task at hand, you could scavenge from that source file the parts that you need, skipping anything about PEM and symmetric encryption; your entry point would be the DecodePrivateKeyInfo() function, which apparently expects a DER-encoded unencrypted PKCS#8 file, just like Java's PKCS8EncodedKeySpec.