I'm trying to encrypt a class before serializing it. The only way I found is to encrypt it and return a String or a stream, is it possible to return the original class ?
public static Options Encrypt(Options Settings)
{
Options sk = null;
try
{
using (var stream = new MemoryStream())
{
RuntimeTypeModel.Default.Serialize(stream, Settings);
byte[] data = encryptWithPadding(stream.ToArray(), 0);
String base64EncryptedString = Convert.ToBase64String(data);
// needs to return an option instance
}
}
catch (Exception e)
{
Global.LogError("Serialization failed", e);
}
return sk;
}
You can't "encrypt" a protobuf without serializing it. Encryption generally works on bytes, not data structures. If you want to encrypt it, and then embed the encrypted version as a field in some other protobuf, make that other field have type bytes instead of Options.
(In theory you could encrypt each of the fields of Options individually, but this would not help you much and would very likely be less secure than encrypting the whole serialized blob.)
Related
I am using ASP.NET Core 1.1 and I need to create an unique token with data.
So each token will be composed by: UniqueID + Data1 + Data2 + ... + DataN.
The UniqueId is a Guid and the Data objects can be types like Int32, String, etc:
DateTime expires = DateTime.Now.AddHours(24);
Int32 userId = user.Id;
Boolean enable = true;
And the method might be something like this:
public String GenerateToken(Guid id, params[] Object data) {
Byte[] idBin = id.ToByteArray();
// 1. Convert each object to Byte array
// 2. Concat all byte arrays into tokenData string
String token = Convert.ToBase64String(tokenData.ToArray());
// 3. Encrypt token
return encryptedToken;
}
So the main problems I have are:
Convert each object to Byte array
I know how to convert a specific type but not an Object.
Concat all byte arrays into tokenData string
Encrypt token
Is this the best way to create a token? The token will be sent in a URL.
And how can I solve problems 1 to 3?
Keeping your original method I made this:
public String GenerateToken(Guid id, params object[] allData)
{
byte[] idBin = id.ToByteArray();
byte[] total = new byte[] { };
total.Concat(idBin);
foreach (var data in allData)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, data);
total.Concat(ms.ToArray());
}
}
String token = Convert.ToBase64String(total);
return token;
}
This turns all your extra parameters into 1 concatenated byte array.
The only thing I have excluded is the encryption as there are already a million examples out there: How to Encrypt and Decrypt (hint read past the 1st answer)
I guess that you are using encryption so the user is not able to see or modify data inside this token.
You want to serialize an object to a byte array. The best way to do it is to keep your object typed instead of using an object array. Then use a serialization library like BinaryFormatter or protobuf (better). C# and .NET: How to serialize a structure into a byte[] array, using BinaryWriter?
It will be done automatically using a typed token content and serialization library.
To protect your token, you must use MachineKey.Protect.
Sample :
public String GenerateToken(TokenContent data)
{
byte[] data;
using(var ms = new MemoryStream())
{
Serializer.Serialize(ms, cust);
data = ms.ToArray();
}
var encryptedData = MachineKey.Protect(data, "TokenDataUrl");
var token = Convert.ToBase64String(encryptedData);
return token;
}
public TokenContent ReadToken(string token)
{
byte[] encryptedData = Convert.FromBase64String(token);
var data = MachineKey.Unprotect(encryptedData , "TokenDataUrl");
TokenContent content;
using(var ms = new MemoryStream(data))
{
content = Serializer.Deserialize<TokenContent>(ms);
}
return content;
}
I'd definitely consider using something like Json.Net. This will keep all your serialised data nice and cross-platform. Also, since you mentioned you're using Asp.Net Core, BinaryFormatter isn't available to you if you want to use the cross-platform .Net Standard libraries.
To take your example, you might do something similar to this:
public static string GenerateToken(Guid id, params object[] data)
{
var claims = new List<object>(data);
claims.Add(new
{
id = id
});
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
So you can call the method with something like:
GenerateToken(Guid.NewGuid(), "hello world!", 25, new { Test = "value" });
Giving you the following:
["hello world!",25,{"Test":"value"},{"id":"bf9e5d38-5ac4-4c6b-b68f-88136fc233cf"}]
You could just encrypt this string and pass it to your API, decrypt it and then deserialise it to an object:
public static object DeserialiseToken(string token)
{
object deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject(token);
return deserialised;
}
That will return you an object with all your original data in.
Notice that because we're using params object[] for our arguments, we can't create key-value pairs easily. We lose a variable's original name when we pass it into the method, and we haven't really got a good way of knowing what each entry in the data array should be called. For example, accessing the 'hello world!' string could be tedious.
We might run into difficulties interpreting the data properly when we want to read it later on.
Improving It!
Having said all that, I think we can improve the approach a little bit.
The first thing I'd do is introduce a proper model for your claims. If you can guarantee your tokens will all have the same 'model' for the data they contain, you can have a class such as:
public class Token
{
public Guid Id { get; set; }
public int UserId { get; set; }
public bool Enable { get; set; }
}
And pass that directly into Json.Net (or use some other serialiser):
string output = GenerateToken(new Token
{
Id = Guid.NewGuid(),
Enable = false,
UserId = 2062
});
and
public static string GenerateToken(Token claims)
{
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
When you get back around to deserialising the json, you can map it straight to an object:
public static Token DeserialiseToken(string token)
{
Token deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(token);
return deserialised;
}
You'll have a strongly-typed object with all your claims mapped against them.
You should also think about whether actually need to encrypt your token. One popular approach is the JSON Web Token (JWT) standard, where the set of claims are plaintext, but sent along with a verification hash, where the claims are hashed together with a secret.
In a situation where a user modifies the claims, when the token reaches your API it will rehash the claims with the secret, and the signatures won't match, so you'll know it's been tampered with!
If you're not storing anything particularly sensitive in your token then this is a perfectly good approach.
Just a thought, but if you want to serialise (and deserialise) objects (any C# .net object) without having to write a serialisation routing for them, and assuming that Json is OK as an output format, you can use NewtonSoft Json.Net:
http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_SerializeObject.htm
var output = JsonConvert.SerializeObject(object);
if you wanted then to make a unique hash for this object, you could use SHA256 like here: Hashing a string with Sha256
So, what I need is next:
Create certifiactes for development, get one for the client and one for server
Retrieve password through API that is encoded from client and decode it on server
Now, I managed to create certifiactes following this link. The girl there gave step by step instructions on how to get self signed certifiactes, put them in store, etc... Now, the part I'm having problem with:
I've managed to encrypt my data using this code:
public static string Encrypt(string stringForEncription, string PathToPrivateKey)
{
X509Certificate2 myCertificate;
try
{
myCertificate = new X509Certificate2(PathToPrivateKey, "Test123");
}
catch (Exception e)
{
throw new CryptographicException("Unable to open key file.");
}
RSACryptoServiceProvider rsaObj;
if (myCertificate.HasPrivateKey)
{
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
}
else
throw new CryptographicException("Private key not contained within certificate.");
if (rsaObj == null)
return String.Empty;
byte[] decryptedBytes;
byte[] array = Encoding.UTF8.GetBytes(stringForEncription);
try
{
decryptedBytes = rsaObj.Encrypt(array, false);
//decryptedBytes = rsaObj.Encrypt(Convert.FromBase64String(Base64EncryptedData), false);
}
catch (Exception e)
{
throw new CryptographicException("Unable to encrypt data.");
}
// Check to make sure we decrpyted the string
if (decryptedBytes.Length == 0)
return String.Empty;
else
return System.Text.Encoding.UTF8.GetString(decryptedBytes);
}
For PathToPrivate key variable I am using the path to my client ClientCert.pfx. I don't know if I should use any other, but here is the snap of the folder with all the certificates that I made:
Now, for the decryption, I'm using next code:
public static string DecryptEncryptedData(string Base64EncryptedData, string PathToPrivateKey)
{
X509Certificate2 myCertificate;
try
{
myCertificate = new X509Certificate2(PathToPrivateKey, "Test123");
}
catch (Exception e)
{
throw new CryptographicException("Unable to open key file.");
}
RSACryptoServiceProvider rsaObj;
if (myCertificate.HasPrivateKey)
{
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
}
else
throw new CryptographicException("Private key not contained within certificate.");
if (rsaObj == null)
return String.Empty;
byte[] decryptedBytes;
try
{
decryptedBytes = rsaObj.Decrypt(Convert.FromBase64String(Base64EncryptedData), false);
}
catch (Exception e)
{
throw new CryptographicException("Unable to decrypt data.");
}
// Check to make sure we decrpyted the string
if (decryptedBytes.Length == 0)
return String.Empty;
else
return System.Text.Encoding.UTF8.GetString(decryptedBytes);
}
And whatever I try to do, it gives me exception:
{"The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. "}
Would really apreciate somebody helping me out.
The reason you are getting the error is the string you are trying to Convert.FromBase64String from a value that is not actually a base-64 string.
After encrypting your data, you should convert the byte array to a base-64 string.
Use Convert.ToBase64String for this.
return Convert.ToBase64String(decryptedBytes);
Then your decrypt line will work:
decryptedBytes =
rsaObj.Decrypt(Convert.FromBase64String(Base64EncryptedData), false);
This is not the exact answer which you might expected but I write here as it's too long as a comment.
I think the decryption itself has no problem at all (I've found the example blog of your code with php encryption) That's why I commented I was curious on the encryptedstring which is the target of decryption.
I also struggled in understanding Security for months and now I use symmetric(AES) and asymmetric(RSA) together. Understanding is really important and everybody takes time..
RSA is asymmetric and one-way which means the Encryption can be done only by public key and the Decryption can be done only by private key.
You're using private key in Encryption method and it seems just copied from Decryption.
The answer by Zesty is right only in terms of formatting. You're also needed to understand the formatting. We need Convert.ToBase64String and Convert.FromBase64String in Encryption and Decryption from byte to base64string and vice versa. However this base64string is not just plain like 'hello' but
'SABlAGwAbABvACAAVwBvAHIAbABkAA==' as you see here
And I kindly recommend to use complete solution(not half one like php encryption) like this blog so that Encryption and Decryption and all are in harmony.
And as last as I commented also, you're needed to think about how to prevent the black users if encryption is done from client side and you don't have only good users.
I hope my experience helps to understand Security which is of most importance.
I made a routine to sign some proprietary binary files with PKCS#7 X509 certificates. The routine is functioning like a charm:
public static byte[] SignFile(X509Certificate2Collection certs, byte[] data, bool Tipo_A3 = false)
{
try
{
ContentInfo content = new ContentInfo(data);
SignedCms signedCms = new SignedCms(content, false);
if (VerifySign(data))
{
signedCms.Decode(data);
}
foreach (X509Certificate2 cert in certs)
{
CmsSigner signer = new CmsSigner( cert);
signer.IncludeOption = X509IncludeOption.WholeChain;
signer.SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber;
signer.SignedAttributes.Add(new Pkcs9SigningTime(System.DateTime.Now));
if (Type_A3 == true)
{
signedCms.ComputeSignature(signer, false);
}
else
{
signedCms.ComputeSignature(signer);
}
}
return signedCms.Encode();
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
return null;
}
}
My problem is related to RECOVER the original info. An 1Kb file will be transformed in a ~8Kb file, since the signature in within this file.
I need to read the data without the signature/certificate within the file, I mean, I need to recover the original data before its signing - and I don't know how to do it.
I saw that the signed file has bytes BEFORE and AFTER its original contents (I made a test using a tiny TXT file with "abcd"), but I'm affraid to consider ever the same data length before and after the original data to extract it.
I know I get the original content using this function, where DATA is the signed file:
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.IO;
using System.Windows.Forms;
public static Int VerifyContentInfo(byte[] data)
{
try
{
SignedCms signed = new SignedCms();
signed.Decode(data);
signed.CheckSignature(true);
return signed.ContentInfo.Content.Length
}
catch
{
return null;
}
}
The problem: even knowing the length of the original data within the signed file, how securely locate and extract it using a .NET function?
Thanks for any help!
signed.ContentInfo.Content (whose value you took the length of) is the original content.
I have a server which communicates with Clients using a TCP Sockets .
I want to send an object from one of the client (Sender) to server which sends this object to another client (Receiver )
the object contains fields of different types like this
Class Test {
public string key;
public int id;
public string message;
public Test ()
{
// constructor code
}
}
my question is how to convert the object to array of bytes, and when receive this array of bytes in Receiver how to do the opposite operation (convert from array of bytes to objects)?
You need to serialize your object. There are plenty of ways of doing that in C#.
You can serialize your object to binary bytes, XML or custom forms. If you want binary bytes (apparently that's what you're looking for) you can use BinaryFormatter class.
From the MSDN example:
Test test = new Test();
FileStream fs = new FileStream("output", FileMode.Create);
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, test);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
Of course, instead of a FileStream object you will use a socket output stream to send data.
If you are considering multiple platforms, I would suggest you use an XML based serialization so you won't run into issues related to platform endianness (byte order).
I need two methods one to encrypt and one to decrypt an xml file with a key= "hello world",the key hello world should be used to encrypt and decrypt the xml file.These methods should work on all machines!!! Any encryption methods will do. XML File contents below:
<root>
<lic>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</lic>
</root>
Can some give me a sample?The issue is the msdn sample encyptions make a xml file encypted but when I decrypt on another machine it doesn't work.For example
I tried this sample:
How to: Encrypt XML Elements with Asymmetric Keys,
but here there is some kinda session and on another machine it says bad data phewf!
If you want the same key for encrypting and decrypting you should use a symmetric method (that's the definition, really). Here's the closest one to your sample (same source).
http://msdn.microsoft.com/en-us/library/sb7w85t6.aspx
The posted sample isn't working because they aren't using the same keys. Not only on different machines: running the program on the same machine twice should not work either (didn't work for me), because they use different random keys every time.
try adding this code after creating your key:
key = new RijndaelManaged();
string password = "Password1234"; //password here
byte[] saltBytes = Encoding.UTF8.GetBytes("Salt"); // salt here (another string)
var p = new Rfc2898DeriveBytes(password, saltBytes); //TODO: think about number of iterations (third parameter)
// sizes are devided by 8 because [ 1 byte = 8 bits ]
key.IV = p.GetBytes(key.BlockSize / 8);
key.Key = p.GetBytes(key.KeySize / 8);
Now the program is using the same key and initial vector, and Encrypt and Decrypt should work on all machines.
Also, consider renaming key to algorithm, otherwise this is very misleading. I'd say it's a bad, not-working-well example from MSDN.
NOTE: PasswordDeriveBytes.GetBytes() has been deprecated because of serious (security) issues within the PasswordDeriveBytes class. The code above has been rewritten to use the safer Rfc2898DeriveBytes class instead (PBKDF2 instead of PBKDF1). Code generated with the above using PasswordDeriveBytes may be compromised.
See also: Recommended # of iterations when using PKBDF2-SHA256?
First of all, if you want to use the same key for encrypting and decrypting, you should look at symmetric cryptography. Asymmetric cryptography is when the keys for encrypting and decrypting are different. Just so that you know - RSA is asymmetric, TripleDES and Rijndael are symmetric. There are others too, but .NET does not have default implementations for them.
I'd advise studying the System.Security.Cryptography namespace. And learning a bit about all that stuff. It has all you need to encrypt and decrypt files, as well as generate a password. In particular, you might be interested in these classes:
CryptoStream
PasswordDeriveBytes
RijndaelManaged
There are also examples for usage in MSDN for each of them. You can use these classes to encrypt any file, not just XML. If however you want to encrypt just a select few elements, you can take a look at System.Security.Cryptography.Xml namespace. I see you've already found one article about it. Keep following the links on that page and you will learn more about those classes.
Would be cooler if you used a private key to sign the <lic> element and added the result to the file (in a <hash> element perhaps). This would make it possibly for everyone to read the xml file in case your support needs to know the license number, or the date of expiry, but they can not change any values without the private key.
The public key needed to verify the signature would be common knowledge.
Clarification
Signing your code will only protect it against changes, it will not keep any information in it hidden. Your original question mentions encryption, but I am not sure that it is a requirement to hide the data, or just protect it from modification.
Example code: (Never publish PrivateKey.key. ServerMethods are only needed when signing the xml file, ClientMethods are only needed when verifying the xml file.)
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
public static class Program {
public static void Main() {
if (!File.Exists("PublicKey.key")) {
// Assume first run, generate keys and sign document.
ServerMethods.GenerateKeyPair();
var input = new XmlDocument();
input.Load("input.xml");
Debug.Assert(input.DocumentElement != null);
var licNode = input.DocumentElement["lic"];
Debug.Assert(licNode != null);
var licNodeXml = licNode.OuterXml;
var signedNode = input.CreateElement("signature");
signedNode.InnerText = ServerMethods.CalculateSignature(licNodeXml);
input.DocumentElement.AppendChild(signedNode);
input.Save("output.xml");
}
if (ClientMethods.IsValidLicense("output.xml")) {
Console.WriteLine("VALID");
} else {
Console.WriteLine("INVALID");
}
}
public static class ServerMethods {
public static void GenerateKeyPair() {
var rsa = SharedInformation.CryptoProvider;
using (var keyWriter = File.CreateText("PublicKey.key"))
keyWriter.Write(rsa.ToXmlString(false));
using (var keyWriter = File.CreateText("PrivateKey.key"))
keyWriter.Write(rsa.ToXmlString(true));
}
public static string CalculateSignature(string data) {
var rsa = SharedInformation.CryptoProvider;
rsa.FromXmlString(File.ReadAllText("PrivateKey.key"));
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = rsa.SignData(dataBytes, SharedInformation.HashAlgorithm);
return Convert.ToBase64String(signatureBytes);
}
}
public static class ClientMethods {
public static bool IsValid(string data, string signature) {
var rsa = SharedInformation.CryptoProvider;
rsa.FromXmlString(File.ReadAllText("PublicKey.key"));
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = Convert.FromBase64String(signature);
return rsa.VerifyData(dataBytes, SharedInformation.HashAlgorithm, signatureBytes);
}
public static bool IsValidLicense(string filename) {
var doc = new XmlDocument();
doc.Load(filename);
var licNode = doc.SelectSingleNode("/root/lic") as XmlElement;
var signatureNode = doc.SelectSingleNode("/root/signature") as XmlElement;
if (licNode == null || signatureNode == null) return false;
return IsValid(licNode.OuterXml, signatureNode.InnerText);
}
}
public static class SharedInformation {
public static int KeySize {
get { return 1024; }
}
public static string HashAlgorithm {
get { return "SHA512"; }
}
public static RSACryptoServiceProvider CryptoProvider {
get { return new RSACryptoServiceProvider(KeySize, new CspParameters()); }
}
}
}
this is how you digitally sign and verify XML documents Sign XML Documents