I am passing a public key from a UWP application:
// Open the algorithm provider for the specified asymmetric algorithm.
AsymmetricKeyAlgorithmProvider objAlgProv = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// Create an asymmetric key pair.
CryptographicKey keyPair = objAlgProv.CreateKeyPair(512);
// Export the public key to a buffer for use by others.
// Default X509SubjectPublicKeyInfo format
IBuffer buffPublicKey = keyPair.ExportPublicKey();
byte[] publicKey = null;
CrytographicBuffer.CopyToByteArray(buffPublicKey, out publicKey);
Conver.ToBase64String(publicKey);
The above string is sent to my Azure function App.
Here is a snippet of my Azure function app code:
// Algorithm = RsaPkcs1
// Key Length = 512
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512))
{
byte[] bPublicKey = Convert.FromBase64String(publickey);
byte[] bExponent = {1, 0, 1};
RSAParameters RSAKeyInfo = new RSAParameters() { Modulus = bPublicKey, Exponent = bExponent };
RSA.ImportParameters(RSAKeyInfo);
try
{
return RSA.Encrypt(sessionCode, System.Security.Cryptography.RSAEncryptionPadding.Pkcs1);
}
catch (Exception errenc)
{
}
return null;
}
I am trying to encrypt a value with public key and return.
I am running into issues with my Azure Function App and UWP Integration.
Azure side encrypts with the public key; no error is generated. Encrypted value is sent back to UWP. When I go to decrypt on the UWP side I get error "Value does not fall within the expected range."
Any help is appreciated. Having difficulty with the public key aspect on the azure side.
Thanks again
Related
I have a simple node js code with crypto js library through which I am decrypting a json object which I will receive from a .net based desktop application, hence the whole process is cross-platform. For testing I can easily encrypt using crypto js library and decrypt the cipher text generated by it. But my doubt is if this method is going to be compatible with encryption in .net. As I'm new to this, so far what I've learned is that the crypto js library generates a random Integration vector (IV) value. If an IV is generated by the .net code as well, do I need to specify it during decryption in node js as well? Currently I'm just using the simple example given in the documentation and no IV or padding is specified and I'm not sure if any default specifications are used by this library. In short I just need to make sure I'm doing this correctly and the method used for decryption won't cause any issues for the encryption part.
var CryptoJS = require("crypto-js");
var aeskey = "bQeThWmZq4t7w!z%C*F-JaNcRfUjXn2r";
var cloudcreds = {
accessKeyId: "abcdef",
accessKeySecret: "zxywvt",
};
//Encryption - Tbd with .net
var encryptedData = CryptoJS.AES.encrypt(
JSON.stringify(cloudcreds),
aeskey
).toString();
console.log("Encrypted Cloud Creds =>", encryptedData);
//Decryption - With node js
var bytes = CryptoJS.AES.decrypt(encryptedData, aeskey);
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
console.log("Decrypted Cloud Creds => ", decryptedData);
With the crypto module I'm now using my own IV and so far it seems like this could work.
var crypto = require("crypto");
var algorithm = "aes-256-cbc"; //algorithm to use
const key = "3zTvzr3p67VC61jmV54rIYu1545x4TlY"; //create key
var data = {
name: "Catalin",
surname: "Munteanu",
address: "Romania",
};
data = JSON.stringify(data);
const iv = "0000000000000000"; // generate different ciphertext everytime
const cipher = crypto.createCipheriv(algorithm, key, iv);
var encrypted = cipher.update(data, "utf8", "base64") + cipher.final("base64"); // encrypted text
console.log(encrypted);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
var decrypted = decipher.update(encrypted, "base64", "utf8") + decipher.final("utf8"); //deciphered text
console.log(JSON.parse(decrypted));
I have a requirement to generate RSA key pair in C# and then store public key in the database to be used in JWK format later on.
But I am unable to get the string from the RSAParams.Modulus.
I have tried UTF8,UTF32 and general encoding but still it is not showing.
Here is the code below from MSDN site.
try
{
// Create a new RSACryptoServiceProvider object.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Export the key information to an RSAParameters object.
//Pass false to export the public key information or pass
//true to export public and private key information.
RSAParameters RSAParams = RSA.ExportParameters(true);
byte[] modulus = RSAParams.Modulus;
var str = System.Text.Encoding.UTF8.GetString(RSAParams.Modulus);
Console.WriteLine(str);
Console.ReadKey();
}
}
catch (CryptographicException e)
{
//Catch this exception in case the encryption did
// not succeed.
Console.WriteLine(e.Message);
}
Thank you.
I assume that you want your output to be base64. Then you can use Convert.ToBase64String to convert the Exponent and the Modulus parts of the RSA key:
var exponent = Convert.ToBase64String(rsaParams.Modulus);
var modulus = Convert.ToBase64String(rsaParams.Exponent);
That is the same thing that your solution in the comments does (see source code of .ToXmlString) but it does require the detour over XML.
I have some code that creates a digital signature. The message as well as the signature is passed from one system to another. When its received, the signature is verified. This code has been run through Fortify, a service that analyzes code for security vulnerabilities. Fortify is reporting that "The method CreateDigitalSignature() in RSACryptography.cs performs public key RSA encryption without OAEP padding".
I see a parameter on the RSACryptoServiceProvider.Encrypt() method that if true, means to use OAEP padding. But I'm not using Encrypt(). I'm using a RSAPKCS1SignatureFormatter to generate and a RSAPKCS1SignatureDeformatter to verify the signature. So my question is how do I add the padding? Am I supposed to Encrypt the signature before sending it back? See my code where I have marked "IS WHAT I NEED TO DO" where I have added Encrypt and Decrypt calls. Is that what I need to do or something else?
// create a digital signature
// returns true if successful. Also, the public key (as an xml string) that can be sent to the other party to verify messages sent
public bool CreateDigitalSignature(string msgToSend, out string publicKey, out string signature)
{
bool rc = false;
publicKey = null;
signature = null;
try
{
// get the hash of the message to send
byte[] hashValue = GetHashedBytes(msgToSend);
// Load or generate a public/private key pair.
// If it already exists in the key container it will be loaded, otherwise, a new key pair is created
CspParameters cp = new CspParameters();
cp.KeyContainerName = KeyStoreContainerName;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(cp);
// get some info about the key:
CspKeyContainerInfo info = new CspKeyContainerInfo(cp);
//Create an RSAPKCS1SignatureFormatter object and pass it the RSACryptoServiceProvider to transfer the private key.
RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
// Set the hash algorithm
RSAFormatter.SetHashAlgorithm(hashAlgorithm); // "SHA256", "SHA1", etc.
//Create a signature for the hashed value of the data
byte[] signedHashValue = RSAFormatter.CreateSignature(hashValue);
// fortify says I need to use OAEP padding
// IS THIS WHAT I NEED TO DO? Encrypt the signature before I convert it to a string?
signedHashValue = RSA.Encrypt(signedHashValue, true);
// convert the signature to a string
signature = Convert.ToBase64String(signedHashValue);
// get the public key to return so it can be pased to the receiver and used to verify the signature
// There are two ways - either export the parameters or create an xml string
// Using export: This gets public key inforation only (specify true to get both public and private keys)
// RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
// get a string value of the public key
publicKey = RSA.ToXmlString(false);
// demonstration only. how to get the private key
//string privateKey = RSA.ToXmlString(true);
rc = true;
}
catch (Exception ex)
{
throw ex;
}
return rc;
}
And then to verify the signature:
public bool VerifySignature(string origintalData, string publicKeyXml, string signature)
{
bool verified = false;
try
{
// get the hashed value of the original data. used the specified algoritm stored in the this class
byte[] hashValue = GetHashedBytes(origintalData);
// get a byte array of the signature
byte[] signaturebytes = Convert.FromBase64String(signature);
// create a crypto provider
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
// set the public key of the crypto service provider
RSA.FromXmlString(publicKeyXml);
// create a deformatter
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
// set the hash algorithm. The sender must use the same algorithm
RSADeformatter.SetHashAlgorithm(hashAlgorithm);
// As per Fortify, need to use OAEP padding
// IS THIS WHAT i NEED TO DO - decrypt the signature before veryfying it?
signaturebytes = RSA.Decrypt(signaturebytes, true);
// verify the signature
verified = RSADeformatter.VerifySignature(hashValue, signaturebytes);
}
catch (Exception ex)
{
throw ex;
}
return verified;
}
Update:
After upgrading from .Net 4.5.1 to 4.6.1. I can use
byte[] signedHashValue = RSA.SignData(bytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
and the corresponding method
RSA.VerifyData(System.Text.Encoding.UTF8.GetBytes(originalData), signaturebytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
but I can't specify RSASignaturePadding.Pss. If I do I get an exception "Specified padding mode is not valid for this algorithm."
Also I get the same signature as before, so I feel I haven't really accomplished anything. No way to use AOEP padding?
I am encrypting the message in .NET with RSACryptoServiceProvider with private key. (PKCS#1 v1.5)
When I try to decrypt in .NET with the following code that uses public key everything works fine:
private static string Decrypt(string key, string content)
{
byte[] rgb = Convert.FromBase64String(content);
var cryptoServiceProvider = new RSACryptoServiceProvider(new CspParameters()
{
ProviderType = 1
});
cryptoServiceProvider.ImportCspBlob(Convert.FromBase64String(key));
return Convert.ToBase64String(cryptoServiceProvider.Decrypt(rgb, false));
}
When on the other hand I try to find an algorithm to make the same decrypt method in Android, I am failing to decrypt it properly with public key. I exported the modulus and exponent from public key in .NET in order to load it properly on Android.
The method in Android is here:
public String Decrypt(String input) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String modulusString = "mmGn1IXB+/NEm1ecLiUzgz7g2L6L5EE5DUcptppTNwZSqxeYKn0AuAccupL0iyX3LMPw6Dl9pjPXDjk93TQwYwyGgZaXOSRDQd/W2Y93g8erpGBRm/Olt7QN2GYhxP8Vn+cWUbNuikdD4yMfYX9NeD9UNt5WJGFf+jRkLk0zRK0A7ZIS+q0NvGJ/CgaRuoe3x4Mh1qYP9ZWNRw8rsDbZ6N2zyUa3Hk/WJkptRa6jrzc937r3QYF3eDTurVJZHwC7c3TJ474/8up3YNREnpK1p7hqwQ78fn35Tw4ZyTNxCevVJfYtc7pKHHiwfk36OxtOIesfKlMnHMs4vMWJm79ctixqAe3i9aFbbRj710dKAfZZ0FnwSnTpsoKO5g7N8mKY8nVpZej7tcLdTL44JqWEqnQkocRqgO/p3R8V/6To/OjQGf0r6ut9y/LnlM5qalnKJ1gFg1D7gCzZJ150TX4AO5kGSAFRyjkwGxnR0WLKf+BDZ8T/syOrFOrzg6b05OxiECwCvLWk0AaQiJkdu2uHbsFUj3J2BcwDYm/kZiD0Ri886xHqZMNExZshlIqiecqCskQhaMVC1+aCm+IFf16Qg/+eMYCd+3jm/deezT4rcMBOV/M+muownGYQ9WOdjEK53h9oVheahD3LqCW8MizABFimvXR3wAgkIUvhocVhSN0=";
String exponentString = "AQAB";
byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT);
byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger d = new BigInteger(1, dBytes);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d);
PublicKey key = keyFactory.generatePublic(keySpec);
//at one point I read somewhere that .net reverses the byte array so that it needs to be reversed for java, but who knows any more
/*byte[] inputArrayReversed = Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT);
for (int i = 0; i < inputArrayReversed.length / 2; i++) {
byte temp = inputArrayReversed[i];
inputArrayReversed[i] = inputArrayReversed[inputArrayReversed.length - 1];
inputArrayReversed[inputArrayReversed.length - 1] = temp;
}*/
byte[] decryptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedText = cipher.doFinal(Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT));
return Base64.encodeToString(decryptedText, Base64.NO_WRAP);
//return new String(decryptedText, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
Actually I tried also with different algorithms specified in Cypher class, also tried many other combinations, tried using SpongyCastle instead of built in Android RSA providers, but nothing worked. If anybody has any clue to point me in right direction, I would be absolutely grateful.
First hint is that decrypted string from .NET comes as around 25 characters long, and when I get Android to return decrypted string without exceptions it is usually much longer, around 500 bytes.
Second hint deleted
Third hint I also tried spongycastle, but it didn't help that much
Anyways, thank you in advance for any help!!!
UPDATE 1
Second hint is deleted because was wrong, disregard it. Now I have one question if the following can prove that the public key is loaded correctly, just to rule that problem out.
BigInteger modulus and exponent in the upper Android code and the following BigIntegers in .NET show equal integer values.
var parameters = csp.ExportParameters(false);
var modulusInteger = new BigInteger(parameters.Modulus.Reverse().Concat(new byte[] { 0 }).ToArray());
var exponentInteger = new BigInteger(parameters.Exponent.Reverse().Concat(new byte[] { 0 }).ToArray());
UPDATE 2
This and This SO answers provide some interesting clues
Heeh, the mistake was one of the basics, we had an architecture where we were doing encryption with public key and decryption with private key. The problem was in the architecture itself because as we initially set it up, we were sending private keys to all our client apps, which is big security flaw.
My mistake was that I assumed that on the client we have public key and actually from private key all the time I was trying to load the public key and then do decrypt.
If I knew the PKI in depth and communicated a bit better with my colleague, I could have noticed few things:
Decrypt can be done with private key only, while one the other hand verify can be done with public key, so when I saw Decrypt being used on client in .NET, I should have assumed that on the client we have private key (which is a security flaw in the end in the way we want to use PKI)
Few things that I already knew or learnt and want to share with others:
Private key should be kept secret, whether you want to have it on server or preferably only on one client because public key can easily be guessed from private key and then someone can easily repeat your whole encryption process easily and breach your security
PKI works for two scenarios:
First scenario is when you want to Encrypt something and that only specific person/computer can Decrypt it. In first scenario as you see, many stakeholders can have someone's Public key and send messages to him and that only he can read them with his Private key. Second scenario is when you want to be sure that the message that came to you was not altered and was sent by specific person/computer. In that case you Sign data with Private key and Verify it on the other end with Public key. The only process that is suitable for us is Sign <-> Verify because we send plain text license with signature in it, and thus on the client we want to be sure that nobody tampered with the plain text license and that it came from us.
In your code, if Decrypt or Verify functions throw exceptions in 50% of the time it is because of loading the incorrect key or incorrectly loading the correct key and in the other 50% it is because you are using the incorrect algorithm or because algorithm parameters are incorrectly set or because the algorithm implementations between platforms are incompatible (the last one is very rare)
.NET server code
public string Sign(string privateKey, string data)
{
_rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));
//// Write the message to a byte array using UTF8 as the encoding.
var encoder = new UTF8Encoding();
byte[] byteData = encoder.GetBytes(data);
//// Sign the data, using SHA512 as the hashing algorithm
byte[] encryptedBytes = _rsaProvider.SignData(byteData, new SHA1CryptoServiceProvider());
return Convert.ToBase64String(encryptedBytes);
}
.NET client code (Win Mobile)
private bool Verify(string key, string signature, string data)
{
CspParameters cspParams = new CspParameters { ProviderType = 1 };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(key));
byte[] signatureBytes = Convert.FromBase64String(signature);
var encoder = new UTF8Encoding();
byte[] dataBytes = encoder.GetBytes(data);
return rsaProvider.VerifyData(dataBytes, new SHA1CryptoServiceProvider(), signatureBytes);
}
Android client code:
public boolean Verify(RSAPublicKey key, String signature, String data)
{
try
{
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(key);
sign.update(data.getBytes("UTF-8"));
return sign.verify(Base64.decode(signature.getBytes("UTF-8"), Base64.NO_WRAP));
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
in .NET public key is exported in xml format with following code:
public string ExportPublicToXML(string publicKey)
{
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(new CspParameters()
{
ProviderType = 1
});
csp.ImportCspBlob(Convert.FromBase64String(publicKey));
return csp.ToXmlString(false);
}
and then modulus and exponent are used in Android to load public key:
private RSAPublicKey GetPublicKey(String keyXmlString) throws InvalidKeySpecException, UnsupportedEncodingException, NoSuchAlgorithmException
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String modulusString = keyXmlString.substring(keyXmlString.indexOf("<Modulus>"), keyXmlString.indexOf("</Modulus>")).replace("<Modulus>", "");
String exponentString = keyXmlString.substring(keyXmlString.indexOf("<Exponent>"), keyXmlString.indexOf("</Exponent>")).replace("<Exponent>", "");
byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT);
byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger d = new BigInteger(1, dBytes);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
I'm migrating/converting/rebuilding a Windows Phone 7.1 app to a Windows 8 Store App.
One method I am using in de WP7 app is giving me trouble:
private byte[] GetSHA256Key(string data, string secretKey)
{
byte[] value = Encoding.UTF8.GetBytes(data);
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
HMACSHA256 hmacsha256 = new HMACSHA256(secretKeyBytes);
byte[] resultBytes = hmacsha256.ComputeHash(value);
return resultBytes;
}
Looking at the documentation for Windows Store Apps I came up with this new code which I hoped would give the same result. But, no. I'm doing something wrong. But what?
private byte[] GetSHA256Key(string value, string secretKey)
{
// Create a MacAlgorithmProvider object for the specified algorithm.
MacAlgorithmProvider objMacProv = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
// Create a buffer that contains the message to be signed.
IBuffer valueBuffer = CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8);
// Create a key to be signed with the message.
IBuffer buffKeyMaterial = CryptographicBuffer.ConvertStringToBinary(secretKey, BinaryStringEncoding.Utf8);
CryptographicKey cryptographicKey = objMacProv.CreateKey(buffKeyMaterial);
// Sign the key and message together.
IBuffer bufferProtected = CryptographicEngine.Sign(cryptographicKey, valueBuffer);
DataReader dataReader = DataReader.FromBuffer(bufferProtected);
byte[] bytes = new byte[bufferProtected.Length];
dataReader.ReadBytes(bytes);
return bytes;
}
I'm not an expert on Cryptography. I'm not sure what I'm doing. Maybe there is somebody out there who can help me.
Thanx,
JP
using System.Runtime.InteropServices.WindowsRuntime;
private string GetSHA256Key(byte[] secretKey, string value)
{
var objMacProv = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
var hash = objMacProv.CreateHash(secretKey.AsBuffer());
hash.Append(CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8));
return CryptographicBuffer.EncodeToBase64String(hash.GetValueAndReset());
}
new HMACSHA256(keydata) uses a key as input, while MacAlgorithmProvider.CreateKey() uses input as 'Random data used to help generate the key', which is not a key for HMAC algorithm.