Saving the PgpPublicKey to string - c#

I am generating a public key and secret Key and want to save the public key in a string field.Saving it to the file works fine. but how do I get it into a string...
private string SaveAndReturn(PgpPublicKey publicKey, bool armor = true)
{
var fileStream = new FileInfo($#"C:\encrypt\Keys\pub2.asc").OpenWrite() as Stream;
SaveFile(publicKey, fileStream, armor);
fileStream.Close();
var keyString = GetStringFromKey(publicKey);
return keyString;
}
public void SaveFile(PgpPublicKey publicKey, Stream outStream, bool armor = true)
{
if (armor)
{
outStream = new ArmoredOutputStream(outStream);
}
publicKey.Encode(outStream);
outStream.Close();
}
private string GetStringFromKey(PgpPublicKey publicKey)
{
var armor = true;
var stream = new MemoryStream() as Stream;
publicKey.Encode(stream);
stream.Position = 0;
var keyString = GetString(stream);
stream.Close();
Console.WriteLine(keyString);
return keyString;
}
private string GetString(Stream theStream, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var reader = new StreamReader(theStream, encoding))
{
return reader.ReadToEnd();
}
}
The pub2.asc file is created fine and has the in there. Instead of saving the key into a file, I want it into a string.. and the following code gives me an empty string. What am I doing wrong?
So, I updated the code to set the position before reading the stream. But the string returned is not the same as the text I see in file.
I think ArmoredOutputStream does Base64 encoding. How to I get the same string as in text file
String from the above code:
???z}?.?u?l:?|?5 ?test?? Wh?? $???_??cH???L~??+??Bc$?rN??g?.??m?s????0?Z???k?-??G??mzBQ$
U?E??V|/?,l???=???p???? ??5?%%?9??ae?S?|8Lh?\???|pB??G*?8????N9'???+(?|R??Q#R??_9D
If I use ASCII encoding, the string is
??Wh{? ??;cgx??hO)???C?~????t?roS?4??:??????E?fF?U?r?+?=?p???#L?????a??B}????-.o??q?i5???Ff.??6???????6r?HdX??}?S???4?(j)3 ?test?? Wh?
?U?+U???????????v??????BJEwp*0?'?lz2??6??P,A???Q??2M??3?a?W??9???*????M%?|?U^>??????q?&??V??N
Text from Pub2.asc file
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG C# v1.8.1.0
mIsEV2h5lwEEAJakqTE+XcRBQ0b2P2vjNuDrgi9IcCsgEGdD3bpH6d1W8CSR+YkZ
EV/PwmNI1dyh8Ex+zqHQKwaPCIfEQhNjF2sIJKVyTtjpZ4YukoRto3PBjdse0zAB
2Freiv7Ha9ct0wSQRwID2haKbXpCUSQN5/j8xbx90y60ddxsOoV8C7A1AAUTtAR0
ZXN0iJwEEAECAAYFAldosdcACgkQPIdhk1pxnGAhxwQAg4NRYbuSDg5WVw3G1tVm
1xM746+O5J6B5RHnaQvFePpVVUm/tpLyCbHPNeYlJao5FeHRYWWpU4d8OExo4phc
iv7JfHBCx9tHKso4uccS3ROaTjknw+jwuLErKLN8UvjaUUBSn8wVXzlEDVXSRYCt
Vnwv/Sxsxe7mgT2qBJSYcKY=
=PYEC
-----END PGP PUBLIC KEY BLOCK-----

The position of the MemoryStream is still set to the end of the string you wrote into it. You need to reset the position to 0 before you try to read from it again. You can do this in the calling code or in GetString() directly. Here is the fix in the calling code:
private string GetStringFromKey(PgpPublicKey publicKey)
{
var armor = true;
var stream = new MemoryStream() as Stream;
publicKey.Encode(stream);
-->stream.Position = 0;
var keyString = GetString(stream);
stream.Close();
Console.WriteLine(keyString);
return keyString;
}
You may also find some useful information over at How do you get a string from a MemoryStream?

Related

BouncyCastle EncryptAndSign not signing properly

I have limited knowledge with PGP but came across code to use BouncyCastle to take care of it. I'm able to Encrypt/Decrypt a file but was asked to send an Encrypted and Signed file to an external vendor. When I use the following code I've found it does encrypt the file but when the vendor tries to decrypt they are getting an error gpg: Can't check signature: No public key. I don't know enough about PGP to know how to troubleshoot this properly. Looking for any suggestions to try.
Here is the code I'm using. The source and encrypted file path seem to be fine. The Public Key is pointing to the vendor's Public Key they provided me. The Private Key and Private Key Phrase are the Private Key file and password from the Key that I made for my company.
PGPEncryptDecrypt.EncryptAndSign(sourceFileFullPath,
encryptedFileFullPath,
publicKeyFileName,
privateKeyFileName,
privateKeyPhrase,
true);
This is the code in the PGPEncryptDecrypt class I pulled from an example.
#region Encrypt and Sign
public static void EncryptAndSign(string inputFile, string outputFile, string publicKeyFile, string privateKeyFile, string passPhrase, bool armor)
{
PGPEncryptionKeys encryptionKeys = new PGPEncryptionKeys(publicKeyFile, privateKeyFile, passPhrase);
if (!File.Exists(inputFile))
throw new FileNotFoundException(String.Format("Input file [{0}] does not exist.", inputFile));
if (!File.Exists(publicKeyFile))
throw new FileNotFoundException(String.Format("Public Key file [{0}] does not exist.", publicKeyFile));
if (!File.Exists(privateKeyFile))
throw new FileNotFoundException(String.Format("Private Key file [{0}] does not exist.", privateKeyFile));
if (String.IsNullOrEmpty(passPhrase))
throw new ArgumentNullException("Invalid Pass Phrase.");
if (encryptionKeys == null)
throw new ArgumentNullException("Encryption Key not found.");
using (Stream outputStream = File.Create(outputFile))
{
if (armor)
using (ArmoredOutputStream armoredOutputStream = new ArmoredOutputStream(outputStream))
{
OutputEncrypted(inputFile, armoredOutputStream, encryptionKeys);
}
else
OutputEncrypted(inputFile, outputStream, encryptionKeys);
}
}
private static void OutputEncrypted(string inputFile, Stream outputStream, PGPEncryptionKeys encryptionKeys)
{
using (Stream encryptedOut = ChainEncryptedOut(outputStream, encryptionKeys))
{
FileInfo unencryptedFileInfo = new FileInfo(inputFile);
using (Stream compressedOut = ChainCompressedOut(encryptedOut))
{
PgpSignatureGenerator signatureGenerator = InitSignatureGenerator(compressedOut, encryptionKeys);
using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo))
{
using (FileStream inputFileStream = unencryptedFileInfo.OpenRead())
{
WriteOutputAndSign(compressedOut, literalOut, inputFileStream, signatureGenerator);
inputFileStream.Close();
}
}
}
}
}
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 static Stream ChainEncryptedOut(Stream outputStream, PGPEncryptionKeys m_encryptionKeys)
{
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 static PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut, PGPEncryptionKeys m_encryptionKeys)
{
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;
}
#endregion Encrypt and Sign
NEW ERROR AFTER INITIAL FIX
I was able to get by the initial error of the signature check (we had the wrong keys) but now the vendor is getting the following error. Keep in mind that just Encrypting the file works but EncryptAndSign is what is giving the error. Anyone have another suggestions?
gpg: WARNING: message was not integrity protected
gpg: Hint: If this message was created before the year 2003 it is
likely that this message is legitimate. This is because back
then integrity protection was not widely used.
gpg: Use the option '--ignore-mdc-error' to decrypt anyway.
gpg: decryption forced to fail!

c# base64 encoding. Invalid length error on decrpytion

I'm trying to decrypt a string which is also base64 encoded, but I am receiving an when I try to decrypt the string.
The error I am receiving is:
{System.FormatException: Invalid length for a Base-64 char array or string.
at this line in the decrypt function below:
MemoryStream ms = new MemoryStream(Convert.FromBase64String(inString));
Encrpyt/Decrypt functions:
//ENCRYPT
public static bool stringEncrypt(string inString,ref string outstring)
{
try
{
if(String.IsNullOrEmpty(inString)){return false;}
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms,provider.CreateEncryptor(PWbytes,PWbytes),CryptoStreamMode.Write);
StreamWriter sw = new StreamWriter(cs);
sw.Write(inString);
sw.Flush();
cs.FlushFinalBlock();
sw.Flush();
outstring = Convert.ToBase64String(ms.GetBuffer(),0,(int)ms.Length);
return true;
}
catch(Exception ex)
{
clsCommonBase.AppendToExceptionFile("Encrypt : " + ex.Message);
return false;
}
}
//DECRPYT
public static bool stringDecrypt(string inString,ref string outstring)
{
try
{
if(String.IsNullOrEmpty(inString)){return false;};
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
MemoryStream ms = new MemoryStream(Convert.FromBase64String(inString));
CryptoStream cs = new CryptoStream(ms, provider.CreateDecryptor(PWbytes,PWbytes),CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cs);
outstring = sr.ReadToEnd();
return true;
}
catch(Exception ex)
{
clsCommonBase.AppendToExceptionFile("Decrypt : " + ex.Message);
return false;
}
}
Solved using the simple solution in the following link
How do I encode and decode a base64 string?
Also added some code in the encoding function to ensure the plain text string will be converted to a valid length base64 string.
Code:
public static string Base64Encode(string plainText)
{
//check plain text string and pad if needed
int mod4 = plainText.Length % 4;
if (mod4 > 0)
{
plainText += new string('=', 4 - mod4);
}
//convert to base64 and return
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static string Base64Decode(string base64EncodedData)
{
//decode base64 and return as string
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}

SHA-1 Hash a byte array vs a file stream

I am writing an API that allows users to upload files (image, video, etc). I use a SHA-1 hash to make sure the same file isn't uploaded multiple times. Previously we only allowed smaller files so I was reading them into a byte array and hashing that but now we allow larger files so I am using a file stream. The problem is the SHA-1 algorithm returns a different hash. I need to figure out how to get the same hash regardless of the method, even if I have to turn the byte array into a file stream or something. However, I've tried writing the byte array to a temp file and reading it in and it returns the same hash as the byte array. Here is an example console app that shows what I am doing:
static void Main(string[] args)
{
string file = "C:\\CUWCDFileStorage\\temp\\test.png";
var bytes = File.ReadAllBytes(file);
using (var stream = File.Open(file, FileMode.Open))
{
Console.WriteLine(Sha1HashFile(bytes)); // Returns B7F6D90C30233F91FCEFE05FB49679F8B26C9D80
Console.WriteLine(Sha1HashFile(stream)); // Returns DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
Console.WriteLine(Sha1HashFile2(bytes)); // Returns B7F6D90C30233F91FCEFE05FB49679F8B26C9D80
}
Console.Read();
}
public static string Sha1HashFile(byte[] file)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", "");
}
}
public static string Sha1HashFile(Stream stream)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(stream)).Replace("-", "");
}
}
public static string Sha1HashFile2(byte[] bytes)
{
string file = "C:\\CUWCDFileStorage\\temp\\test2.png";
File.WriteAllBytes(file, bytes);
return Sha1HashFile(File.OpenRead(file));
}
I've even tried to just put the byte array into a MemoryStream with new MemoryStream(bytes) but that didn't work either. It seems like once I have the file in a byte array it can't be put back.
EDIT:
I removed some code from my example because I thought MD5 was working. Here is the original code I was using to test:
static void Main(string[] args)
{
string file = "C:\\CUWCDFileStorage\\temp\\test.png";
var bytes = File.ReadAllBytes(file);
using (var stream = File.Open(file, FileMode.Open))
{
Console.WriteLine(Md5HashFile(bytes));
Console.WriteLine(Md5HashFile(stream));
Console.WriteLine(Sha1HashFile(bytes));
Console.WriteLine(Sha1HashFile(stream));
Console.WriteLine(Sha1HashFile2(bytes));
}
Console.Read();
}
public static string Md5HashFile(byte[] file)
{
using (MD5 md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(file)).Replace("-", "");
}
}
public static string Sha1HashFile(byte[] file)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", "");
}
}
public static string Md5HashFile(Stream stream)
{
using (MD5 md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "");
}
}
public static string Sha1HashFile(Stream stream)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(stream)).Replace("-", "");
}
}
public static string Sha1HashFile2(byte[] bytes)
{
string file = "C:\\CUWCDFileStorage\\temp\\test2.png";
File.WriteAllBytes(file, bytes);
return Sha1HashFile(File.OpenRead(file));
}
See answer below for explanation of the problem.
The problem is that the stream is being read to the end when hashing the first way. That is causing the second hash to be wrong. Because of that, I need to either reopen a stream for the second hash or rewind the stream to the beginning before hashing the second way. Here is the solution:
static void Main(string[] args)
{
string file = "C:\\CUWCDFileStorage\\temp\\test.png";
var bytes = File.ReadAllBytes(file);
using (var stream = File.Open(file, FileMode.Open))
{
Console.WriteLine(Md5HashFile(bytes));
Console.WriteLine(Md5HashFile(stream));
}
using (var stream = File.Open(file, FileMode.Open))
{
Console.WriteLine(Sha1HashFile(bytes));
Console.WriteLine(Sha1HashFile(stream));
Console.WriteLine(Sha1HashFile2(bytes));
}
Console.Read();
}
public static string Md5HashFile(byte[] file)
{
using (MD5 md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(file)).Replace("-", "");
}
}
public static string Sha1HashFile(byte[] file)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", "");
}
}
public static string Md5HashFile(Stream stream)
{
using (MD5 md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "");
}
}
public static string Sha1HashFile(Stream stream)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
return BitConverter.ToString(sha1.ComputeHash(stream)).Replace("-", "");
}
}
public static string Sha1HashFile2(byte[] bytes)
{
string file = "C:\\CUWCDFileStorage\\temp\\test2.png";
File.WriteAllBytes(file, bytes);
return Sha1HashFile(File.OpenRead(file));
}

OpenPGP encryption with BouncyCastle

I have been trying to put together an in-memory public-key encryption infrastructure using OpenPGP via Bouncy Castle. One of our vendors uses OpenPGP public key encryption to encrypt all their feeds, and requires us to do the same, so I'm stuck with the technology and the implementation. So now I'm coding an OpenPGP encryption/ decryption toolkit for automating these feeds.
The examples at bouncycastle.org inexplicably default to writing encrypted data to and collecting keys from a file system; this is not what I want to do, so I've been trying to get everything stream-based.
I have gotten to the point where I can actually get my code to compile and run, but my encrypted payload is empty. I think I'm missing something silly, but after several days of trying this and that, I have lost the ability to objectively examine this.
My utility class contains these methods:
public static PgpPublicKey ImportPublicKey(
this Stream publicIn)
{
var pubRings =
new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(publicIn)).GetKeyRings().OfType<PgpPublicKeyRing>();
var pubKeys = pubRings.SelectMany(x => x.GetPublicKeys().OfType<PgpPublicKey>());
var pubKey = pubKeys.FirstOrDefault();
return pubKey;
}
public static Stream Streamify(this string theString, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
var stream = new MemoryStream(encoding.GetBytes(theString));
return stream;
}
public static string Stringify(this Stream theStream,
Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var reader = new StreamReader(theStream, encoding))
{
return reader.ReadToEnd();
}
}
public static byte[] ReadFully(this Stream stream)
{
if (!stream.CanRead) throw new ArgumentException("This is not a readable stream.");
var buffer = new byte[32768];
using (var ms = new MemoryStream())
{
while (true)
{
var read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public static void PgpEncrypt(
this Stream toEncrypt,
Stream outStream,
PgpPublicKey encryptionKey,
bool armor = true,
bool verify = true,
CompressionAlgorithmTag compressionAlgorithm = CompressionAlgorithmTag.Zip)
{
if (armor) outStream = new ArmoredOutputStream(outStream);
var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
outStream = compressor.Open(outStream);
var data = toEncrypt.ReadFully();
var encryptor = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, verify, new SecureRandom());
encryptor.AddMethod(encryptionKey);
outStream = encryptor.Open(outStream, data.Length);
outStream.Write(data, 0, data.Length);
}
My test method looks like this:
private static void EncryptMessage()
{
var pubKey = #"<public key text>";
var clearText = "This is an encrypted message. There are many like it but this one is cryptic.";
using (var stream = pubKey.Streamify())
{
var key = stream.ImportPublicKey();
using (var clearStream = clearText.Streamify())
using (var cryptoStream = new MemoryStream())
{
clearStream.PgpEncrypt(cryptoStream,key);
cryptoStream.Position = 0;
Console.WriteLine(cryptoStream.Stringify());
Console.WriteLine("Press any key to continue.");
}
}
Console.ReadKey();
}
The result I get looks like this:
-----BEGIN PGP MESSAGE-----
Version: BCPG C# v1.7.4114.6378
Press any key to continue.
Can someone tell me what I am doing wrong?
OK, I managed to get this working. There were several problems with this implementation. One problem was that certain things had to be done in order. Here is what seems to need to happen:
The raw data needs to be put into a PgpLiteralData object
The literal data needs to be encrypted.
The encrypted data needs to be compressed.
The compressed data (optionally) needs to be armored.
The underlying streams need to be closed in order of usage.
There should be a more elegant way to do this, but the streams used by the BouncyCastle library are all frustratingly one-way, and at several points, I needed to convert the stream to a byte array to get another part to work. I include the code I used and independently verified; if someone has a verifyably better way of doing this, I would be quite interested.
public static class OpenPgpUtility
{
public static void ExportKeyPair(
Stream secretOut,
Stream publicOut,
AsymmetricKeyParameter publicKey,
AsymmetricKeyParameter privateKey,
string identity,
char[] passPhrase,
bool armor)
{
if (armor)
{
secretOut = new ArmoredOutputStream(secretOut);
}
var secretKey = new PgpSecretKey(
PgpSignature.DefaultCertification,
PublicKeyAlgorithmTag.RsaGeneral,
publicKey,
privateKey,
DateTime.UtcNow,
identity,
SymmetricKeyAlgorithmTag.Cast5,
passPhrase,
null,
null,
new SecureRandom()
);
secretKey.Encode(secretOut);
if (armor)
{
secretOut.Close();
publicOut = new ArmoredOutputStream(publicOut);
}
var key = secretKey.PublicKey;
key.Encode(publicOut);
if (armor)
{
publicOut.Close();
}
}
public static PgpPublicKey ImportPublicKey(
this Stream publicIn)
{
var pubRings =
new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(publicIn)).GetKeyRings().OfType<PgpPublicKeyRing>();
var pubKeys = pubRings.SelectMany(x => x.GetPublicKeys().OfType<PgpPublicKey>());
var pubKey = pubKeys.FirstOrDefault();
return pubKey;
}
public static PgpSecretKey ImportSecretKey(
this Stream secretIn)
{
var secRings =
new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(secretIn)).GetKeyRings().OfType<PgpSecretKeyRing>();
var secKeys = secRings.SelectMany(x => x.GetSecretKeys().OfType<PgpSecretKey>());
var secKey = secKeys.FirstOrDefault();
return secKey;
}
public static Stream Streamify(this string theString, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
var stream = new MemoryStream(encoding.GetBytes(theString));
return stream;
}
public static string Stringify(this Stream theStream,
Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var reader = new StreamReader(theStream, encoding))
{
return reader.ReadToEnd();
}
}
public static byte[] ReadFully(this Stream stream, int position = 0)
{
if (!stream.CanRead) throw new ArgumentException("This is not a readable stream.");
if (stream.CanSeek) stream.Position = 0;
var buffer = new byte[32768];
using (var ms = new MemoryStream())
{
while (true)
{
var read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public static void PgpEncrypt(
this Stream toEncrypt,
Stream outStream,
PgpPublicKey encryptionKey,
bool armor = true,
bool verify = false,
CompressionAlgorithmTag compressionAlgorithm = CompressionAlgorithmTag.Zip)
{
var encryptor = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, verify, new SecureRandom());
var literalizer = new PgpLiteralDataGenerator();
var compressor = new PgpCompressedDataGenerator(compressionAlgorithm);
encryptor.AddMethod(encryptionKey);
//it would be nice if these streams were read/write, and supported seeking. Since they are not,
//we need to shunt the data to a read/write stream so that we can control the flow of data as
//we go.
using (var stream = new MemoryStream()) // this is the read/write stream
using (var armoredStream = armor ? new ArmoredOutputStream(stream) : stream as Stream)
using (var compressedStream = compressor.Open(armoredStream))
{
//data is encrypted first, then compressed, but because of the one-way nature of these streams,
//other "interim" streams are required. The raw data is encapsulated in a "Literal" PGP object.
var rawData = toEncrypt.ReadFully();
var buffer = new byte[1024];
using (var literalOut = new MemoryStream())
using (var literalStream = literalizer.Open(literalOut, 'b', "STREAM", DateTime.UtcNow, buffer))
{
literalStream.Write(rawData, 0, rawData.Length);
literalStream.Close();
var literalData = literalOut.ReadFully();
//The literal data object is then encrypted, which flows into the compressing stream and
//(optionally) into the ASCII armoring stream.
using (var encryptedStream = encryptor.Open(compressedStream, literalData.Length))
{
encryptedStream.Write(literalData, 0, literalData.Length);
encryptedStream.Close();
compressedStream.Close();
armoredStream.Close();
//the stream processes are now complete, and our read/write stream is now populated with
//encrypted data. Convert the stream to a byte array and write to the out stream.
stream.Position = 0;
var data = stream.ReadFully();
outStream.Write(data, 0, data.Length);
}
}
}
}
}
My test method looked like this:
private static void EncryptMessage()
{
var pubKey = #"<public key text here>";
var clearText = #"<message text here>";
using (var stream = pubKey.Streamify())
{
var key = stream.ImportPublicKey();
using (var clearStream = clearText.Streamify())
using (var cryptoStream = new MemoryStream())
{
clearStream.PgpEncrypt(cryptoStream, key);
cryptoStream.Position = 0;
var cryptoString = cryptoStream.Stringify();
Console.WriteLine(cryptoString);
Console.WriteLine("Press any key to continue.");
}
}
Console.ReadKey();
}
Since someone asked, my decryption algorithm looked like this:
public static Stream PgpDecrypt(
this Stream encryptedData,
string armoredPrivateKey,
string privateKeyPassword,
Encoding armorEncoding = null)
{
armorEncoding = armorEncoding ?? Encoding.UTF8;
var stream = PgpUtilities.GetDecoderStream(encryptedData);
var layeredStreams = new List<Stream> { stream }; //this is to clean up/ dispose of any layered streams.
var dataObjectFactory = new PgpObjectFactory(stream);
var dataObject = dataObjectFactory.NextPgpObject();
Dictionary<long, PgpSecretKey> secretKeys;
using (var privateKeyStream = armoredPrivateKey.Streamify(armorEncoding))
{
var secRings =
new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream)).GetKeyRings()
.OfType<PgpSecretKeyRing>();
var pgpSecretKeyRings = secRings as PgpSecretKeyRing[] ?? secRings.ToArray();
if (!pgpSecretKeyRings.Any()) throw new ArgumentException("No secret keys found.");
secretKeys = pgpSecretKeyRings.SelectMany(x => x.GetSecretKeys().OfType<PgpSecretKey>())
.ToDictionary(key => key.KeyId, value => value);
}
while (!(dataObject is PgpLiteralData) && dataObject != null)
{
try
{
var compressedData = dataObject as PgpCompressedData;
var listedData = dataObject as PgpEncryptedDataList;
//strip away the compression stream
if (compressedData != null)
{
stream = compressedData.GetDataStream();
layeredStreams.Add(stream);
dataObjectFactory = new PgpObjectFactory(stream);
}
//strip the PgpEncryptedDataList
if (listedData != null)
{
var encryptedDataList = listedData.GetEncryptedDataObjects()
.OfType<PgpPublicKeyEncryptedData>().First();
var decryptionKey = secretKeys[encryptedDataList.KeyId]
.ExtractPrivateKey(privateKeyPassword.ToCharArray());
stream = encryptedDataList.GetDataStream(decryptionKey);
layeredStreams.Add(stream);
dataObjectFactory = new PgpObjectFactory(stream);
}
dataObject = dataObjectFactory.NextPgpObject();
}
catch (Exception ex)
{
//Log exception here.
throw new PgpException("Failed to strip encapsulating streams.", ex);
}
}
foreach (var layeredStream in layeredStreams)
{
layeredStream.Close();
layeredStream.Dispose();
}
if (dataObject == null) return null;
var literalData = (PgpLiteralData)dataObject;
var ms = new MemoryStream();
using (var clearData = literalData.GetInputStream())
{
Streams.PipeAll(clearData, ms);
}
ms.Position = 0;
return ms;
}

C# UTF8 Decoding, returning bytes/numbers instead of string

I've having an issue decoding a file using an UTF8Encoder.
I am reading text from a file which I have encoded with UTF8 (String > Byte)
See the following method.
public static void Encode(string Path)
{
string text;
Byte[] bytes;
using (StreamReader sr = new StreamReader(Path))
{
text = sr.ReadToEnd();
UTF8Encoding Encoding = new UTF8Encoding();
bytes = Encoding.GetBytes(text);
sr.Close();
}
using (StreamWriter sw = new StreamWriter(Path))
{
foreach (byte b in bytes)
sw.Write(b.ToString());
sw.Close();
}
}
I then decode it using the method
public static String Decode(string Path)
{
String text;
Byte[] bytes;
using (StreamReader sr = new StreamReader(Path))
{
text = sr.ReadToEnd();
UTF8Encoding Encoding = new UTF8Encoding();
bytes = Encoding.GetBytes(text);
text = Encoding.GetString(bytes);
return text;
}
}
But instead of decoding the byte to have it come back to text, it just returns it as a string of numbers. I can't see what I am doing wrong as I don't really have much experience with this.
EDIT: To clarify what I'm trying to achieve. I'm trying to have a text file save the text as bytes, rather than chars/numbers. This is to provide a very simple encryption to the files, that so you can't modify them, unless you know what you're doing. The Decode function is then used to read the text (bytes) from the file and make them in to readable text. I hope this clarified what I'm trying to achieve.
PS: Sry for no comments, but I think it's short enough to be understandable
What exactly are you trying to achieve? UTF-8 (and all other Encodings) is a method to converting strings to byte arrays (text to raw data) and vice versa. StreamReader and StreamWriter are used to read/write strings from/to files. No need to re-encode anything there. Just using reader.ReadToEnd() will return the correct string.
Your piece of code seems to attempt to write a file containing a list of numbers (as a readable, textual representation) corresponding to UTF-8 bytes of the given text. OK. Even though this is very strange idea (I hope you are not trying to do anything like “encryption” with that.), this is definitely possible, if that’s really what you want to do. But you need to separate the readable numbers somehow, e.g. by newlines, and parse it when reading them back:
public static void Encode(string path)
{
byte[] bytes;
using (var sr = new StreamReader(path))
{
var text = sr.ReadToEnd();
bytes = Encoding.UTF8.GetBytes(text);
}
using (var sw = new StreamWriter(path))
{
foreach (byte b in bytes)
{
sw.WriteLine(b);
}
}
}
public static void Decode(string path)
{
var data = new List<byte>();
using (var sr = new StreamReader(path))
{
string line;
while((line = sr.ReadLine()) != null)
data.Add(Byte.Parse(line));
}
using (var sw = new StreamWriter(path))
{
sw.Write(Encoding.UTF8.GetString(data.ToArray()));
}
}
This code will decode encrypted string to text, it worked on my side.
public static String Decode(string Path)
{
String text;
using (StreamReader sr = new StreamReader(Path))
{
text = st.ReadToEnd();
byte[] bytes = Convert.FromBase64String(text);
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
System.Text.Decoder decoder = encoder.GetDecoder();
int count = decoder.GetCharCount(bytes, 0, bytes.Length);
char[] arr = new char[count];
decoder.GetChars(bytes, 0, bytes.Length, arr, 0);
text= new string(arr);
return text;
}
}
The StreamReader class will handle decoding for you, so your Decode() method can be as simple as this:
public static string Decode(string path)
{
// This StreamReader constructor defaults to UTF-8
using (StreamReader reader = new StreamReader(path))
return reader.ReadToEnd();
}
I'm not sure what your Encode() method is supposed to do, since the intent seems to be to read a file as UTF-8 and then write the text back to the exact same file as UTF-8. Something like this might make more sense:
public static void Encode(string path, string text)
{
// This StreamWriter constructor defaults to UTF-8
using (StreamWriter writer = new StreamWriter(path))
writer.Write(text);
}

Categories

Resources