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.
Related
I have a PEM file which includes my DSA private Key and i need to sign a string with this private key to send it to my partner API. (code attached)
For some reason i'm always getting Signature Invalid form my partner API. which means the sign is not in a good format.
My partner offers me to use bouncycastle for c# but i couldn't find any examples of how to sign the string with DSA from external PEM file.
Can i get any examples here?
Thanks.
I've tried to sign the string with a method i wrote:
public string ComputeSignature(string method, string path,string client_id, string timestamp, string username)
{
var data = String.Concat(method, path, client_id, timestamp);
if (!string.IsNullOrEmpty(username))
{
data = data + username;
}
string sig;
var encoding = new ASCIIEncoding();
byte[] dataInBytes = encoding.GetBytes(data);
using (System.Security.Cryptography.HMAC Hmac = new HMACSHA1())
{
Hmac.Key = encoding.GetBytes(privateKeyClean);
byte[] hash = Hmac.ComputeHash(dataInBytes, 0, dataInBytes.Length);
sig = Convert.ToBase64String(hash, 0, hash.Length);
}
return sig;
}
I solved this issue using bouncycastle:
private static string ComputeSignature()
{
AsymmetricCipherKeyPair asymmetricCipherKeyPair;
using (TextReader textReader = new StreamReader("myprivatekey.pem"))
{
asymmetricCipherKeyPair = (AsymmetricCipherKeyPair)new PemReader(textReader).ReadObject();
}
DsaPrivateKeyParameters parameters = (DsaPrivateKeyParameters)asymmetricCipherKeyPair.Private;
string text = TEXTTOENC;
if (!string.IsNullOrEmpty(userName))
{
text += userName;
}
Console.Out.WriteLine("Data: {0}", text);
byte[] bytes = Encoding.ASCII.GetBytes(text);
DsaDigestSigner dsaDigestSigner = new DsaDigestSigner(new DsaSigner(), new Sha1Digest());
dsaDigestSigner.Init(true, parameters);
dsaDigestSigner.BlockUpdate(bytes, 0, bytes.Length);
byte[] inArray = dsaDigestSigner.GenerateSignature();
string text2 = Convert.ToBase64String(inArray);
Console.WriteLine("Signature: {0}", text2);
return text2;
}
The reason that the DSA signature fails here is you aren't doing DSA at all. You're doing HMAC-SHA-1 using the contents of a key file as the HMAC key.
Read the DSA key parameters from the file.
.NET has no intrinsic support for reading PEM key files. But your title mentions BouncyCastle, so you can probably adapt the accepted answer to How to read a PEM RSA private key from .NET to DSA (using the PemReader).
Alternatively, you could use OpenSSL to make a self-signed certificate based off of this key, and put the cert+key into a PFX. .NET can load PFXes just fine.
Populate a DSA object.
If you go the PFX route, cert.GetDSAPrivateKey() will do the right thing (.NET 4.6.2). On versions older than 4.6.2 you can use cert.PrivateKey as DSA, which will only work for FIPS 186-2-compatible DSA (DSA was upgraded in FIPS 186-3).
If you're using BouncyCastle to read the PEM key parameters you can then stick with BouncyCastle or do something like using (DSA dsa = DSA.Create()) { dsa.ImportParameters(parametersFromTheFile); /* other stuff here */ }. (DSA.Create() will give an object limited to FIPS-186-2 on .NET Framework, but it can do FIPS-186-3 on .NET Core).
Sign the data
FIPS-186-2 only allows SHA-1 as the hash algorithm, FIPS-186-3 allows the SHA-2 family (SHA256, SHA384, SHA512). We'll assume that you're using FIPS-186-2/SHA-1 (if not, the substitutions required are hopefully obvious).
BouncyCastle: However they compute signatures. (Sorry, I'm not familiar with their APIs)
.NET 4.6.1 or older:
using (SHA1 hash = SHA1.Create())
{
signature = dsa.CreateSignature(hash.ComputeHash(dataInBytes));
}
.NET 4.6.2 or newer:
signature = dsa.SignData(dataInBytes, HashAlgorithmName.SHA1);
Soapbox
Then, once that's all said and done (or, perhaps, before): Ask yourself "why am I using DSA?". In FIPS 186-2 (and prior) DSA is limited to a 1024-bit key size and the SHA-1 hash. NIST SP800-57 classifies SHA-1 and DSA-1024 both as having 80 bits of security (Tables 2 and 3). NIST classified 80 bits of security as "Deprecated" for 2011-2013 and "Disallowed" for 2014 and beyond (Table 4). Modern usage of DSA (for entities subject to NIST recommendations) requires FIPS-186-3 support.
ECDSA gets ~keysize/2 bits of security; so the lowest (commonly supported) ECDSA keysize (keys based on NIST P-256/secp256r1) gets 128 bits of security, which NIST rates as good for 2031+.
RSA is also a better choice than DSA, because it has much better breadth of support for signatures still considered secure by NIST.
Though, if you're conforming to a protocol I guess there aren't a lot of options.
I need help in retrieving AES128-EBC encrypted string under Universal Windows Application.
I have a password in string that is used as a key. With it's 32 bits length MD5 hash value I would like to encrypt text with AES128-EBC.
Now I am using this for creating MD5Hash:
public string GetMD5Hash(String strMsg)
{
string strAlgName = HashAlgorithmNames.Md5;
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm
string strAlgNameUsed = objAlgProv.AlgorithmName;
IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
string hex = CryptographicBuffer.EncodeToHexString(buffHash);
return hex;
}
And this code for encryption:
public string Encrypt(string input, string pass)
{
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
CryptographicKey key;
string encrypted = "";
byte[] keyhash = Encoding.ASCII.GetBytes(GetMD5Hash(pass));
key = provider.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(keyhash));
IBuffer data = CryptographicBuffer.CreateFromByteArray(Encoding.Unicode.GetBytes(input));
encrypted = CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Encrypt(key, data, null));
return encrypted;
}
The cause why I am using SymmetricAlgorithmNames.AesEcbPkcs7 is when I am using SymmetricAlgorithmNames.AesEcb the output string is empty. I don't understand why.
My question is: Does my code create an AES128-ECB encryption? Because I not really sure it does. Because the software that is waiting for that encrypted data not recognizes it, so it cannot decrypt it.
My question is: Does my code create an AES128-ECB encryption? Because I not really sure it does. Because the software that is waiting for that encrypted data not recognizes it, so it cannot decrypt it.
Yes, your code create an AES encryption with ECB cipher mode and PKCS7 padding. If I correctly understand your problem, you said this works with AesEcbPkcs7, but failed using AesEcb, your software for decryption doesn't work for this.
The difference between AesEcbPkcs7 and AesEcb is, AesEcbPkcs7 use PKCS#7 block padding modes, and PKCS #7 algorithms automatically pads the message to an appropriate length, so you don’t need to pad the cipher to a multiple of the block-size of the algorithm you are using. So if you insist to use AesEcb to encrypt, I recommend to use `AesEcbPkcs7, otherwise an exception: The supplied user buffer is not valid for the requested operation.
So I guess, one possibility here in your decryption software, it may have the ability to use AesEcbPkcs7, but it doesn't implement the decrytion of AesEcb. Here I tested decryption based on your code, this code can decrypt AesEcb correctly:
public string Decrypt(string input, string pass)
{
var keyHash = Encoding.ASCII.GetBytes(GetMD5Hash(pass));
// Create a buffer that contains the encoded message to be decrypted.
IBuffer toDecryptBuffer = CryptographicBuffer.DecodeFromBase64String(input);
// Open a symmetric algorithm provider for the specified algorithm.
SymmetricKeyAlgorithmProvider aes = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcb);
// Create a symmetric key.
var symetricKey = aes.CreateSymmetricKey(keyHash.AsBuffer());
var buffDecrypted = CryptographicEngine.Decrypt(symetricKey, toDecryptBuffer, null);
string strDecrypted = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, buffDecrypted);
return strDecrypted;
}
Another possibility I think you catch the exception when using AesEcb and the user buffer is not valid for the requested operation and handled it when you call your Encrypt(string input, string pass) method, the encryption failed actually.
Use the same method below to encrypt the same plaintext twice,but the ciphertext are not the same after encryption.Why?
I want to encrypt my password when saving user information to database,and use the encryption method below.But I want to complete the edit-user feature,after inputing the old password and new password,find out the user according to the selected userid, and encrypt the old password,try to validate the old password typed is matched the password stored in database.But they never matched even though I input the right password.So is there any way to encrypt the password and validate if they are the same after using the same method to encrypt the password.
//encrypt the plainText
public static string Encrypt(string plainText)
{
if (plainText == null || plainText == "")
throw new ArgumentNullException("plainText");
var temp = Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = ProtectedData.Protect(temp, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted);
}
Based on the MSDN documentation of the ProtectedData class, it looks like it is performing 2-way encryption using a machine- or user-specific key. That means that if the machine or user that is active checking the key is different than the machine that originally encrypted it, you will get garbage results. If you only want to check the validity of a password, I would suggest a 1-way esecure hashing algorithm instead of 2-way encryption that supports decryption. Other posts on StackOverflow talk about using secure hashing algorithms (What is the most secure hashing algorithm in the .NET framework?).
Edit: I haven't tested this code on various systems under various users, but I expect it would work more universally because I don't expect that it is based on a machine or user key.
System.Security.Cryptography.SHA256Managed sha = new System.Security.Cryptography.SHA256Managed();
byte[] hashed = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(args[0]));
Console.WriteLine("Hash of {0}={1}", args[0], Convert.ToBase64String(hashed));
Edit 2: I would also add that my understanding of cryptography also suggests that you should probably include some "salt" in your hashed value. In other words, add something (like the user name) to the end of the password string before hashing it (both when entered and when checked) so that users with the same password don't end up with the same hash value, for example.
Some randomness in the output is actually a feature for encryption.
You get two choices:
If you ever need to recover the password from its encrypted form as part of your requirements, use encryption as you do, but decipher the result before making the comparison between expected and entered password.
If you don't really need to recover the password, use a hash algorithm instead (with a suitable salt). You'll be able to compare the two hashed values.
Here is something I threw together in a Console application. The Ciphers will not match, but as you can see, you can decrypt them just fine :)
class Program
{
private static string salt = "My Salt Brings All The Boys To The Yard... Wait A Second.....";
static void Main(string[] args)
{
for (int i = 0; 0 < 20; i++)
{
string password = "Guess my password!";
string cipher = Encrypt(password, salt);
string decipher = Decrypt(cipher, salt);
Console.WriteLine(decipher);
Thread.Sleep(500);
}
Console.ReadKey();
}
static public string Encrypt(string password, string salt)
{
byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
byte[] saltBytes = Encoding.Unicode.GetBytes(salt);
byte[] cipherBytes = ProtectedData.Protect(passwordBytes, saltBytes, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(cipherBytes);
}
static public string Decrypt(string cipher, string salt)
{
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = Encoding.Unicode.GetBytes(salt);
byte[] passwordBytes = ProtectedData.Unprotect(cipherBytes, saltBytes, DataProtectionScope.CurrentUser);
return Encoding.Unicode.GetString(passwordBytes);
}
}
I am creating a C# Winforms application which POSTs data to a server over HTTPS.
The login mechanism is supposed to be like this:
I send the username to the server, it responds with rsa-modulus and rsa-exponent
I encrypt the password using these given parameters and send username + password to the server for authentication
I have tried the RSACryptoServiceProvider class, but I cannot find samples or anything said on how we can do the encryption using a given modulus and exponent?.
I think that without specifying any values, its doing default encryption parameters..
So if anybody has done this before, can they give me some hints please? thanks
UPDATE: according to the suggestion by Mr. Carsten Konig, . I have tried to do it with RSAParameters and RSA.ImportParameters, but it returns a "BAD DATA" error with cryptographic exception. My code is given below.
I have also tried RSA.FromXmlString(mykey); (where mykey contains an xml string with modulus and exp) but I also get a "BAD DATA" errror with cryptographic exception... any idea anybody? or if its some microsoft bug, can anyone suggest some other decent library to do this easily?
RSAParameters rsaparam = new RSAParameters();
rsaparam.Modulus = modbytes;
rsaparam.Exponent = expbytes;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider() ;
RSA.ImportParameters(rsaparam);
byte[] encryptedData = RSA.Encrypt(dataToEncrypt, false)
You can do this by using the RSACryptoServiceProvider.Encrypt method. You will also need to use the RSACryptoServiceProvider.ImportParameters method and pass it an RSAParameters structure (this is where you set the exponent, modulus, etc).
Please have a look at the documentation in the link for the RSAParameters - it's very well documented what parameter you have to pass for what structure-field - should be no problem if you now the algorithm.
EDIT: here is the example straight from the MSDN-site:
class RSACSPSample
{
static void Main()
{
try
{ //initialze the byte arrays to the public key information.
byte[] PublicKey = {214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147};
byte[] Exponent = {1,0,1};
//Values to store encrypted symmetric keys.
byte[] EncryptedSymmetricKey;
byte[] EncryptedSymmetricIV;
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Create a new instance of RSAParameters.
RSAParameters RSAKeyInfo = new RSAParameters();
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = PublicKey;
RSAKeyInfo.Exponent = Exponent;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfo);
//Create a new instance of the RijndaelManaged class.
RijndaelManaged RM = new RijndaelManaged();
//Encrypt the symmetric key and IV.
EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false);
EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false);
Console.WriteLine("RijndaelManaged Key and IV have been encrypted with RSACryptoServiceProvider.");
}
//Catch and display a CryptographicException
//to the console.
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
}
}
}
Please note that only the key/iv gets encrypted - not arbitrary bytes - the length of those bytes is important too!
The allowed length is described in MSDN an depends on the OS!
If you are using RSACryptoServiceProvider.ToXmlString to export the modulus and exponent that the server sends, you need to use Convert.FromBase64String.
public RSAParameters SetPublicKey(string modulus, string exponent)
{
RSAParameters result = new RSAParameters();
result.Modulus = Convert.FromBase64String(modulus);
result.Exponent = Convert.FromBase64String(exponent);
return result;
}
One additional hint that was very useful for me:
In this line,
//Set RSAKeyInfo to the public key values.
SAKeyInfo.Modulus = PublicKey;
PublicKey can also be a direct, straightforward, array of bytes that you can get from the "Public Key" field of a X509 Certificate (directly).
I trying to verify the integrity of a file at work and an having a hard time of it. I'm not very well versed with encryption and hashing, so bear with me.
I have some files that have an MD5 hash located at the end of them. I have written code to grab the bytes that I think are the hash and they seen to be uniformly 128 bytes long. In the file, just before the hash, is the keyword "RSA1024", which I have taken to mean the hash is encrypted using RSA 1024.
I have what I know is the RSA key in a file, and have read out the bytes (always 258 bytes long). I have seen many tutorials which use FromXmlString() to pull in the key, but this RSA key was not generated using the .net framework, and is not in an XML format.
I have written the following method to decrypt the hash data using the key, and it throws this error when executing ImportCspBlob() - System.Security.Cryptography.CryptographicException: Bad Version of provider.
Any ideas?
public byte[] DecryptRSA(byte[] encryptedData, byte[] keyData)
{
CspParameters param = new CspParameters();
param.Flags = CspProviderFlags.UseExistingKey;
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
rsaProvider.ImportCspBlob(keyData);
byte[] decryptedData = rsaProvider.Decrypt(encryptedData, false);
return decryptedData;
}
Basic Algorithm
It may sound strange to want to "decrypt an MD5 hash", and especially when one says that they want to "decrypt it with a public key". But that is how digital signatures work. With RSA you can:
encrypt with private key
decrypt with the public key
The message digest is encrypted with the private key, and can then only be decrypted with the public key. That way you know that only the person with the private key could have signed the message.
Your key is most likely not a CSP-type key (it is most likely DER encoded). You can decrypt it using Bouncy Castle with the DER key like this:
RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(key);
byte[] rv = null;
RsaEngine eng = new RsaEngine();
eng.Init(false, privateKey);
int size = eng.GetOutputBlockSize();
rv = eng.ProcessBlock(cipher, 0, cipher.Length);
EDIT: to addressing GregS scenario that it may be a signature verify operation
If you are trying to verify a signature, you would need a certificate used to verify a message, the original message text, and the existing message signature to compare against.
What you do is pass in the original message text (minus the signature), the bytes of the message signature, and the path to the certificate you will use to verify the passed in signature.
Then, you will hash the original message and compare the result against the passed in signature.
Here is some code to illustrate:
private bool VerifySignature(string messageText, byte[] messageSignature, string certificatePath)
{
// Load the certificate from a file
X509Certificate2 cert = new X509Certificate2(certificatePath);
// Get public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
// Next, hash the messageText
SHA1Managed sha1 = new SHA1Managed();
byte[] messageBytes = Encoding.Unicode.GetBytes(messageText);
byte[] hash = sha1.ComputeHash(messageBytes);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), messageSignature);
}
MD5 is one-way hash. But you might check out hashing algorithm. There are some ways to break this hash, just do some research ;)