C# RSACryptoServiceProvider. Padding clearance. (Password encryption) - c#

Could anyone clarify the "whole" story with Rsa-related thing. (I guess, that's because of padding or something).
Ok. My general aim is to encrypt passwords (one way only) to store in database in hashed state.
My approach is:
I create private/public keys with this How to Generate Unique Public and Private Key via RSA (common) approach.
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
string publicPrivateKeyXML = rsa.ToXmlString(true);
string publicOnlyKeyXML = rsa.ToXmlString(false);
// do stuff with keys...
and when needed, do this:
var rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(publicOnlyKeyXML);
var valueToEncode = Console.ReadLine();
Console.WriteLine(Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(valueToEncode), false)));
// initially that's rsa.Encrypt(...
and every new time I do the same value encryption, I receive different values, which is not suitable to store password in such way (as I don't decrypt it for users' authentication, but just compare hashed value).
Could anyone clarify the situation with the encoding stuff?
And also, what are the common practices for passwords encoding to hashes these days (.NET/C#).
Thank you!

Related

Generate RSA KEY paramater from secret key without using bouncy castle or any third party library

I want to get the RSAKey Parameters in order to validate JWT token i.e. without using bouncy castle or any third party library.I search a lot but unable to find any thing that generate Modulus and Exponent i.e. part of RSAParameters.
Below sharing small snippet i have tried please let me know if you have any suggestions
string secret = "XXXXXXX";
string token = "XXXXXXX";
string[] parts = token.Split('.');
string header = parts[0];
string payload = parts[1];
var keyBytes = Convert.FromBase64String(secret); // your key here
var rsaKeyParameters = (RsaKeyParameters)PublicKeyFactory.CreateKey(keyBytes);//////I don't want to use this line of code as it is part of //////////third party library it only helps us in getting modulus and exponent
//////Please let me know any other way to achieve validation of JWT token if any
RSAParameters rsaParameters = new RSAParameters
{
Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
byte[] hash = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(header + '.' + payload));
var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
rsaDeformatter.SetHashAlgorithm("SHA256");
bool isv = rsaDeformatter.VerifySignature(hash, FromBase64Url(parts[2]));
Console.WriteLine("is" + isv);

HMAC SHA256 decrypt string in C#

How can I decrypt HMAC SHA256 encrypted string?
private string CreateToken(string message, string secret)
{
secret = secret ?? "";
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
How can I do reverse engineering if I have resultant string?
Thanks in advance.
A hash is a one-way function, by definition it is not reversible. What is it that you are trying to achieve?
If it's for something like password comparison then just hash the password you have and then compare the resulting hashes. Without any further context it's hard what to suggest.
HMACSHA256: It is a hash based Message authentication code.It is used to verify the message integrity on the insecure channel.It is a forward only hash algorithm, it can not be decrypted. It hashes the message based on the shared secret key which is known by both parties(sender and receiver).Sender uses the shared secret and compute the hash value from original data and send both the original data and hash value and then receiver re-computes the hash value from the original data. If both hash value matches it means no tempered in original data, if not matched it means tempered in original data for this scenario validation is failed.

Translating C# RSACryptoServiceProvider into JAVA Code

I was given this C# code written by the web service team that exposes some web service that I'm planning to consume. My password needs to be encrypted with this code so that the web service knows how to decrypt it on their end.
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(publicKey);
byte[] plainBytes = Encoding.Unicode.GetBytes(clearText);
byte[] encryptedBytes = rsa.Encrypt(plainBytes, false);
return Convert.ToBase64String(encryptedBytes);
}
I'm using Java to consume this web service and right now, I'm having problem translating that #C code into Java code because that web service can't decrypt my password properly.
Here's my current failed attempt:-
// my clear text password
String clearTextPassword = "XXXXX";
// these values are provided by the web service team
String modulusString = "...";
String publicExponentString = "...";
BigInteger modulus = new BigInteger(1, Base64.decodeBase64(modulusString.getBytes("UTF-8")));
BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(publicExponentString.getBytes("UTF-8")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(Base64.encodeBase64(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));
What did I do wrong? Thanks much.
2013-08-07 - UPDATE
I was reading this website and I realized that my modulus value and public exponent value are not in Hex. So, I modified my code a little bit and tried with RSA/ECB/PKCS1PADDING as mentioned by #Dev.
// my clear text password
String clearTextPassword = "XXXXX";
// these are the actual values I get from the web service team
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
Base64 base64Encoder = new Base64();
String modulusHex = new String(Hex.encodeHex(modulusString.getBytes("UTF-8")));
String publicExponentHex = new String(Hex.encodeHex(publicExponentString.getBytes("UTF-8")));
BigInteger modulus = new BigInteger(modulusHex, 16);
BigInteger publicExponent = new BigInteger(publicExponentHex);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(base64Encoder.encode(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));
When I hit the webservice, I'm getting this error: "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." It seems like the clear text password is still not encrypted properly.
Any help or suggestion is greatly appreciated. Thanks.
2013-08-09 - SOLUTION
I posted my final working solution below.
Found the solution.
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(modulusString);
byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = clearTextPassword.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.encodeBase64String(cipherData);
According to MSDN docs on RSACryptoServiceProvider.Encrypt, when the second argument is false the cipher uses PKCS#1 v1.5 padding. So right off the bat your cipher spec is incorrect.
Try RSA/ECB/PKCS1PADDING instead.
You are converting your key material to much in your second code example and you corrupted it which ends up making your cipher think you have more key material than you actually have and makes your message too long (which is triggering your error) as well as unintelligible to the decryption cipher on the other end. Convert directly to byte arrays and pass those to BigInteger.
String modulusString = "...";
String publicExponentString = "...";
byte[] mod = Base64.decodeBase64(modulusString);
byte[] e = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, mod);
BigInteger publicExponent = new BigInteger(1, e);
Using RSA/ECB/OAEPWithSHA-1AndMGF1Padding seems to be the solution for this issue.
Don't forget these replacements.
Encoding to Base64
Use System.Convert to convert the input to Base64.
Replace + by - and / by _. Example: Foo+bar/=== becomes Foo-bar_===.
Replace any number of = at the end of the string, with an integer denoting how many they were. Example: Foo-bar_=== becomes Foo-bar_3.
Decoding from Base64
Replace the digit at the end of the string by the same number of = signs. Example: Foo-bar_3 becomes Foo-bar_===.
Replace - by + and _ by /. Example: Foo-bar_=== becomes Foo+bar/===.
Use System.Convert to decode the preprocessed input from Base64.

How to transmit an AES encryption key?

On my Blackberry I am creating an AES key and encrypting data. I am then encrypting the AES key with RSA before sending to the client c#.net
The AES key is a byte array. How can I convert this to a string so that it can be encrypted by RSA and then decrypted on the .net side?
Do I have to convert to string?
I am transmitting the data via JSON. I guess my question is really how to transmit a but array in JSON? What character encoding would I use?
Thanks.
You can use the following, which is URL safe and relatively easy to visually inspect. This takes a little more storage than Convert.ToBase64String, but shouldn't be an issue with a fixed width encryption key.
string MyKey = BitConverter.ToString(MyAESKey); // dash removal is trivial here
OR
string MyKey = Convert.ToBase64String(MyAESKey);
Code Sample
byte[] a = new byte[256/8];
Random random = new Random();
random.NextBytes(a);
string base64 = Convert.ToBase64String(a);
byte [] b = Convert.FromBase64String(base64);
if (a.SequenceEqual(b))
// true
string c = BitConverter.ToString(a);
string[] c1 = c.Split('-');
byte[] d = new byte[arr.Length];
for (int i = 0; i < arr.Length; i++) d[i] = Convert.ToByte(c1[i], 16);
if (a.SequenceEqual(d))
// true

Bouncy Castle, RSA: transforming keys into a String format

I'm using RSA (Bouncy Castle API) in my C# project. I generated the keypair with this method:
RsaKeyPairGenerator r = new RsaKeyPairGenerator();
r.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
AsymmetricCipherKeyPair keys = r.GenerateKeyPair();
AsymmetricKeyParameter private_key = keys.Private;
AsymmetricKeyParameter public_key = keys.Public;
Now I want to save them in a txt file but the problem is that I can't convert them to a string format. I read in another post that keys must be serialized using:
PrivateKeyInfo k = PrivateKeyInfoFactory.CreatePrivateKeyInfo(private_key);
byte[] serializedKey = k.ToAsn1Object().GetDerEncoded();
Is it the right way? If yes, what should I do after this? Just convert them from byte[] to String?
You could also use PemWriter to store them in PEM format:
TextWriter textWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(keys.Private);
pemWriter.Writer.Flush();
string privateKey = textWriter.ToString();
Now privateKey contain something like this:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDFhB3xI1AzSMsKvt7rZ7gp2o/vd49zON89iL1ENvKkph7oFXa2
ew/hjzbAV33lHnFFlA/vA5SDCbggRyU1/SmjfuPJFEzFbgcPLuO1Sw6z+bWXiIFp
QNCOTIw11c0fbwk+kB2y4E1OkLv5f9atlNlekb4wBn8bMbFYtu6LGWjkFQIDAQAB
AoGANPoMteLM3hSGMSmkzOsS4Fb5CDe/SB5W8kA805ERX8wWuhUXtDYpukwZWVme
MNgLdagS5f7F1KfEtROgDW0Lv4/T/FWAbpgh8O2CPKYDY4ZXl8tmRH9dtYn/824l
veLxdgNjHwo5OMvuTSDMjC3tbg2UA3kmV4VAm5QotlRinUECQQDo4zvI5e81zWnS
kNrUARX80t432MOZyr0RvAaVJfBNQpJl0H0xp5LKP74hvPVO9WdJvJ0M4Z4QrmGO
bm2Hsfz5AkEA2R469YXxgPLDg/LvUQqfrl8Ji9+rip7eQP7iS/dt66NMS31/HymT
+HscEZ3qGlnQuyyyRR2rGQdhdjU42HNy/QJBAKbPTF1DxSmGbovyUauU84jaCW17
B++6dd6kDRr7x7mvO2lOAr50RwIy0h8cV6EsycTZIqy9VhigryP0GOQfKxECQA8T
uVZpab7hnNAh45qGGVabhOcwrhHfPGHZEU/jK7/sRBUN7vD0CzF7IxTaGXKhAAyv
auW/zKzdRVhXE858HeUCQQCGaaAg8GwM0qIS0nHRTLldu4hIGjKn7Sk0Z46Plfwr
oqPCtuP4ehX85EIhqCcoFnG6Ttr6AxSgNMZvErVxDBiD
-----END RSA PRIVATE KEY-----
Well, I don't know about the RSA-specific side, but once you've got an opaque binary string (i.e. it could contain any arbitrary data) the best bet for text conversion is Convert.ToBase64String(byte[]) which you can reverse with Convert.FromBase64String(string).
Do not use Encoding.GetString(byte[]) and Encoding.GetBytes(string) for this - the binary data isn't text in a particular encoding, and shouldn't be treated as such. You're almost bound to lose data if you try this.
This might be what you are looking out for:
http://www.rahulsingla.com/blog/2011/04/serializing-deserializing-rsa-public-private-keys-generated-using-bounty-castle-library
Try the following
RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator();
rsaKeyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), XXX));
AsymmetricCipherKeyPair keys = rsaKeyPairGenerator.GenerateKeyPair();
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keys.Private);
// Write out an RSA private key with it's asscociated information as described in PKCS8.
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
// Convert to Base64 ..
string serializedPrivateString = Convert.ToBase64String(serializedPrivateBytes);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keys.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublicString = Convert.ToBase64String(serializedPublicBytes);
If you convert the bouncycastle certificate to a .net certificate. The function to do this is in the bouncycastle lib (i believe it's in a class called DotNetUtilities). The RSACryptoServiceProvider has a function:
ToXmlString(bool includePrivateKey).
Which gives you an x representation of a certificate with if you want the private key containing all the components serialized to base64 seperately, exponent, modulus, and d (private exponent).

Categories

Resources