We are trying to implement PGP encryption into one of our .net apps for the first time at the request of a vendor that we work with. Most of the examples I have found reference using Bouncy Castle so I have attempted to implement that. Everything seems to work fine and I end up with a .pgp file that looks correct. When I send it to the vendor, they say that it appears to be double-encrypted. They said they had to decrypt it twice to read it. Anyone have any ideas what may be going on here? I'll include the sample code that I am using below.
public class Pgp
{
public static void DecryptFile(
string inputFileName,
string keyFileName,
char[] passwd,
string defaultFileName)
{
using (Stream input = File.OpenRead(inputFileName),
keyIn = File.OpenRead(keyFileName))
{
DecryptFile(input, keyIn, passwd, defaultFileName);
}
}
/**
* decrypt the passed in message stream
*/
private static void DecryptFile(
Stream inputStream,
Stream keyIn,
char[] passwd,
string defaultFileName)
{
inputStream = PgpUtilities.GetDecoderStream(inputStream);
try
{
PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
PgpEncryptedDataList enc;
PgpObject o = pgpF.NextPgpObject();
//
// the first object might be a PGP marker packet.
//
if (o is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)o;
}
else
{
enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
}
//
// find the secret key
//
PgpPrivateKey sKey = null;
PgpPublicKeyEncryptedData pbe = null;
PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
PgpUtilities.GetDecoderStream(keyIn));
foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
{
sKey = PgpExampleUtilities.FindSecretKey(pgpSec, pked.KeyId, passwd);
if (sKey != null)
{
pbe = pked;
break;
}
}
if (sKey == null)
{
throw new ArgumentException("secret key for message not found.");
}
Stream clear = pbe.GetDataStream(sKey);
PgpObjectFactory plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());
message = pgpFact.NextPgpObject();
}
if (message is PgpLiteralData)
{
PgpLiteralData ld = (PgpLiteralData)message;
string outFileName = ld.FileName;
if (outFileName.Length == 0)
{
outFileName = defaultFileName;
}
Stream fOut = File.Create(outFileName);
Stream unc = ld.GetInputStream();
Streams.PipeAll(unc, fOut);
fOut.Close();
}
else if (message is PgpOnePassSignatureList)
{
throw new PgpException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PgpException("message is not a simple encrypted file - type unknown.");
}
if (pbe.IsIntegrityProtected())
{
if (!pbe.Verify())
{
Console.Error.WriteLine("message failed integrity check");
}
else
{
Console.Error.WriteLine("message integrity check passed");
}
}
else
{
Console.Error.WriteLine("no message integrity check");
}
}
catch (PgpException e)
{
Console.Error.WriteLine(e);
Exception underlyingException = e.InnerException;
if (underlyingException != null)
{
Console.Error.WriteLine(underlyingException.Message);
Console.Error.WriteLine(underlyingException.StackTrace);
}
}
}
public static void EncryptFile(
string outputFileName,
string inputFileName,
string encKeyFileName,
bool armor,
bool withIntegrityCheck)
{
PgpPublicKey encKey = PgpExampleUtilities.ReadPublicKey(encKeyFileName);
using (Stream output = File.Create(outputFileName))
{
EncryptFile(output, inputFileName, encKey, armor, withIntegrityCheck);
}
}
private static void EncryptFile(
Stream outputStream,
string fileName,
PgpPublicKey encKey,
bool armor,
bool withIntegrityCheck)
{
if (armor)
{
outputStream = new ArmoredOutputStream(outputStream);
}
try
{
byte[] bytes = PgpExampleUtilities.CompressFile(fileName, CompressionAlgorithmTag.Zip);
PgpEncryptedDataGenerator encGen = new PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
encGen.AddMethod(encKey);
Stream cOut = encGen.Open(outputStream, bytes.Length);
cOut.Write(bytes, 0, bytes.Length);
cOut.Close();
if (armor)
{
outputStream.Close();
}
}
catch (PgpException e)
{
Console.Error.WriteLine(e);
Exception underlyingException = e.InnerException;
if (underlyingException != null)
{
Console.Error.WriteLine(underlyingException.Message);
Console.Error.WriteLine(underlyingException.StackTrace);
}
}
}
}
public class PgpExampleUtilities
{
/**
* Search a secret key ring collection for a secret key corresponding to keyID if it
* exists.
*
* #param pgpSec a secret key ring collection.
* #param keyID keyID we want.
* #param pass passphrase to decrypt secret key with.
* #return
* #throws PGPException
* #throws NoSuchProviderException
*/
internal static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyID, char[] pass)
{
PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.ExtractPrivateKey(pass);
}
internal static PgpPublicKey ReadPublicKey(string fileName)
{
using (Stream keyIn = File.OpenRead(fileName))
{
return ReadPublicKey(keyIn);
}
}
internal static PgpPublicKey ReadPublicKey(Stream input)
{
PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(
PgpUtilities.GetDecoderStream(input));
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
foreach (PgpPublicKeyRing keyRing in pgpPub.GetKeyRings())
{
foreach (PgpPublicKey key in keyRing.GetPublicKeys())
{
if (key.IsEncryptionKey)
{
return key;
}
}
}
throw new ArgumentException("Can't find encryption key in key ring.");
}
internal static byte[] CompressFile(string fileName, CompressionAlgorithmTag algorithm)
{
MemoryStream bOut = new MemoryStream();
PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(algorithm);
PgpUtilities.WriteFileToLiteralData(comData.Open(bOut), PgpLiteralData.Binary,
new FileInfo(fileName));
comData.Close();
return bOut.ToArray();
}
}
public class Streams
{
private const int BufferSize = 512;
public static void PipeAll(Stream inStr, Stream outStr)
{
byte[] bs = new byte[BufferSize];
int numRead;
while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0)
{
outStr.Write(bs, 0, numRead);
}
}
}
}
I then call the EncryptFile Method as below:
public void EncryptMyFile()
{
Pgp.EncryptFile(#"C:\ ... Output.pgp", #"C:\ ... Input.txt", #"C:\ ... pubkey.txt", false, false);
}
Used code above and it properly generated a file, but the vendor says that it is "double encrypted".
Related
I am making a security software which encrypt data using AES encryption
when I encrypt or decrypt using FileIO it works fine as all the bytes are read and fed to the encrypt function at once but that leads to problem for huge files as it loads a huge file into the ram.
So I used datareader and datawriter to load file partially and sent it to encrypt
but the problem is as I do this when I decrypt this file it throws cyclic redundancy error. I partially know the reason but don't know how to fix
here are my codes
New one using data reader and writer
public async void EncryptDecryptFileNew(StorageFile sourcefile, StorageFolder destinationfolder, string password, string username, bool IsEncrypt)
{
passwordstring = AES_Encrypt(StringToBytes(password), username);
if (sourcefile != null)
{
StorageFile sfile = null;
bool isCompletedSuccesfully = false;
try
{
if (StorageApplicationPermissions.FutureAccessList.CheckAccess(sourcefile))
{
var properties = await sourcefile.GetBasicPropertiesAsync();
string temp = destinationfolder.Path;
StorageFolder sf = await StorageFolder.GetFolderFromPathAsync(temp);
sfile = await sf.CreateFileAsync(sourcefile.Name, CreationCollisionOption.ReplaceExisting);
Debug.WriteLine(sourcefile.DisplayName + properties.Size, "FileStart");
if (properties.Size > 0)
{
using (Stream sstream = await sourcefile.OpenStreamForReadAsync())
{
using (DataReader sourceReader = new DataReader(sstream.AsInputStream()))
{
using (var dstream = await sfile.OpenAsync(FileAccessMode.ReadWrite))
{
using (var outputStream = dstream.GetOutputStreamAt(0))
{
using (DataWriter dWriter = new DataWriter(outputStream))
{
byte[] f;
for (ulong i = 0; i < (properties.Size / 64); i++)
{
await sourceReader.LoadAsync(64);
if (IsEncrypt)
f = AES_Encrypt(sourceReader.ReadBuffer(64).ToArray(), BytesToString(passwordstring));
else
f = AES_Decrypt(sourceReader.ReadBuffer(64).ToArray(), BytesToString(passwordstring));
dWriter.WriteBytes(f);
}
uint remaining = (uint)properties.Size % 64;
if ((remaining) != 0)
{
await sourceReader.LoadAsync(remaining);
if (IsEncrypt)
f = AES_Encrypt(sourceReader.ReadBuffer(remaining).ToArray(), BytesToString(passwordstring));
else
f = AES_Decrypt(sourceReader.ReadBuffer(remaining).ToArray(), BytesToString(passwordstring));
dWriter.WriteBytes(f);
}
await dWriter.StoreAsync();
}
}
}
}
}
isCompletedSuccesfully = true;
GC.Collect();
}
else
isCompletedSuccesfully = true;
Debug.WriteLine(sourcefile.DisplayName, "FileEnd");
}
}
catch (OutOfMemoryException y)
{
GC.Collect();
}
catch (Exception e)
{
Debug.WriteLine(e.Message, "Encryption File");
}
finally
{
System.GC.Collect();
if (isCompletedSuccesfully)
{
if (totalSize != 0)
{
Debug.WriteLine(sourcefile.DisplayName + " " + fileDictionary[sourcefile.Path] + " " + totalSize);
Percentage += fileDictionary[sourcefile.Path] / totalSize;
}
if (FEDD != null)
FEDD();
}
else
{
if (fileDictionary.Count != 0)
{
if (sfile != null)
{
await sfile.DeleteAsync();
}
EncryptDecryptFileNew(sourcefile, destinationfolder, password, username, IsEncrypt);
}
}
await Task.CompletedTask;
}
}
}
for encryption
public byte[] AES_Encrypt(byte[] input, string pass)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
CryptographicKey AES;
HashAlgorithmProvider HAP = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
CryptographicHash Hash_AES = HAP.CreateHash();
try
{
IBuffer Buffer = CryptographicBuffer.CreateFromByteArray(input);
byte[] encrypted;
byte[] hash = new byte[32];
Hash_AES.Append(CryptographicBuffer.CreateFromByteArray(System.Text.Encoding.UTF8.GetBytes(pass)));
byte[] temp;
CryptographicBuffer.CopyToByteArray(Hash_AES.GetValueAndReset(), out temp);
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES = SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(hash));
encrypted= (CryptographicEngine.Encrypt(AES, Buffer, null)).ToArray();
//encrypted = CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Encrypt(AES, Buffer, null));
return encrypted;
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message,"AES ENCRYPT");
return null;
}
}
for decryption
public byte[] AES_Decrypt(byte[] input, string pass)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
CryptographicKey AES;
HashAlgorithmProvider HAP = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
CryptographicHash Hash_AES = HAP.CreateHash();
try
{
byte[] hash = new byte[32];
Hash_AES.Append(CryptographicBuffer.CreateFromByteArray(System.Text.Encoding.UTF8.GetBytes(pass)));
byte[] temp;
CryptographicBuffer.CopyToByteArray(Hash_AES.GetValueAndReset(), out temp);
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES = SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(hash));
IBuffer Buffer = GetBufferFromBytes(input);
byte[] Decrypted;
CryptographicBuffer.CopyToByteArray(CryptographicEngine.Decrypt(AES, Buffer, null), out Decrypted);
return Decrypted;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
and the old one that worked
public async void EncryptDecryptFile(StorageFile sourcefile,StorageFolder destinationfolder,string password,string username,bool IsEncrypt)
{
passwordstring = AES_Encrypt(StringToBytes(password), username);
if (sourcefile != null)
{
StorageFile sfile=null;
bool isCompletedSuccesfully = false;
try
{
if (StorageApplicationPermissions.FutureAccessList.CheckAccess(sourcefile))
{
var properties = await sourcefile.GetBasicPropertiesAsync();
string temp = destinationfolder.Path;
StorageFolder sf = await StorageFolder.GetFolderFromPathAsync(temp);
sfile = await sf.CreateFileAsync(sourcefile.Name, CreationCollisionOption.ReplaceExisting);
Debug.WriteLine(sourcefile.DisplayName+properties.Size,"FileStart");
if (properties.Size > 0)
{
IBuffer buffer = await FileIO.ReadBufferAsync(sourcefile);
byte[] f = null;
Debug.WriteLine("tw1", sourcefile.Name);
GC.Collect();
if (IsEncrypt)
f = AES_Encrypt(buffer.ToArray(), BytesToString(passwordstring));
else
f = AES_Decrypt(buffer.ToArray(), BytesToString(passwordstring));
Debug.WriteLine("tw2", sourcefile.Name);
GC.Collect();
var rt = FileIO.WriteBufferAsync(sfile, GetBufferFromBytes(f));
rt.AsTask().Wait();
isCompletedSuccesfully = true;
GC.Collect();
Debug.WriteLine("tw3", sourcefile.Name);
}
else
isCompletedSuccesfully = true;
Debug.WriteLine(sourcefile.DisplayName, "FileEnd");
}
}
catch (OutOfMemoryException y)
{
GC.Collect();
}
catch (Exception e)
{
Debug.WriteLine(e.Message,"Encryption File");
}
finally
{
System.GC.Collect();
if (isCompletedSuccesfully)
{
if (totalSize != 0)
{
Debug.WriteLine(sourcefile.DisplayName + " " + fileDictionary[sourcefile.Path] + " " + totalSize);
Percentage += fileDictionary[sourcefile.Path] / totalSize;
}
if(FEDD!=null)
FEDD();
}
else
{
if (fileDictionary.Count != 0)
{
if (sfile != null)
{
await sfile.DeleteAsync();
}
EncryptDecryptFile(sourcefile, destinationfolder, password, username, IsEncrypt);
}
}
await Task.CompletedTask;
}
}
}
We want to use Cipher encryption/decryption in windows phone 7. We have done for android using java. But when we try to develop in c# we struggling.
Our Java code:
public AES()
{
try
{
Security.addProvider(new BouncyCastleProvider());
cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public String doDecrypt(String key, String cipherText)
{
try
{
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
SecretKeySpec skey = new SecretKeySpec(raw, "AES");
cipher.init(Cipher.DECRYPT_MODE, skey );
return new String(cipher.doFinal(Base64.decode(cipherText,Base64.DEFAULT)), Charset.forName("UTF-8"));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
public String doEncrypt(String key, String plainText)
{
try
{
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
SecretKeySpec skey = new SecretKeySpec(raw, "AES");
cipher.init(Cipher.ENCRYPT_MODE, skey );
return Base64.encodeToString(cipher.doFinal(plainText.getBytes(Charset.forName("UTF-8"))),Base64.DEFAULT);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
}
Here we can encrypt and decryption.
Our C# code is:
public static byte[] EncryptWithAES(string dataToEncrypt, String Key)
{
byte[] encryptedData;
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(Key);
using (AesManaged aesEnc = new AesManaged())
{
aesEnc.Key = keyBytes;
aesEnc.IV = new byte[16];
//Create encryptor for converting
ICryptoTransform encryptor = aesEnc.CreateEncryptor(aesEnc.Key, aesEnc.IV);
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream crypStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter srmWriter = new StreamWriter(crypStream))
{
srmWriter.Write(dataToEncrypt);
}
encryptedData = memStream.ToArray();
}
}
}
return encryptedData;
}
But here we receive different output.
Java OP:-
OYbW6pI8mgqU5xOcfG8N92e28T9GUObtcea4XWqU0yQyJRULSLV/yjAzDh8gq9Hgj5K5OubZfdm/
/ts66eQMJYH4TBX0/hN5zPwQbdTWmfVU3dDyU2SyQek5zYcWW+OgnppL9jcMcJZg4pv2+q6x8w==
C# OP:-
OYbW6pI8mgqU5xOcfG8N9wXs2/gWMc6dcUSEoLXm3L5v9Ih9eN63xO31mXmEDLprIzusXaOS1rNNtBPi5I8FG3IukVgicagrkLul1vfa142z+XDULJXFmg5rxPa6iJzXqeZ6x3wxbfI3T/ZqGwxqbg==
We can not get the exact encrypted data like java. Please suggest or provide any links for Cipher encryption/decryption in windows phone 7.
It works for me using LINQPad:
Console.Write(String.Join(" ", EncryptWithAES("hello", "AAECAwQFBgcICQoLDA0ODw==")));
yields:
91 209 208 157 151 41 81 76 99 8 248 231 34 62 204 1
Perhaps it's something specific for Window Phone 7 that is causing it not to work?
Key from https://stackoverflow.com/a/2919565/1185053
Finally I got solution from stackoverflow. I found the solution from here.. It is working fine..
The Solution is:-
Crypt Class:-
public class BCEngine
{
private Encoding _encoding;
private IBlockCipher _blockCipher;
private PaddedBufferedBlockCipher _cipher;
private IBlockCipherPadding _padding;
Pkcs7Padding pkcs = new Pkcs7Padding();
public BCEngine(IBlockCipher blockCipher, Encoding encoding)
{
_blockCipher = blockCipher;
_encoding = encoding;
}
public string Encrypt(string plain, string key)
{
byte[] result = BouncyCastleCrypto(true, _encoding.GetBytes(plain), key);
return Convert.ToBase64String(result);
}
public string Decrypt(string cipher, string key)
{
byte[] result = BouncyCastleCrypto(false, Convert.FromBase64String(cipher), key);
return _encoding.GetString(result, 0, result.Length);
}
private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, string key)
{
try
{
_cipher = _padding == null ? new PaddedBufferedBlockCipher(_blockCipher) : new PaddedBufferedBlockCipher(_blockCipher, _padding);
byte[] keyByte = _encoding.GetBytes(key);
_cipher.Init(forEncrypt, new KeyParameter(keyByte));
return _cipher.DoFinal(input);
}
catch (Org.BouncyCastle.Crypto.CryptoException ex)
{
throw new CryptoException(ex.Message);
}
}
public string AESEncryption(string plain, string key)
{
return Encrypt(plain, key);
}
public string AESDecryption(string cipher, string key)
{
return Decrypt(cipher, key);
}
public BCEngine()
{
_blockCipher = new AesEngine();
_encoding = Encoding.UTF8;
pkcs = new Pkcs7Padding();
_padding = pkcs;
}
}
Now I can Call from anywhere to encrypt/decrypt the text.
Example:-
public partial class AesExample : PhoneApplicationPage
{
public AesExample()
{
InitializeComponent();
string key = "b09f72a0lkb1lktb";
string plainText = "Text To Encrypt";
BCEngine bcEngine = new BCEngine();
string encryptedString= bcEngine.Encrypt(plainText, key);
Console.WriteLine("\n\nEncrypted String==> " + encryptedString);
BCEngine bcEnginenew = new BCEngine();
string decryptedString = bcEnginenew.Decrypt(encryptedString, key);
Console.WriteLine("\n\nDecrypted String==> " + decryptedString);
}
}
I have following code that works fine when I use a four letter word as input, E.g. “Test”. When the input is not a multiple of 4, it fails E.g. “MyTest”.
Eexception: Invalid length for a Base-64 char array.
QUESTIONS
Is it guaranteed that the encrypted result will be always compatible to a Unicode string (without any loss). If yes I can use UTF encoding instead of Base64? What's the difference between UTF8/UTF16 and Base64 in terms of encoding
How can add padding so that we get correct result (after decryption) even if the input is not a multiple of 4?
MAIN PRGORAM
class Program
{
static void Main(string[] args)
{
string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
string inputValue = "MyTest";
string keyValue = valid128BitString;
byte[] byteValForString = Convert.FromBase64String(inputValue);
EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
EncryptResult encyptedValue = new EncryptResult();
string resultingIV = "4uy34C9sqOC9rbV4GD8jrA==";
if (String.Equals(resultingIV,result.IV))
{
int x = 0;
}
encyptedValue.IV = resultingIV;
encyptedValue.EncryptedMsg = result.EncryptedMsg;
string finalResult = Convert.ToBase64String(Aes128Utility.DecryptData(encyptedValue, keyValue));
Console.WriteLine(finalResult);
if (String.Equals(inputValue, finalResult))
{
Console.WriteLine("Match");
}
else
{
Console.WriteLine("Differ");
}
Console.ReadLine();
}
}
AES Crypto UTILITY
public static class Aes128Utility
{
private static byte[] key;
public static EncryptResult EncryptData(byte[] rawData, string strKey)
{
EncryptResult result = null;
if (key == null)
{
if (!String.IsNullOrEmpty(strKey))
{
key = Convert.FromBase64String((strKey));
result = Encrypt(rawData);
}
}
else
{
result = Encrypt(rawData);
}
return result;
}
public static byte[] DecryptData(EncryptResult encryptResult, string strKey)
{
byte[] origData = null;
if (key == null)
{
if (!String.IsNullOrEmpty(strKey))
{
key = Convert.FromBase64String(strKey);
origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
}
}
else
{
origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
}
return origData;
}
private static EncryptResult Encrypt(byte[] rawData)
{
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = key;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.IV = Convert.FromBase64String("4uy34C9sqOC9rbV4GD8jrA==");
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream encStream = new CryptoStream(memStream, aesProvider.CreateEncryptor(), CryptoStreamMode.Write);
encStream.Write(rawData, 0, rawData.Length);
encStream.FlushFinalBlock();
EncryptResult encResult = new EncryptResult();
encResult.EncryptedMsg = Convert.ToBase64String(memStream.ToArray());
encResult.IV = Convert.ToBase64String(aesProvider.IV);
return encResult;
}
}
}
private static byte[] Decrypt(byte[] encryptedMsg, byte[] iv)
{
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = key;
aesProvider.IV = iv;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream decStream = new CryptoStream(memStream, aesProvider.CreateDecryptor(), CryptoStreamMode.Write);
decStream.Write(encryptedMsg, 0, encryptedMsg.Length);
decStream.FlushFinalBlock();
return memStream.ToArray();
}
}
}
}
DTO
public class EncryptResult
{
public string EncryptedMsg { get; set; }
public string IV { get; set; }
}
REFERENCES:
How to create byte[] with length 16 using FromBase64String
Getting incorrect decryption value using AesCryptoServiceProvider
If you are encrypting, then encoding Base64 for me doesn't add anything useful, instead it brings the problems you face.
As for the padding, a solution i have seen is to create a new byte[] that is indeed a multiple of 4 and copy the source byte[] to that new byte[].
So, something like this:
if (rawdata.Length % 16 !=0)
{
newSource = new byte[source.Length + 16 - source.Length % 16];
Array.Copy(source, newSource, source.Length);
}
Base64 is a way of representing binary values as text so that you do not conflict with common control codes like \x0A for newline or \0 for a string terminator. It is NOT for turning typed text in to binary.
Here is how you should be passing the text in and getting it back out. You can replace UTF8 with whatever encoding you want, but you will need to make sure the Encoding.Whatever.GetBytes is the same encoding as the Encoding.Whatever.GetString
class Program
{
static void Main(string[] args)
{
string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
string inputValue = "MyTest";
string keyValue = valid128BitString;
//Turns our text in to binary data
byte[] byteValForString = Encoding.UTF8.GetBytes(inputValue);
EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
EncryptResult encyptedValue = new EncryptResult();
//(Snip)
encyptedValue.IV = resultingIV;
encyptedValue.EncryptedMsg = result.EncryptedMsg;
string finalResult = Encoding.UTF8.GetString(Aes128Utility.DecryptData(encyptedValue, keyValue));
Console.WriteLine(finalResult);
if (String.Equals(inputValue, finalResult))
{
Console.WriteLine("Match");
}
else
{
Console.WriteLine("Differ");
}
Console.ReadLine();
}
}
I've seen a lot of questions, none really provide good answers, or if they try, they assume the one asking the question isn't asking what he actually IS asking. And just for the record, I am NOT asking about signing, encryption or PEM files!
In my case, I'll be receiving a small file of encrypted data. It has been encrypted using the private key of an RSA key pair.
The public key I have available is a DER file in the ASN.1 format, it is not a certificate as such, and it is not signed.
In Java, this is easy, in c# it is a nightmare from what I can tell.
X509Certificate2 claims "Object not found", when I try to parse it the byte array of the key.
The keys were generated with the following openSSL commands:
>openssl genrsa -out private_key.pem 2048
Generating RSA private key, 2048 bit long modulus
...........+++
....................................................................................................
...........................+++
e is 65537 (0x10001)
>openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
>openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
writing RSA key
Edit: The code I'm trying:
private byte[] ReadBytesFromStream(string streamName)
{
using (Stream stream = this.GetType().Assembly.GetManifestResourceStream(streamName))
{
byte[] result = new byte[stream.Length];
stream.Read(result, 0, (int) stream.Length);
return result;
}
}
public void LoadPublicKey()
{
byte[] key = ReadBytesFromStream("my.namespace.Resources.public_key.der");
X509Certificate2 myCertificate;
try
{
myCertificate = new X509Certificate2(key);
}
catch (Exception e)
{
throw new CryptographicException(String.Format("Unable to open key file. {0}", e));
}
}
The ReadBytesFromStream method does return the correct byte stream, the public_key.der file is an embedded resource.
Just to polish this one off. The code below is still a bit messy though, but it does seem to be what I could get to work.
The reason we can't use public keys to decrypt a message in Windows is explained here:
http://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.security/2004-05/0270.html
Next, you need the public key modulus and exponent in the c# code, somewhere, be it in a file or embedded is really not the matter here. I do use Base64 to wrap up the binary keys though. I gave up on generating a key that Windows would actually import. Besides, for what I need, the public key will be an embedded resource.
The last problem I had were that the modulus generated from the key weren't working on Windows (c#). This StackOverflow post did hold the key to solve that one, the modulus in Windows can not contain leading zeroes.
Android in-app billing Verification of Receipt in Dot Net(C#)
The modulus generated from Java didn't work on c#. Use the stripLeadingZeros code from that post to create a working Public key modulus:
private static byte[] stripLeadingZeros(byte[] a) {
int lastZero = -1;
for (int i = 0; i < a.length; i++) {
if (a[i] == 0) {
lastZero = i;
}
else {
break;
}
}
lastZero++;
byte[] result = new byte[a.length - lastZero];
System.arraycopy(a, lastZero, result, 0, result.length);
return result;
}
For starters, this is how I generated the key files:
$ openssl genrsa -out private_key.pem 2048
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
$ openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
The Java Code:
package com.example.signing;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import jcifs.util.Base64;
public class SigningExample {
private static String PRIVATE_KEY_FILE = "private_key.der";
private static String PUBLIC_KEY_FILE = "public_key.der";
/**
* #param args
*/
public static void main(String[] args) {
SigningExample.sign();
}
private static void sign() {
try {
String text = new String(SigningExample.loadFromFile("message.xml"));
String message = Base64.encode(text.getBytes());
RSAPrivateKey privateKey = PrivateRSAKeyReader.getKeyFile(SigningExample.PRIVATE_KEY_FILE);
RSAPublicKey publicKey = PublicRSAKeyReader.getKeyFile(SigningExample.PUBLIC_KEY_FILE);
byte[] modulusBytes = publicKey.getModulus().toByteArray();
byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();
modulusBytes = SigningExample.stripLeadingZeros(modulusBytes);
String modulusB64 = Base64.encode(modulusBytes);
String exponentB64 = Base64.encode(exponentBytes);
System.out.println("Copy these two into your c# code:");
System.out.println("DotNet Modulus : " + modulusB64);
System.out.println("DotNet Exponent: " + exponentB64);
// Testing
byte[] signature = SigningExample.sign(message.getBytes(), privateKey, "SHA1withRSA");
String signedMessage = message + "\n" + Base64.encode(signature);
SigningExample.saveToBase64File("message.signed", signedMessage.getBytes());
System.out.println("\nMessage :\n" + signedMessage);
String[] newkey = new String(SigningExample.loadFromBase64File("message.signed")).split("\\n");
System.out.println("Verified : " + SigningExample.verify(newkey[0].getBytes(), publicKey, "SHA1withRSA", Base64.decode(newkey[1])));
} catch (Exception e) {
e.printStackTrace();
}
}
private static byte[] stripLeadingZeros(byte[] a) {
int lastZero = -1;
for (int i = 0; i < a.length; i++) {
if (a[i] == 0) {
lastZero = i;
} else {
break;
}
}
lastZero++;
byte[] result = new byte[a.length - lastZero];
System.arraycopy(a, lastZero, result, 0, result.length);
return result;
}
private static byte[] sign(byte[] data, PrivateKey prvKey,
String sigAlg) throws Exception {
Signature sig = Signature.getInstance(sigAlg);
sig.initSign(prvKey);
sig.update(data, 0, data.length);
return sig.sign();
}
private static boolean verify(byte[] data, PublicKey pubKey,
String sigAlg, byte[] sigbytes) throws Exception {
Signature sig = Signature.getInstance(sigAlg);
sig.initVerify(pubKey);
sig.update(data, 0, data.length);
return sig.verify(sigbytes);
}
public static void saveToBase64File(String fileName, byte[] data) throws IOException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));
try {
String b64 = Base64.encode(data);
int lineLength = 64;
int idx = 0;
int len = b64.length();
while (len - idx >= lineLength) {
out.write(b64.substring(idx, idx + lineLength).getBytes());
out.write('\n');
idx += lineLength;
}
out.write(b64.substring(idx, len).getBytes());
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
out.close();
}
}
public static byte[] loadFromBase64File(String fileName) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fileName));
try {
StringBuffer sb = new StringBuffer();
String buffer;
while ((buffer = br.readLine()) != null) {
sb.append(buffer);
}
return Base64.decode(sb.toString());
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
br.close();
}
}
public static void saveToFile(String fileName, byte[] data) throws IOException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));
try {
out.write(data);
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
out.close();
}
}
public static byte[] loadFromFile(String fileName) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fileName));
try {
StringBuffer sb = new StringBuffer();
String buffer;
while ((buffer = br.readLine()) != null) {
sb.append(buffer);
}
return sb.toString().getBytes();
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
br.close();
}
}
}
class PrivateRSAKeyReader {
public static RSAPrivateKey getKeyFile(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
return PrivateRSAKeyReader.generateKey(keyBytes);
}
public static RSAPrivateKey getKey(String base64) throws Exception {
byte[] keyBytes = Base64.decode(base64);
return PrivateRSAKeyReader.generateKey(keyBytes);
}
private static RSAPrivateKey generateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
}
}
class PublicRSAKeyReader {
public static RSAPublicKey getKeyFile(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
return PublicRSAKeyReader.generateKey(keyBytes);
}
public static RSAPublicKey getKey(String base64) throws Exception {
byte[] keyBytes = Base64.decode(base64);
return PublicRSAKeyReader.generateKey(keyBytes);
}
private static RSAPublicKey generateKey(byte[] keyBytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
}
}
In c# this code fragment should do the tricks:
String signedMessage = ""; // load Base64 coded the message generated in Java here.
byte[] message = Convert.FromBase64String(signedMessage);
String messageString = Encoding.ASCII.GetString(message);
String[] lines = Regex.Split(messageString, "\n");
byte[] content = Convert.FromBase64String(lines[0]); // first line of the message were the content
byte[] signature = Convert.FromBase64String(lines[1]); // second line were the signature
RSACryptoServiceProvider rsaObj = new RSACryptoServiceProvider(2048);
//Create a new instance of the RSAParameters structure.
RSAParameters rsaPars = new RSAParameters();
rsaPars.Modulus = Convert.FromBase64String("insert your modulus revealed in the Java example here");
rsaPars.Exponent = Convert.FromBase64String("AQAB"); // Exponent. Should always be this.
// Import key parameters into RSA.
rsaObj.ImportParameters(rsaPars);
// verify the message
Console.WriteLine(rsaObj.VerifyData(Encoding.ASCII.GetBytes(lines[0]), "SHA1", signature));
The code is using :
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
I freely admit that packing the final message into another layer of Base64 is a little overkill and should be changed.
I've researched a bit about how to achieve what I said in the question and found several APIs but most of them look very complicated and since I'm just a noobie in this area I just want a simple method like:
public String Encrypt(String message, PublicKey publicKey)
Don't know if this can be done? If not then please someone enlighten me another way to achieve this :)
Thank you.
UPDATE:
So far I have only seen that all of the library for OpenPGP encryption require both the public key and private key to do the encrypt while I only want to encrypt with the public key (because I don't have the private key to use it)!
I found a tutorial here but it requires both Secret Key and Public Key to encrypt data. However I've modified the codes a bit to only require public key (no signing, no compress) and thought I should publish it here in case anyone also looking for a solution for this question. Belows is the modified codes, all the credits for the author - Mr. Kim.
public class PgpEncrypt
{
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 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 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);
}
}
Of course to run these codes you have to include BouncyCastle library in your project.
I've tested encrypting and then decrypting and it runs fine :)
Here is perhaps a cleaner approach:
var pkr = asciiPublicKeyToRing(ascfilein);
if (pkr != null)
{
try
{
EncryptFile(
tbUnencryptedFile.Text, tbEncryptedFile.Text, getFirstPublicEncryptionKeyFromRing(pkr), true, true);
MessageBox.Show("File Encrypted.");
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
else
{
MessageBox.Show(ascfilein + " is not a public key.");
}
private PgpPublicKeyRing asciiPublicKeyToRing(string ascfilein)
{
using (Stream pubFis = File.OpenRead(ascfilein))
{
var pubArmoredStream = new ArmoredInputStream(pubFis);
PgpObjectFactory pgpFact = new PgpObjectFactory(pubArmoredStream);
Object opgp = pgpFact.NextPgpObject();
var pkr = opgp as PgpPublicKeyRing;
return pkr;
}
}
private PgpPublicKey getFirstPublicEncryptionKeyFromRing(PgpPublicKeyRing pkr)
{
foreach (PgpPublicKey k in pkr.GetPublicKeys())
{
if (k.IsEncryptionKey)
return k;
}
throw new ArgumentException("Can't find encryption key in key ring.");
}
public static void EncryptFile(string inputFile, string outputFile, PgpPublicKey encKey, bool armor,
bool withIntegrityCheck)
{
using (MemoryStream bOut = new MemoryStream())
{
PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
PgpUtilities.WriteFileToLiteralData(comData.Open(bOut), PgpLiteralData.Binary,
new FileInfo(inputFile));
comData.Close();
PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Aes256,
withIntegrityCheck, new SecureRandom());
cPk.AddMethod(encKey);
byte[] bytes = bOut.ToArray();
using (Stream outputStream = File.Create(outputFile))
{
if (armor)
{
using (ArmoredOutputStream armoredStream = new ArmoredOutputStream(outputStream))
using (Stream cOut = cPk.Open(armoredStream, bytes.Length))
{
cOut.Write(bytes, 0, bytes.Length);
}
}
else
{
using (Stream cOut = cPk.Open(outputStream, bytes.Length))
{
cOut.Write(bytes, 0, bytes.Length);
}
}
}
}
}
Did you take a look at the bouncycastle pgp? http://www.bouncycastle.org/
There is a source example here of enrypting a file taken from the BouncyCastle site: Need example for BouncyCastle PGP File encryption in C#
If you want to do both encryption and decryption in dotnet core, this is the article I followed: https://nightbaker.github.io/pgp/cryptography/.net/core/2019/02/08/pgp-encryption/
The encryption part does not require the private key.
All credits go to the original author NightBaker.
Install-Package BouncyCastle.NetCore
Install-Package BouncyCastle.NetCoreSdk
public class Pgp
{
public static void EncryptFile(
string outputFileName,
string inputFileName,
string encKeyFileName,
bool armor,
bool withIntegrityCheck)
{
PgpPublicKey encKey = PgpExampleUtilities.ReadPublicKey(encKeyFileName);
using (Stream output = File.Create(outputFileName))
{
EncryptFile(output, inputFileName, encKey, armor, withIntegrityCheck);
}
}
private static void EncryptFile(
Stream outputStream,
string fileName,
PgpPublicKey encKey,
bool armor,
bool withIntegrityCheck)
{
if (armor)
{
outputStream = new ArmoredOutputStream(outputStream);
}
try
{
byte[] bytes = PgpExampleUtilities.CompressFile(fileName, CompressionAlgorithmTag.Zip);
PgpEncryptedDataGenerator encGen = new PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
encGen.AddMethod(encKey);
Stream cOut = encGen.Open(outputStream, bytes.Length);
cOut.Write(bytes, 0, bytes.Length);
cOut.Close();
if (armor)
{
outputStream.Close();
}
}
catch (PgpException e)
{
Console.Error.WriteLine(e);
Exception underlyingException = e.InnerException;
if (underlyingException != null)
{
Console.Error.WriteLine(underlyingException.Message);
Console.Error.WriteLine(underlyingException.StackTrace);
}
}
}
}
public class PgpExampleUtilities
{
internal static PgpPublicKey ReadPublicKey(string fileName)
{
using (Stream keyIn = File.OpenRead(fileName))
{
return ReadPublicKey(keyIn);
}
}
internal static PgpPublicKey ReadPublicKey(Stream input)
{
PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(
PgpUtilities.GetDecoderStream(input));
//
// we just loop through the collection till we find a key suitable for encryption, in the real
// world you would probably want to be a bit smarter about this.
//
foreach (PgpPublicKeyRing keyRing in pgpPub.GetKeyRings())
{
foreach (PgpPublicKey key in keyRing.GetPublicKeys())
{
if (key.IsEncryptionKey)
{
return key;
}
}
}
throw new ArgumentException("Can't find encryption key in key ring.");
}
internal static byte[] CompressFile(string fileName, CompressionAlgorithmTag algorithm)
{
MemoryStream bOut = new MemoryStream();
PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(algorithm);
PgpUtilities.WriteFileToLiteralData(comData.Open(bOut), PgpLiteralData.Binary,
new FileInfo(fileName));
comData.Close();
return bOut.ToArray();
}
}
Usage:
Pgp.EncryptFile("Resources/output.txt", "Resources/input.txt", "Resources/publicKey.txt", true, true);
If you are looking for a simple dotnet library to do pgp encryption take a look at PgpCore. Its a wrapper around Bouncy Castle - just making it a little bit easier than needing to know the internals of Bouncy Castle.