my Windows Mobile 6 application needs to send data to a php REST web service of a company.
that WS has a method that returns the public key to use to encrypt the username and password of the user of the mobile application.
They give me a sample code written in php which simply calls the WS to obtain the public key and then calls openssl_public_encrypt with, as public key parameter, the value returned by the web service's call. This is an excerpt
function CallAPI($url, $data = false)
{
$curl = curl_init();
$url = sprintf("%s?%s", $url, http_build_query($data));
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
return curl_exec($curl);
}
$public_key=CallAPI(, "https://***.***.***/index.php/rest/getPKey");
$json = json_encode(array("username"=>"********","psw"=>"*******"));
openssl_public_encrypt($json, $encrypted, $public_key);
From Windows Mobile point of view seems to be more complicated than this, also I am not really into RSA encryption.
the first thing I do is to call the WS to obtain the public key and I save it into a string. The following is the code I use to encrypt data.
ASCIIEncoding ByteConverter = new ASCIIEncoding();
byte[] dataToEncrypt = ByteConverter.GetBytes(data_string);
byte[] public_key = ByteConverter.GetBytes(public_key_string);
byte[] encryptedData;
RSACryptoServiceProvider RSA_provider = new RSACryptoServiceProvider();
RSAParameters key_info = RSA_provider.ExportParameters(false);
key_info.Modulus = public_key;
RSA_provider.ImportParameters(key_info);
encryptedData = RSA_provider.Encrypt(dataToEncrypt, false);
string encrypted_string = ByteConverter.GetString(encryptedData, 0, encryptedData.Length);
return encrypted_string;
If I try to send data to the web service it fails due to an authentication failure, also I noted that from php code that the encrypted string is always of 256 chars, while the .NET encrypted string has completely different length.
What I'm doing wrong?
I have seen a lot of questions on StackOverflow about working with .NET and RSA Encryption, but the features that are used are not contained in the Compact Framework.
Thank you in advance.
I've solved using BouncyCastle API for ecnryption/decryption on .NET side and sending data between Windows Mobile client and PHP server using UrlEncode and replacing all '+'s in the UrlEncoded string with a custom string known from both the client and the server, I decided for "####".
Related
I'm trying to build a web service using Express/NodeJS which signs a piece of information. The signed data is received and verified by a client written in C#. You'll have to forgive my inexperience in cryptography and its associated technologies.
First off, I generate a certificate for the C# client and a private key for the NodeJS application using OpenSSL;
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days
365
In the NodeJS application, I have the following code;
const crypto = require('crypto')
const fs = require('fs')
var pem = fs.readFileSync('./keys/key.pem');
var key = pem.toString('ascii');
var privateKey = crypto.createPrivateKey({
'key': key,
'format': 'pem',
'passphrase': '<PASSPHRASE>',
});
function sign(identifier){
var sign = crypto.createSign('RSA-SHA256');
sign.update(identifier);
var sig = sign.sign(privateKey, 'base64');
return sig;
}
exports.sign = sign;
In this case, the parameter identifier is the data to be signed. The client will receive this, and the signature generated, sig.
In the C# client I have the following snippet;
X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(pub));
using (var sha256 = SHA256.Create())
{
using (var rsa = cert.GetRSAPublicKey())
{
bool results = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(results.ToString());
}
}
The pub is the generated certificate in Base64, it is stored in a const string. The data contains the same information as identifier in the NodeJS application, but it's converted to bytes using Convert.FromBase64String(...), and likewise the signature is the data returned from sig in the NodeJS application, only converted from Base64 to byte data.
When all information is inserted, VerifyData() returns false, this leads me to believe that there's some kind of missmatch between the cryptographic configurations of the web service and the client.
Any ideas?
As pointed out in the comments, the problem was that data in the C# client was converted to from Base64 when the data in the NodeJS application read from UTF-8.
The solution was to convert the string using Encoding.UTF8.GetBytes()
Thanks for the quick response!
I already have an asymmetric algorithm implemented in an MVC C# Application, however I would like to modify the encryption method so that I make use of both symmetric and asymmetric encryption (AKA Hybrid encryption). Any idea how I can do this?
Asymmetric encrypt:
public string AsymmEncrypt(int accId, string input, string publickey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
publickey = new UserRepository().PublicKeyByAccountId(accId);
cspParams.KeyContainerName = publickey;
myAlg = new RSACryptoServiceProvider(cspParams);
byte[] cipher = myAlg.Encrypt(UTF8Encoding.UTF8.GetBytes(input), true);
return Convert.ToBase64String(cipher);
}
Asymmetric decrypt:
public string AsymmDecrypt(int accId, string input, string privatekey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
privatekey = new UserRepository().PrivateKeyByAccountId(accId);
byte[] cipher = myAlg.Decrypt(Convert.FromBase64String(input), true);
return UTF8Encoding.UTF8.GetString(cipher);
}
You should probably not try to reinvent the wheel here. The System.Security.Cryptography namespace in .net alrady provides a large array of cryptography functionality that is quite well vetted. Don't try to use your own Asymmetric functions to accomplish this.
If you want to do private key distribution through public key encryption, you should use something like RSAPKCS1KeyExchangeFormatter or maybe even RSAOAEPKeyExchangeFormatter if you have the flexibility to support PKCS#1v2
I would suggest reading how SSL or OpenPGP are implemented.
I'm not sure what part you are struggling with.
In short, the asymmetric algorithm is used for symmetric key exchange.
The symmetric algorithm is used for the bulk data (stream/block) crypto. You won't get it done with simply modifying your 2 functions, you will need to implement a handshake and key exchange.
Since you have an MVC.NET app, you can host it within a web server and gain HTTPS/SSL transport. You can also do the same with WCF. Any reason why aren't using what is provided by the underlying transport? You can even configure your application (web.config) to require client certificates.
PS: I agree about not re-inventing the wheel, even Microsoft's article that Erik linked to provides a warning about it.
Caution We recommend that you do not attempt to create your own key exchange method from the basic functionality provided, because many details of the operation must be performed carefully in order for the key exchange to be successful.
I am new in Windows store apps development. I am creating a Windows Store App which requires to store some data on client side. This data is present in JSON format having Build Action as Content. Whenever user runs the application, I am initializing some objects by reading this JSON file. However this is just a plain text and contains data that should not be revealed to the user. Can I encrypt this JSON by any means. I need basically a workaround where I encrypt the json data while building the application and decrypt this json while reading and initializing the objects. Please suggest.
I'm assuming your code looks like this:
string json = File.ReadAllText("/<path to json>/config.json");
// Parse the json ...
You can encrypt the content of the JSON file using AES encryption.
You will need to define a key that will be used for encryption and decryption.
Take a look in here : using AES encrypt / decrypt in c#
After using encryption your code will look like this:
when you need to read your configuration:
string encryptedJson = File.ReadAllText("/<path to json>/config.json");
string aesKey = "<your aes key>";
string plainJson = AesDecrypt(encryptedJson, aesKey);
// Parse the json ...
When you need save the configuration:
// Generate json ...
string plainJson;
string aesKey = "<your aes key>";
string encryptedJson = AesEncrypt(plainJson, aesKey);
File.WriteAllText("/<path to json>/config.json", encryptedJson);
Note that your key can be extracted from your compiled assemblies using reflection methods
I want to implement a scenario where two endpoints can securely communicate with each other using public/private key encryption. The scenario is following:
For A to send a message to B:
A encrypts the message using A's private key.
A encrypts the message using B's public key.
A sends the message.
B receives the message.
B decrypts the message using A's public key.
B decrypts the message using B's private key.
B reads the message.
Here is what I have in C# using RSA encryption:
// Alice wants to send a message to Bob:
String plainText = "Hello, World!";
Byte[] plainData = Encoding.Default.GetBytes(plainText);
Byte[] cipherData = null;
RSACryptoServiceProvider alice = new RSACryptoServiceProvider();
RSACryptoServiceProvider bob = new RSACryptoServiceProvider();
var alicePrivateKey = alice.ExportParameters(true);
var alicePublicKey = alice.ExportParameters(false);
var bobPrivateKey = bob.ExportParameters(true);
var bobPublicKey = bob.ExportParameters(false);
RSACryptoServiceProvider messenger = new RSACryptoServiceProvider();
messenger.ImportParameters(alicePrivateKey);
cipherData = messenger.Encrypt(plainData, true);
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
messenger.ImportParameters(alicePublicKey);
cipherData = messenger.Decrypt(cipherData, true);
messenger.ImportParameters(bobPrivateKey);
cipherData = messenger.Decrypt(cipherData, true);
String result = Encoding.Default.GetString(alice.Decrypt(cipherData, true));
Clearly, there is something wrong with the following lines:
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
Which throws System.Security.Cryptography.CryptographyException with message { "Bad Length" }.
As I can see it is not able to encrypt the data using just the public part of bob's key.
Can someone throw some light on how to properly accomplish what I want to do in C#?
Two problems here:
A) Your protocol design is wrong. If you want to use RSA to exchange messages, the algorithm is this:
A encrypts message using B's public key
A sends the message
B decrypts the message using B's private key
(B does processing)
B encrypts message using A's public key
B sends the message
A decrypts the message using A's private key
and so on. Notice how A does not know B's private key, and vice versa. The public and private keys are related in such a way that a message encrypted with a public key (known to everyone) can only be decrypted with the corresponding private key (known only to the intendent recipient of the encrypted message). This is the whole point of RSA, actually.
As for implementation in C#, it is quite trivial to do with the Crypto classes once you really understand the underlying concepts. See for example here and here.
B) RSA is good for exchanging small amounts of data. It is meant for key exchange over an insecure channel without the need for a shared secret. For exchanging "normal" data, a symmetric algorithm such as AES is used. So the idea would be generating a random passphrase and IV from A, and sending that to B via RSA as discussed in A; after both parties know the passphrase and IV, they can just encrypt data using AES with the shared key.
This is what SSL does, and you should have a really good reason to roll your own instead of using a standard SSL stream.
RSA is used to encrypt data which are smaller than the key. You use symmetric key to encrypt large amount of data and then use the RSA to share the symmetric key.
For further details you might refer to this question : how to use RSA to encrypt files (huge data) in C#
My server creates a RSACryptoServiceProvider and exports its parameters to a variable (RSAKeyInfo).
Then, the public key is sent to the client, and the client encrypts something with that public key.
Now, I need to be able to decrypt this very data when sent back to the server - hence why RSA is useful in my case.
However, I get a "Bad Data" exception when trying to recreate a RSACryptoServiceProvider with imported parameters from the first RSACryptoServiceProvider created previously.
... Code might be clearer.
Creating the crypto:
class Cryptograph
{
public Cryptograph()
{
this.RSAKeyInfo = new RSACryptoServiceProvider(2048, new CspParameters(1)).ExportParameters(true);
}
}
Accessing it later for decryption:
byte[] encrypted = ...;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(this.Cryptograph.RSAKeyInfo);
byte[] decrypted = rsa.Decrypt(encrypted, false);
Console.WriteLine(Utilities.ByteArrayToHexString(decrypted));
I get the "Bad Data" exception at this line:
byte[] decrypted = rsa.Decrypt(encrypted, false);
What am I doing wrong? How can I do it properly?
Thank you :)
P.S.: Please don't send MSDN or obvious Google results links, I've read all these pages and still can't get it to work.
When something is encrypted with a public key, you need to use the private key for the decryption. I don't see where you are using the private key for decryption.
I realize you have already read this, but you may want to read the Encrypt page and this Decrypt page, and make certain that you are following the steps:
http://msdn.microsoft.com/en-us/library/te15te69.aspx
Unless you are encrypting very short messages, such as a password, RSA encryption should generally be used for encrypting a symmetric key, which is faster to encrypt/decrypt longer messages.
The size of what you can encrypt with a public key is tied to the length of the key.
I needed an encryption/decryption that used no padding, and C#.NET doesn't provide it by default. OpenSSL.NET will do the job, however, I'm stuck while trying to use it. (See this question if you want to help me make it work). :(