Encrypting and decrypting data using NHibernate - c#

I'm writing a publicly accessible web application which will contain personal user data, such as names and birth dates, and I'm required to encrypt this data in a form that will be difficult for a human who might access the raw data to decrypt. I'm using Fluent NHibernate, mySQL, and C# 3.5.
What method should I use for industry standard encryption and decryption of user information? The method of encryption should not be database dependent.
How do I tell nHibernate to do transparent encryption/decryption on certain mapped classes with a simple property, like StorageType = StorageType.Encrypted. I don't mind if the resulting database table has just one or two columns, or one for each encrypted field. From what I've found, I should create my own data type from IUserDataType and encrypt the data in the constructor. Is this correct?

In true Blue Peter fashion, here's one I created earlier to do just this. It relies on a provider pattern to get the encryption algorithm but you could replace this with whatever you want.
This exposes a string property in your domain object, but persists it as a binary (array of bytes) representing the encrypted form. In my provider pattern code, Encrypt takes a string and returns a byte array, and Decrypt does the opposite.
[Serializable]
public class EncryptedStringType : PrimitiveType
{
public EncryptedStringType() : this(new BinarySqlType()) {}
public EncryptedStringType(SqlType sqlType) : base(sqlType) {}
public override string Name
{
get { return "String"; }
}
public override Type ReturnedClass
{
get { return typeof (string); }
}
public override Type PrimitiveClass
{
get { return typeof (string); }
}
public override object DefaultValue
{
get { return null; }
}
public override void Set(IDbCommand cmd, object value, int index)
{
if (cmd == null) throw new ArgumentNullException("cmd");
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = null;
}
else
{
((IDataParameter)cmd.Parameters[index]).Value = EncryptionManager.Provider.Encrypt((string)value);
}
}
public override object Get(IDataReader rs, int index)
{
if (rs == null) throw new ArgumentNullException("rs");
var encrypted = rs[index] as byte[];
if (encrypted == null) return null;
return EncryptionManager.Provider.Decrypt(encrypted);
}
public override object Get(IDataReader rs, string name)
{
return Get(rs, rs.GetOrdinal(name));
}
public override object FromStringValue(string xml)
{
if (xml == null)
{
return null;
}
if (xml.Length % 2 != 0)
{
throw new ArgumentException(
"The string is not a valid xml representation of a binary content.",
"xml");
}
var bytes = new byte[xml.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
string hexStr = xml.Substring(i * 2, (i + 1) * 2);
bytes[i] = (byte)(byte.MinValue
+ byte.Parse(hexStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture));
}
return EncryptionManager.Provider.Decrypt(bytes);
}
public override string ObjectToSQLString(object value, Dialect dialect)
{
var bytes = value as byte[];
if (bytes == null)
{
return "NULL";
}
var builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
string hexStr = (bytes[i] - byte.MinValue).ToString("x", CultureInfo.InvariantCulture);
if (hexStr.Length == 1)
{
builder.Append('0');
}
builder.Append(hexStr);
}
return builder.ToString();
}
}

I would create an EncryptionService that encrypts strings using whatever Key you'd like. Then I would make 2 properties in your entity. One that NHibernate interacts with (Encrypted values) and another that you (or other developers) interact with that will automatically encrypt the values.
See: http://kockerbeck.blogspot.com/2009/08/fluent-nhibernate-encrypting-values.html
A sample EncryptionService, User entity and UserMap are below.
public class User
{
private readonly EncryptionService _encryptionService =
new EncryptionService();
public virtual int Id { get; set; }
public virtual DateTime? DateOfBirth
{
get
{
return _encryptionService.DecryptObject<DateTime?>(DateOfBirthEncrypted);
}
set
{
DateOfBirthEncrypted= _encryptionService.EncryptString(value.Value
.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
[Obsolete("Use the 'DateOfBirth' property -- this property is only to be used by NHibernate")]
public virtual string DateOfBirthEncrypted { get; set; }
}
public sealed class UserMap : ClassMap<User>
{
public UserMap()
{
WithTable("dbo.[User]");
Id(x => x.Id, "[ID]");
Map(x => x.DateOfBirthEncrypted, "DOB");
}
}
And the EncryptionService:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Services
{
public class EncryptionService : IEncryptionService
{
/// <summary>
/// Decrypts a string
/// </summary>
/// <param name="encryptedString"></param>
/// <returns></returns>
public String DecryptString(string encryptedString)
{
if (String.IsNullOrEmpty(encryptedString)) return String.Empty;
try
{
using (TripleDESCryptoServiceProvider cypher = new TripleDESCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("ENTERAKEYHERE", new byte[0]);
cypher.Key = pdb.GetBytes(16);
cypher.IV = pdb.GetBytes(8);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cypher.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] data = Convert.FromBase64String(encryptedString);
cs.Write(data, 0, data.Length);
cs.Close();
return Encoding.Unicode.GetString(ms.ToArray());
}
}
}
}
catch
{
return String.Empty;
}
}
/// <summary>
/// Encrypts a string
/// </summary>
/// <param name="decryptedString"
/// <returns></returns>
public String EncryptString(string decryptedString)
{
if (String.IsNullOrEmpty(decryptedString)) return String.Empty;
using (TripleDESCryptoServiceProvider cypher = new TripleDESCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes("ENTERAKEYHERE", new byte[0]);
cypher.Key = pdb.GetBytes(16);
cypher.IV = pdb.GetBytes(8);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cypher.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] data = Encoding.Unicode.GetBytes(decryptedString);
cs.Write(data, 0, data.Length);
cs.Close();
return Convert.ToBase64String(ms.ToArray());
}
}
}
}
/// <summary>
/// Decrypts a given value as type of T, if unsuccessful the defaultValue is used
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public T DecryptObject<T>(object value, T defaultValue)
{
if (value == null) return defaultValue;
try
{
Type conversionType = typeof(T);
// Some trickery for Nullable Types
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
conversionType = new NullableConverter(conversionType).UnderlyingType;
}
return (T)Convert.ChangeType(DecryptString(Convert.ToString(value)), conversionType);
}
catch
{
// Do nothing
}
return defaultValue;
}
}
}

Related

C# XML Serializer cannot deserialize Celsius Degree Sign

I have a bunch of xml files that I need to deserialize and store in a database.
I found a generic implementation of an XML deserilizer that does a great job. The only inconvenient is that I cannot deserialize the Celsius degree sign.
The following XML samples contain the Celsius degree sign, which I cannot deserialize:
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>°C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
or
<pt>
<ch>5</ch>
<type>Analog</type>
<chtype>Wind Direction</chtype>
<chunits>°</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
My Deserializer implementation is the following:
public class XmlConvert
{
public static string SerializeObject<T>(T dataObject)
{
if (dataObject == null)
{
return string.Empty;
}
try
{
using (StringWriter stringWriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringWriter, dataObject);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
return string.Empty;
}
}
public static T DeserializeObject<T>(string xml)
where T : new()
{
if (string.IsNullOrEmpty(xml))
{
return new T();
}
try
{
using (var stringReader = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
catch (Exception ex)
{
return new T();
}
}
}
The class I use to convert is the following:
/// <summary>
/// Container for a single data point.
/// </summary>
public class MessageDataPoint
{
/// <summary>
/// Channel number for data point.
/// </summary>
[System.Xml.Serialization.XmlElement("ch")]
public string Ch { get; set; }
/// <summary>
/// Point type (Analog, Flow)
/// </summary>
[System.Xml.Serialization.XmlElement("type")]
public string Type { get; set; }
/// <summary>
///
/// </summary>
[System.Xml.Serialization.XmlElement("chtype")]
public string ChType { get; set; }
/// <summary>
/// Channel units
/// </summary>
[System.Xml.Serialization.XmlElement("chunits")]
public string ChUnits { get; set; }
/// <summary>
/// Point timestamp.
/// </summary>
[System.Xml.Serialization.XmlElement("time")]
public string Time { get; set; }
[System.Xml.Serialization.XmlIgnore]
public DateTime? TimeParsed
{
get
{
DateTime.TryParse(Time, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt);
return dt;
}
private set { }
}
/// <summary>
/// Channel Value
/// </summary>
[System.Xml.Serialization.XmlElement("value")]
public string Value { get; set; }
}
The deserializer implementation is the following:
public async Task<string> GetFileAsStream(string key)
{
var content = string.Empty;
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = key
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
{
using (StreamReader reader = new StreamReader(response.ResponseStream))
{
content = await reader.ReadToEndAsync();
var logger = XmlConvert.DeserializeObject<Logger>(content);
// code removed for brevity
}
}
}
}
The result is the following:
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>�C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
<pt>
<ch>2</ch>
<type>Analog</type>
<chtype>Wind Chill</chtype>
<chunits>�C</chunits>
<time>2020-05-03 22:10:00</time>
<value>0</value>
</pt>
Any ideas?
This is a sample file:
<?xml version="1.0" encoding="ISO-8859-15"?>
<logger>
<id>111111</id>
<mobileNumber>12345676</mobileNumber>
<serialNumber>01404</serialNumber>
<siteName>abcdef</siteName>
<siteId>abcdef</siteId>
<maintenanceflag>False</maintenanceflag>
<messages>
<message>
<id>123456789</id>
<number>123456</number>
<dateReceived>2020-05-04 00:02:25.0</dateReceived>
<dateCredited>2020-05-04 00:02:25.0</dateCredited>
<message> <siteId>abcdef</siteId>
<type>data</type>
<RST>2020-01-18 08:50:00</RST>
<RTC>2020-05-03 23:02:24</RTC>
<DST>2020-05-03 22:00:00</DST>
<mode>0</mode>
<SR>600</SR>
<pt>
<ch>1</ch>
<type>Analog</type>
<chtype>Temperature</chtype>
<chunits>°C</chunits>
<time>2020-05-03 23:00:00</time>
<value>0</value>
</pt>
<pt>
<ch>5</ch>
<type>Analog</type>
<chtype>Wind Direction</chtype>
<chunits>°</chunits>
<time>2020-05-03 23:00:00</time>
<value>0</value>
</pt>
</message>
<format>BINARY</format>
<source>FTP</source>
<batteryCondition>123</batteryCondition>
<signalStrength>12</signalStrength>
</message>
</messages>
</logger>
Feed the stream to the serializer. This will greatly simplify the code.
Moreover, you can completely delete your XmlConvert class.
using (GetObjectResponse response = await client.GetObjectAsync(request))
using (var stream = response.ResponseStream)
{
var serializer = new XmlSerializer(typeof(Logger));
var logger = (Logger)serializer.DeserializeObject(stream);
}
But if you think that the XmlConvert class is necessary, for example, it does some extra work, then at least use Stream in it instead of string.
public static T DeserializeObject<T>(Stream stream)
where T : new()
{
try
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stream);
}
catch (Exception ex)
{
return new T();
}
}
I found the solution by looking at the following post on Stackoverflow
Using .NET how to convert ISO 8859-1 encoded text files that contain Latin-1 accented characters to UTF-8
Thanks to Nyerguds and Alexander Haas for pointing me in the right direction
The solution for me was
public async Task<string> GetFileAsStream(string key)
{
var content = string.Empty;
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = key
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
{
using (StreamReader reader = new StreamReader(response.ResponseStream, Encoding.GetEncoding("iso-8859-1")))
{
content = await reader.ReadToEndAsync();
var logger = XmlConvert.DeserializeObject<Logger>(content);
// code removed for brevity
}
}
}
}

How can I encrypt selected properties when serializing my objects?

I'm using JSON to store certain settings within my application. Some of the settings contain sensitive information (e.g. passwords) while other settings are not sensitive. Ideally I'd like to be able to serialize my objects where the sensitive properties are encrypted automatically while keeping the non-sensitive settings readable. Is there a way to do this using Json.Net? I did not see any setting related to encryption.
Json.Net does not have built-in encryption. If you want to be able to encrypt and decrypt during the serialization process, you will need to write some custom code. One approach is to use a custom IContractResolver in conjunction with an IValueProvider. The value provider gives you a hook where you can transform values within the serialization process, while the contract resolver gives you control over when and where the value provider gets applied. Together, they can give you the solution you are looking for.
Below is an example of the code you would need. First off, you'll notice I've defined a new [JsonEncrypt] attribute; this will be used to indicate which properties you want to be encrypted. The EncryptedStringPropertyResolver class extends the DefaultContractResolver provided by Json.Net. I've overridden the CreateProperties() method so that I can inspect the JsonProperty objects created by the base resolver and attach an instance of my custom EncryptedStringValueProvider to any string properties which have the [JsonEncrypt] attribute applied. The EncryptedStringValueProvider later handles the actual encryption/decryption of the target string properties via the respective GetValue() and SetValue() methods.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
[AttributeUsage(AttributeTargets.Property)]
public class JsonEncryptAttribute : Attribute
{
}
public class EncryptedStringPropertyResolver : DefaultContractResolver
{
private byte[] encryptionKeyBytes;
public EncryptedStringPropertyResolver(string encryptionKey)
{
if (encryptionKey == null)
throw new ArgumentNullException("encryptionKey");
// Hash the key to ensure it is exactly 256 bits long, as required by AES-256
using (SHA256Managed sha = new SHA256Managed())
{
this.encryptionKeyBytes =
sha.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey));
}
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
// Find all string properties that have a [JsonEncrypt] attribute applied
// and attach an EncryptedStringValueProvider instance to them
foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
{
PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
if (pi != null && pi.GetCustomAttribute(typeof(JsonEncryptAttribute), true) != null)
{
prop.ValueProvider =
new EncryptedStringValueProvider(pi, encryptionKeyBytes);
}
}
return props;
}
class EncryptedStringValueProvider : IValueProvider
{
PropertyInfo targetProperty;
private byte[] encryptionKey;
public EncryptedStringValueProvider(PropertyInfo targetProperty, byte[] encryptionKey)
{
this.targetProperty = targetProperty;
this.encryptionKey = encryptionKey;
}
// GetValue is called by Json.Net during serialization.
// The target parameter has the object from which to read the unencrypted string;
// the return value is an encrypted string that gets written to the JSON
public object GetValue(object target)
{
string value = (string)targetProperty.GetValue(target);
byte[] buffer = Encoding.UTF8.GetBytes(value);
using (MemoryStream inputStream = new MemoryStream(buffer, false))
using (MemoryStream outputStream = new MemoryStream())
using (AesManaged aes = new AesManaged { Key = encryptionKey })
{
byte[] iv = aes.IV; // first access generates a new IV
outputStream.Write(iv, 0, iv.Length);
outputStream.Flush();
ICryptoTransform encryptor = aes.CreateEncryptor(encryptionKey, iv);
using (CryptoStream cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write))
{
inputStream.CopyTo(cryptoStream);
}
return Convert.ToBase64String(outputStream.ToArray());
}
}
// SetValue gets called by Json.Net during deserialization.
// The value parameter has the encrypted value read from the JSON;
// target is the object on which to set the decrypted value.
public void SetValue(object target, object value)
{
byte[] buffer = Convert.FromBase64String((string)value);
using (MemoryStream inputStream = new MemoryStream(buffer, false))
using (MemoryStream outputStream = new MemoryStream())
using (AesManaged aes = new AesManaged { Key = encryptionKey })
{
byte[] iv = new byte[16];
int bytesRead = inputStream.Read(iv, 0, 16);
if (bytesRead < 16)
{
throw new CryptographicException("IV is missing or invalid.");
}
ICryptoTransform decryptor = aes.CreateDecryptor(encryptionKey, iv);
using (CryptoStream cryptoStream = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(outputStream);
}
string decryptedValue = Encoding.UTF8.GetString(outputStream.ToArray());
targetProperty.SetValue(target, decryptedValue);
}
}
}
}
Once you have the resolver in place, the next step is to apply the custom [JsonEncrypt] attribute to the string properties within your classes that you wish to be encrypted during serialization. For example, here is a contrived class that might represent a user:
public class UserInfo
{
public string UserName { get; set; }
[JsonEncrypt]
public string UserPassword { get; set; }
public string FavoriteColor { get; set; }
[JsonEncrypt]
public string CreditCardNumber { get; set; }
}
The last step is to inject the custom resolver into the serialization process. To do that, create a new JsonSerializerSettings instance, then set the ContractResolver property to a new instance of the custom resolver. Pass the settings to the JsonConvert.SerializeObject() or DeserializeObject() methods and everything should just work.
Here is a round-trip demo:
public class Program
{
public static void Main(string[] args)
{
try
{
UserInfo user = new UserInfo
{
UserName = "jschmoe",
UserPassword = "Hunter2",
FavoriteColor = "atomic tangerine",
CreditCardNumber = "1234567898765432",
};
// Note: in production code you should not hardcode the encryption
// key into the application-- instead, consider using the Data Protection
// API (DPAPI) to store the key. .Net provides access to this API via
// the ProtectedData class.
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new EncryptedStringPropertyResolver("My-Sup3r-Secr3t-Key");
Console.WriteLine("----- Serialize -----");
string json = JsonConvert.SerializeObject(user, settings);
Console.WriteLine(json);
Console.WriteLine();
Console.WriteLine("----- Deserialize -----");
UserInfo user2 = JsonConvert.DeserializeObject<UserInfo>(json, settings);
Console.WriteLine("UserName: " + user2.UserName);
Console.WriteLine("UserPassword: " + user2.UserPassword);
Console.WriteLine("FavoriteColor: " + user2.FavoriteColor);
Console.WriteLine("CreditCardNumber: " + user2.CreditCardNumber);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}
}
Output:
----- Serialize -----
{
"UserName": "jschmoe",
"UserPassword": "sK2RvqT6F61Oib1ZittGBlv8xgylMEHoZ+1TuOeYhXQ=",
"FavoriteColor": "atomic tangerine",
"CreditCardNumber": "qz44JVAoJEFsBIGntHuPIgF1sYJ0uyYSCKdYbMzrmfkGorxgZMx3Uiv+VNbIrbPR"
}
----- Deserialize -----
UserName: jschmoe
UserPassword: Hunter2
FavoriteColor: atomic tangerine
CreditCardNumber: 1234567898765432
Fiddle: https://dotnetfiddle.net/trsiQc
Though #Brian's solution is quite clever, I don't like the complexity of a custom ContractResolver. I converted Brian's code to a JsonConverter, so your code would become
public class UserInfo
{
public string UserName { get; set; }
[JsonConverter(typeof(EncryptingJsonConverter), "My-Sup3r-Secr3t-Key")]
public string UserPassword { get; set; }
public string FavoriteColor { get; set; }
[JsonConverter(typeof(EncryptingJsonConverter), "My-Sup3r-Secr3t-Key")]
public string CreditCardNumber { get; set; }
}
I've posted the (quite lengthy) EncryptingJsonConverter as a Gist and also blogged about it.
My solution:
public string PasswordEncrypted { get; set; }
[JsonIgnore]
public string Password
{
get
{
var encrypted = Convert.FromBase64String(PasswordEncrypted);
var data = ProtectedData.Unprotect(encrypted, AdditionalEntropy, DataProtectionScope.LocalMachine);
var res = Encoding.UTF8.GetString(data);
return res;
}
set
{
var data = Encoding.UTF8.GetBytes(value);
var encrypted = ProtectedData.Protect(data, AdditionalEntropy, DataProtectionScope.LocalMachine);
PasswordEncrypted = Convert.ToBase64String(encrypted);
}
(can be made less verbose)

Bouncy Castle Encryption

I have a requirment to send PGP Encrypted CSV Files to SFTP and i know nothing aboutr PGP. After a lot of research I could find following code on internet using 'Bouncy Castle' Library. The code seems gogod but does not work for me given my inexperience in PGP. Please help me in finding right values for:
publicKeyFileName
privateKeyFileName
pasPhrase
Below is my code:
namespace PgpEncryption
{
class Program
{
public static void Main()
{
EncryptAndSign();
}
private static void EncryptAndSign()
{
string publicKeyFileName = "";
string privateKeyFileName = "";
string pasPhrase = "";
PgpEncryptionKeys encryptionKeys
= new PgpEncryptionKeys(publicKeyFileName, privateKeyFileName, pasPhrase);
PgpEncrypt encrypter = new PgpEncrypt(encryptionKeys);
string encryptedFileName = "";
using (Stream outputStream = File.Create(encryptedFileName))
{
string fileToEncrypt = "";
encrypter.EncryptAndSign(outputStream, new FileInfo(fileToEncrypt));
}
}
}
}
public class PgpEncryptionKeys
{
public PgpEncryptionKeys(string publicKeyPath, string privateKeyPath, string passPhrase)
{
if (!File.Exists(publicKeyPath))
throw new ArgumentException("Public key file not found", "publicKeyPath");
if (!File.Exists(privateKeyPath))
throw new ArgumentException("Private key file not found", "privateKeyPath");
if (String.IsNullOrEmpty(passPhrase))
throw new ArgumentException("passPhrase is null or empty.", "passPhrase");
PublicKey = ReadPublicKey(publicKeyPath);
SecretKey = ReadSecretKey(privateKeyPath);
PrivateKey = ReadPrivateKey(passPhrase);
}
#region Secret Key
private PgpSecretKey ReadSecretKey(string privateKeyPath)
{
using (Stream keyIn = File.OpenRead(privateKeyPath))
using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn))
{
PgpSecretKeyRingBundle secretKeyRingBundle = new PgpSecretKeyRingBundle(inputStream);
PgpSecretKey foundKey = GetFirstSecretKey(secretKeyRingBundle);
if (foundKey != null)
return foundKey;
}
throw new ArgumentException("Can't find signing key in key ring.");
}
private PgpSecretKey GetFirstSecretKey(PgpSecretKeyRingBundle secretKeyRingBundle)
{
foreach (PgpSecretKeyRing kRing in secretKeyRingBundle.GetKeyRings())
{
PgpSecretKey key = (PgpSecretKey)kRing.GetSecretKeys();
if (key != null)
return key;
}
return null;
}
#endregion
#region Public Key
private PgpPublicKey ReadPublicKey(string publicKeyPath)
{
using (Stream keyIn = File.OpenRead(publicKeyPath))
using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn))
{
PgpPublicKeyRingBundle publicKeyRingBundle = new PgpPublicKeyRingBundle(inputStream);
PgpPublicKey foundKey = GetFirstPublicKey(publicKeyRingBundle);
if (foundKey != null)
return foundKey;
}
throw new ArgumentException("No encryption key found in public key ring.");
}
private PgpPublicKey GetFirstPublicKey(PgpPublicKeyRingBundle publicKeyRingBundle)
{
foreach (PgpPublicKeyRing kRing in publicKeyRingBundle.GetKeyRings())
{
PgpPublicKey key = (PgpPublicKey)kRing.GetPublicKeys();
//PgpPublicKey key = kRing.GetPublicKeys()
//.Cast<PgpPublicKey>()
// .Where(k => k.IsEncryptionKey)
// .FirstOrDefault();
if (key != null)
return key;
}
return null;
}
#endregion
#region Private Key
private PgpPrivateKey ReadPrivateKey(string passPhrase)
{
PgpPrivateKey privateKey = SecretKey.ExtractPrivateKey(passPhrase.ToCharArray());
if (privateKey != null)
return privateKey;
throw new ArgumentException("No private key found in secret key.");
}
#endregion
}
public class PgpEncrypt
{
private PgpEncryptionKeys m_encryptionKeys;
private const int BufferSize = 0x10000; // should always be power of 2
/// <summary>
/// Instantiate a new PgpEncrypt class with initialized PgpEncryptionKeys.
/// </summary>
/// <param name="encryptionKeys"></param>
/// <exception cref="ArgumentNullException">encryptionKeys is null</exception>
public PgpEncrypt(PgpEncryptionKeys encryptionKeys)
{
if (encryptionKeys == null)
throw new ArgumentNullException("encryptionKeys", "encryptionKeys is null.");
m_encryptionKeys = encryptionKeys;
}
/// <summary>
/// Encrypt and sign the file pointed to by unencryptedFileInfo and
/// write the encrypted content to outputStream.
/// </summary>
/// <param name="outputStream">The stream that will contain the
/// encrypted data when this method returns.</param>
/// <param name="fileName">FileInfo of the file to encrypt</param>
public void EncryptAndSign(Stream outputStream, FileInfo unencryptedFileInfo)
{
if (outputStream == null)
throw new ArgumentNullException("outputStream", "outputStream is null.");
if (unencryptedFileInfo == null)
throw new ArgumentNullException("unencryptedFileInfo", "unencryptedFileInfo is null.");
if (!File.Exists(unencryptedFileInfo.FullName))
throw new ArgumentException("File to encrypt not found.");
using (Stream encryptedOut = ChainEncryptedOut(outputStream))
using (Stream compressedOut = ChainCompressedOut(encryptedOut))
{
PgpSignatureGenerator signatureGenerator = InitSignatureGenerator compressedOut);
using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo))
using (FileStream inputFile = unencryptedFileInfo.OpenRead())
{
WriteOutputAndSign(compressedOut, literalOut, inputFile, signatureGenerator);
}
}
}
private static void WriteOutputAndSign(Stream compressedOut,Stream literalOut,FileStream inputFile,PgpSignatureGenerator signatureGenerator)
{
int length = 0;
byte[] buf = new byte[BufferSize];
while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
{
literalOut.Write(buf, 0, length);
signatureGenerator.Update(buf, 0, length);
}
signatureGenerator.Generate().Encode(compressedOut);
}
private Stream ChainEncryptedOut(Stream outputStream)
{
PgpEncryptedDataGenerator encryptedDataGenerator;
encryptedDataGenerator =
new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes,
new SecureRandom());
encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey);
return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]);
}
private static Stream ChainCompressedOut(Stream encryptedOut)
{
PgpCompressedDataGenerator compressedDataGenerator =
new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
return compressedDataGenerator.Open(encryptedOut);
}
private static Stream ChainLiteralOut(Stream compressedOut, FileInfo file)
{
PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator();
return pgpLiteralDataGenerator.Open(compressedOut, PgpLiteralData.Binary, file);
}
private PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut)
{
const bool IsCritical = false;
const bool IsNested = false;
PublicKeyAlgorithmTag tag = m_encryptionKeys.SecretKey.PublicKey.Algorithm;
PgpSignatureGenerator pgpSignatureGenerator =
new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);
pgpSignatureGenerator.InitSign(PgpSignature.BinaryDocument, m_encryptionKeys.PrivateKey);
foreach (string userId in m_encryptionKeys.SecretKey.PublicKey.GetUserIds())
{
PgpSignatureSubpacketGenerator subPacketGenerator =
new PgpSignatureSubpacketGenerator();
subPacketGenerator.SetSignerUserId(IsCritical, userId);
pgpSignatureGenerator.SetHashedSubpackets(subPacketGenerator.Generate());
// Just the first one!
break;
}
pgpSignatureGenerator.GenerateOnePassVersion(IsNested).Encode(compressedOut);
return pgpSignatureGenerator;
}
}
You should obtain public key of the recipient of the message, and generate your own secret key (if you need to sign files). Also PGP allows password-based encryption, if this suits your needs.
Also I would recommend to take a look on commercial libraries (like SecureBlackbox) as well. They are costy, but includes much better support, demos, documentation, etc.
i think you miss some of the code?
public class PgpEncryptionKeys {
public PgpPublicKey PublicKey { get; private set; }
public PgpPrivateKey PrivateKey { get; private set; }
public PgpSecretKey SecretKey { get; private set; }

How to encrypt a file using PGP Hex ID and PGP Name instead of a public key file in C#

I am currently working on a application which will create a text file, encrypt the file using PGP public key(which is not a file but a hexadecimal number and a pgp name) and post it to an FTP on schedule. The person whom I post the information to needs the file to be PGP encrypted and has shared the public key(which is not a file but a hexadecimal number and a pgp name).
The place where I am stuck is the PGP encryption part. I see examples on how to encrypt a text file using a public key file or public key ring(which I understand is a set of keys) using bouncy castle library. But the information I have is a 32 bit hexa decimal key and a PGP name. I am not sure how exactly to encrypt the file using this information. Should I request the other party to send me the public key file? Is it possible to create the public key file from the information I have? I am pretty new to PGP encryption and am posting this after a lot of googling which didnt provide any information, so please help.
This is the code I have so far to encrypt file. As you can see the code takes the file as an input for the public key not a hexa decimal code and name. My question really is, is it even possible to do a pgp encryption using a pgp name and a hexa decimal number?
public class PGPHelper
{
private PgpEncryptionKeys m_encryptionKeys;
private const int BufferSize = 0x10000;
/// <summary>
/// Instantiate a new PgpEncrypt class with initialized PgpEncryptionKeys.
/// </summary>
/// <param name="encryptionKeys"></param>
/// <exception cref="ArgumentNullException">encryptionKeys is null</exception>
public PGPHelper(PgpEncryptionKeys encryptionKeys)
{
if (encryptionKeys == null)
{
throw new ArgumentNullException("encryptionKeys", "encryptionKeys is null.");
}
m_encryptionKeys = encryptionKeys;
}
/// <summary>
/// Encrypt and sign the file pointed to by unencryptedFileInfo and
/// write the encrypted content to outputStream.
/// </summary>
/// <param name="outputStream">The stream that will contain the
/// encrypted data when this method returns.</param>
/// <param name="fileName">FileInfo of the file to encrypt</param>
public void Encrypt(Stream outputStream, FileInfo unencryptedFileInfo)
{
if (outputStream == null)
{
throw new ArgumentNullException("outputStream", "outputStream is null.");
}
if (unencryptedFileInfo == null)
{
throw new ArgumentNullException("unencryptedFileInfo", "unencryptedFileInfo is null.");
}
if (!File.Exists(unencryptedFileInfo.FullName))
{
throw new ArgumentException("File to encrypt not found.");
}
using (Stream encryptedOut = ChainEncryptedOut(outputStream))
{
using (Stream literalOut = ChainLiteralOut(encryptedOut, unencryptedFileInfo))
using (FileStream inputFile = unencryptedFileInfo.OpenRead())
{
WriteOutput(literalOut, inputFile);
}
}
}
private static void WriteOutput(Stream literalOut,
FileStream inputFile)
{
int length = 0;
byte[] buf = new byte[BufferSize];
while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
{
literalOut.Write(buf, 0, length);
}
}
private Stream ChainEncryptedOut(Stream outputStream)
{
PgpEncryptedDataGenerator encryptedDataGenerator;
encryptedDataGenerator =
new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, new SecureRandom());
encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey);
return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]);
}
private static Stream ChainLiteralOut(Stream encryptedOut, FileInfo file)
{
PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator();
return pgpLiteralDataGenerator.Open(encryptedOut, PgpLiteralData.Binary, file);
}
}
public class PgpEncryptionKeys
{
public PgpPublicKey PublicKey { get; private set; }
/// <summary>
/// Initializes a new instance of the EncryptionKeys class.
/// Two keys are required to encrypt and sign data. Your private key and the recipients public key.
/// The data is encrypted with the recipients public key and signed with your private key.
/// </summary>
/// <param name="publicKeyPath">The key used to encrypt the data</param>
/// <exception cref="ArgumentException">Public key not found. Private key not found. Missing password</exception>
public PgpEncryptionKeys(string publicKeyPath, string privateKeyPath, string passPhrase)
{
if (!File.Exists(publicKeyPath))
{
throw new ArgumentException("Public key file not found", "publicKeyPath");
}
PublicKey = ReadPublicKey(publicKeyPath);
}
#region Public Key
private PgpPublicKey ReadPublicKey(string publicKeyPath)
{
using (Stream keyIn = File.OpenRead(publicKeyPath))
using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn))
{
PgpPublicKeyRingBundle publicKeyRingBundle = new PgpPublicKeyRingBundle(inputStream);
PgpPublicKey foundKey = GetFirstPublicKey(publicKeyRingBundle);
if (foundKey != null)
{
return foundKey;
}
}
throw new ArgumentException("No encryption key found in public key ring.");
}
private PgpPublicKey GetFirstPublicKey(PgpPublicKeyRingBundle publicKeyRingBundle)
{
foreach (PgpPublicKeyRing kRing in publicKeyRingBundle.GetKeyRings())
{
PgpPublicKey key = kRing.GetPublicKeys()
.Cast<PgpPublicKey>()
.Where(k => k.IsEncryptionKey)
.FirstOrDefault();
if (key != null)
{
return key;
}
}
return null;
}
#endregion

Transmitting complex objects using TCP

I have a client server application in which I need to transmit a user defined object from Client to Server using TCP connection. My object is of the following structure:
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Please help me how to transmit this object at from client and again de-serialize it into appropriate object at server side.
As answered, you should make your object serializable. Once you did that with the Serializable attribute, you can use the famous BinaryFormatter to convert your object into a byte array.
You can find many examples out there for using the BinaryFormatter, just use your favorite search engine. Here's a short example:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
As for your class, it includes two private fields. I can't see where you set values for them, so I changed your code a bit, so that they can be set in the constructor. In addition, I added the needed Serializable attribute:
using System;
using System.Collections;
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Now let's put it all together, and see your class serialized and then deserialized, in a Console Application:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Capishi
{
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
class Program
{
static void Main(string[] args)
{
// create and initialize a conversation object
var convName = "Capishi";
var convOwner = "Ice Cream";
Conversation myConversation = new Conversation(convName, convOwner);
myConversation.convUsers = new ArrayList();
myConversation.convUsers.Add("Ron Klein");
myConversation.convUsers.Add("Rakesh K");
// serialize to a byte array
byte[] data = SerializationUtils.SerializeToByteArray(myConversation);
// print the resulting byte array if you want
// PrintArray(data);
// deserialize the object (on the other side of the communication
Conversation otherConversation = SerializationUtils.DeserializeFromByteArray<Conversation>(data);
// let's see if all of the members are really there
Console.WriteLine("*** start output ***");
Console.WriteLine("otherConversation.getConvName() = " + otherConversation.getConvName());
Console.WriteLine("otherConversation.getConvOwner() = " + otherConversation.getConvOwner());
Console.WriteLine("otherConversation.convUsers:");
foreach (object item in otherConversation.convUsers)
{
Console.WriteLine(item);
}
Console.WriteLine("*** done output ***");
// wait before close
Console.ReadLine();
}
/// <summary>
/// just a helper function to dump an array to the console's output
/// </summary>
/// <param name="data"></param>
private static void PrintArray(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
Console.Write("{0:000}", data[i]);
if (i < data.Length - 1)
Console.Write(", ");
}
Console.WriteLine();
}
}
}
The result is:
*** start output ***
otherConversation.getConvName() = Capishi
otherConversation.getConvOwner() = Ice Cream
otherConversation.convUsers:
Ron Klein
Rakesh K
*** done output ***
And a final note:
I'd use the generic List instead of the outdated ArrayList, unless you're bound to .NET 1.*.
One good course of action is to expose this object as a DataContract to a framework like WCF, and use the appropriate transports available in that framework.
For example:
[DataContract]
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
[DataMember]
public string ConvName
{
get { return this.convName; }
}
[DataMember]
public string ConvOwner
{
get { return this.convOwner; }
}
}
You need to make your object serializable.

Categories

Resources