How do I decode DER-formatted detached signature using BouncyCastle bc-sharp? For PEM-formatted signature I do it like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, string sign)
{
try
{
var signatureFileRawBytes = Convert.FromBase64String(sign);
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception exception)
{
return false;
}
}
On rare ocasions I need to verify DER-formatted signature. It appears I just need to covert string to byte array, like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, string sign)
{
try
{
byte[] signatureFileRawBytes;
try
{
signatureFileRawBytes = Convert.FromBase64String(sign);
}
catch (FormatException)
{
signatureFileRawBytes = Encoding.ASCII.GetBytes(sign);
}
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception exception)
{
return false;
}
}
In this case I get an exception on that line:
var cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), signatureFileRawBytes);
Org.BouncyCastle.Cms.CmsException
HResult=0x80131500
Message=IOException reading content.
Source=BouncyCastle
StackTrace:
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Asn1InputStream aIn)
at Org.BouncyCastle.Cms.CmsUtilities.ReadContentInfo(Stream input)
at Org.BouncyCastle.Cms.CmsSignedData..ctor(CmsProcessable signedContent, Byte[] sigBlock)
at backend.Helpers.CryptoHelper.VerifyDetachedSignature(Byte[] fileRawBytes, String sign) in C:\Projects\[...]\Helpers\CryptoHelper.cs:line 107
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
EndOfStreamException: DEF length 63 object truncated by 2
Any thoughts or suggestions on how to decode DER-signature?
It turned out the problem was not related to BouncyCastle. The problem was that I read binary data to a string variable and lose data. When i passed signature as byte array to VerifyDetachedSignature(byte[] fileRawBytes, byte[] sign) method it worked perfectly like this:
public static bool VerifyDetachedSignature(byte[] fileRawBytes, byte[] sign)
{
try
{
CmsSignedData cms;
try
{
cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), sign);
}
catch (CmsException)
{
var strSign = System.Text.Encoding.ASCII.GetString(sign);
var decodedSignRawBytes = Convert.FromBase64String(strSign);
cms = new CmsSignedData(new CmsProcessableByteArray(fileRawBytes), decodedSignRawBytes);
}
var signers = cms.GetSignerInfos();
var certificates = cms.GetCertificates("Collection");
var signerInfos = signers.GetSigners();
foreach (SignerInformation info in signerInfos)
{
var certList = new ArrayList(certificates.GetMatches(info.SignerID));
var cert = (X509Certificate)certList[0];
if (cert == null) throw new NullReferenceException();
var publicKey = cert.GetPublicKey();
info.Verify(publicKey);
}
return true;
}
catch (Exception)
{
return false;
}
}
Hope this helps someone to not make the same mistake.
Related
I am getting AmazonRekognitionException as below when trying to run CompareFacesResponse, I am stuck, what should I do or check?
Amazon.Rekognition.AmazonRekognitionException: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. ---> Amazon.Runtime.Internal.HttpErrorResponseException: Exception of type 'Amazon.Runtime.Internal.HttpErrorResponseException' was thrown
AWS credentials access key and secret are checked and correct
public static async Task<Tuple<bool, string>> Rekognition_Compare_Faces(string _source, string _target, string _bucketName)
{
const string HOSTNAME = "https://rekognition.ap-southeast-1.amazonaws.com/";
const string ACCESS_KEY = "my_access_key";
const string ACCESS_SECRET = "my_secret_key";
float _similarityThreshold = 70F;
bool _ret = false;
string _confidence = string.Empty;
try
{
AmazonRekognitionConfig _config = new AmazonRekognitionConfig();
_config.ServiceURL = HOSTNAME + _bucketName;
AmazonRekognitionClient _rekognitionClient = new AmazonRekognitionClient(ACCESS_KEY, ACCESS_SECRET, _config);
Amazon.Rekognition.Model.Image _imageSource = new Amazon.Rekognition.Model.Image();
Amazon.Rekognition.Model.Image _imageTarget = new Amazon.Rekognition.Model.Image();
Amazon.Rekognition.Model.S3Object _s3_source = new Amazon.Rekognition.Model.S3Object { Bucket = _bucketName, Name = _source };
Amazon.Rekognition.Model.S3Object _s3_target = new Amazon.Rekognition.Model.S3Object { Bucket = _bucketName, Name = _target };
CompareFacesRequest _compareFacesRequest = new CompareFacesRequest()
{
SourceImage = new Amazon.Rekognition.Model.Image
{
S3Object = new Amazon.Rekognition.Model.S3Object
{
Bucket = HOSTNAME + _bucketName,
Name = _source
}
},
TargetImage = new Amazon.Rekognition.Model.Image
{
S3Object = new Amazon.Rekognition.Model.S3Object
{
Bucket = HOSTNAME + _bucketName,
Name = _target
}
},
SimilarityThreshold = _similarityThreshold
};
// IT THROWN HERE!!
CompareFacesResponse _compareFacesResponse = await _rekognitionClient.CompareFacesAsync(_compareFacesRequest);
// Display results
foreach (CompareFacesMatch match in _compareFacesResponse.FaceMatches)
{
ComparedFace face = match.Face;
BoundingBox position = face.BoundingBox;
_confidence = match.Similarity.ToString(AppSettings.Decimal_Number_Format) + "%";
_ret = true;
}
}
catch (Exception ex) { await ClsMain.SaveLog("AWS.Compare_Faces: " + ex.ToString()); }
finally { }
return await Task.FromResult(new Tuple<bool, string>(_ret, _confidence));
}
has anybody experience on this?
thanks a lot in advance
Regards
Don
I had the same error.
I tried adding RegionEndpoint = RegionEndpoint.EUWest1 to my AmazonRekognitionConfig so it now looks like this:
var config = new AmazonRekognitionConfig
{
ServiceURL = $"https://rekognition.ap-southeast-1.amazonaws.com/{_awsSettings.BucketName}",
RegionEndpoint = RegionEndpoint.EUWest1
};
var client = new AmazonRekognitionClient(_awsSettings.AccessKey, _awsSettings.Secret, config);
This fixed the problem for me.
I want to first encrypt some nodes in Umbraco's content editor. The code below is the one I use for encryption. I use MachineKey.Protect for this.
try
{
MailMessage message1 = new MailMessage();
MailMessage message2 = new MailMessage();
SmtpClient client = new SmtpClient();
string AfsenderEmail = model.Email;
string AfsenderNavn = model.Name;
string toAddress = Umbraco.Content(rootNode.Id).mailDerSendesTil;
message1.From = new MailAddress(toAddress);
message2.From = new MailAddress(toAddress);
message1.Subject = $"{Umbraco.Content(rootNode.Id).overskriftPaaDenMailViFaar}";
message1.Subject = message1.Subject.Replace("AfsenderEmail", AfsenderEmail);
message1.Subject = message1.Subject.Replace("AfsenderNavn", AfsenderNavn);
message1.Body = $"{Umbraco.Content(rootNode.Id).beskedViFaarNaarBeskedenSendes}";
message1.Body = message1.Body.Replace("AfsenderEmail", AfsenderEmail);
message1.Body = message1.Body.Replace("AfsenderNavn", AfsenderNavn);
message1.To.Add(new MailAddress(toAddress));
client.Send(message1);
message2.Subject = $"{Umbraco.Content(rootNode.Id).overskriftPaaMeddelelsenAfsenderenFaar}";
message2.Subject = message2.Subject.Replace("AfsenderEmail", AfsenderEmail);
message2.Subject = message2.Subject.Replace("AfsenderNavn", AfsenderNavn);
message2.Body = $"{Umbraco.Content(rootNode.Id).beskedAfsenderenFaarNaarBeskedenSendes}";
message2.Body = message2.Body.Replace("AfsenderEmail", AfsenderEmail);
message2.Body = message2.Body.Replace("AfsenderNavn", AfsenderNavn);
message2.To.Add(new MailAddress(AfsenderEmail));
client.Send(message2);
var beskederNode = Umbraco.TypedContentAtRoot().FirstOrDefault(x => x.ContentType.Alias.Equals("Besked"));
var encryptName = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Name));
var encryptEmail = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Email));
var encryptMessage = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Message));
string nameEncrypted = Encoding.ASCII.GetString(encryptName);
string emailEncrypted = Encoding.ASCII.GetString(encryptEmail);
string messageEncrypted = Encoding.ASCII.GetString(encryptMessage);
var newContent = contentService.CreateContent(nameEncrypted, beskederNode.Id, "mails");
newContent.SetValue("fra", nameEncrypted);
newContent.SetValue("eMail", emailEncrypted);
newContent.SetValue("besked", messageEncrypted);
var result = contentService.SaveAndPublishWithStatus(newContent);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (System.Exception ex)
{
Log.Error("Contact Form Error", ex);
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
This is where I Try to decrypt my code again. It throws an exception (System.Security.Cryptography.CryptographicException: 'Error occurred during a cryptographic operation.') when I call MachineKey.Unprotect(nameDecrypted) and I cannnot find my mistake. I think it maybe has somethimg to do with my Encoding and Decoding?
private void EditorModelEventManager_SendingContentModel(System.Web.Http.Filters.HttpActionExecutedContext sender, EditorModelEventArgs<Umbraco.Web.Models.ContentEditing.ContentItemDisplay> e)
{
var node = e.Model.Properties.ToList();
if (e.Model.IsChildOfListView && e.Model.ContentTypeAlias == "mails")
{
string nameDecrypt = node.Where(x => x.Alias.ToLower() == "fra").Select(x => x.Value).First().ToString();
Byte[] nameDecrypted = Encoding.ASCII.GetBytes(nameDecrypt);
var name = e.Model.Properties.FirstOrDefault(x => x.Alias.ToLower() == "fra");
Byte[] decryptName = MachineKey.Unprotect(nameDecrypted);
string nameReady = Encoding.ASCII.GetString(decryptName);
name.Value = $"{nameReady}";
}
}
}
}
I found a solution. Instead of using Encoding.ASCII.GetString(), I used Convert.FromBase64String().
I'm working on an application where I will have to sign data (a string) which would then be used later in the application. The problem is, each time I try it, the output comes out as an empty string. the code that does the signing is written below...
private string SignData(string dataToSign)
{
log.Info("About to sign data");
string certPath = BankWebUtil.CertPath;
string certPassword = BankWebUtil.CertPassword;
string result = "";
this.log.InfoFormat("The certificate path is : {0}", certPath);
this.log.InfoFormat("The certificate password is : {0}", certPassword);
X509Certificate2 x509Certificate2 = null;
try
{
this.log.Info("Trying to get the certificate object with password");
x509Certificate2 = new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.Exportable);
if (x509Certificate2 == null)
{
throw new Exception("Trying to get the certificate object with password returned null");
}
}
catch
{
this.log.Info("Trying to get the certificate object with only filename");
x509Certificate2 = new X509Certificate2(certPath);
}
finally
{
try
{
if (x509Certificate2 != null)
{
byte[] hash = new SHA256Managed().ComputeHash(new ASCIIEncoding().GetBytes(dataToSign));
byte[] rgbHash = hash;
string str = CryptoConfig.MapNameToOID("SHA256");
if (x509Certificate2.PrivateKey == null)
{
throw new Exception("Certificate PrivateKey is null");
}
var certifiedRSACryptoServiceProvider = x509Certificate2.PrivateKey as RSACryptoServiceProvider;
RSACryptoServiceProvider defaultRSACryptoServiceProvider = new RSACryptoServiceProvider();
defaultRSACryptoServiceProvider.ImportParameters(certifiedRSACryptoServiceProvider.ExportParameters(true));
byte[] inArray = defaultRSACryptoServiceProvider.SignHash(rgbHash, str);
result = Convert.ToBase64String(inArray);
}
else
{
throw new Exception("Certificate object is null");
}
}
catch (Exception ex)
{
this.log.Error(ex.Message,ex);
}
}
return result;
}
This is how I intend to test the code.
var customerRef = "200937943";
var customerName = "KAWEESI MARTIN";
var customerTel = "256774018257";
var customerType = "POSTPAID";
var vendorTranId = "UMEME280918200001";
var VendorCode = "Vendor Code";
var pPassword = "ECO-TEST";
var paymentDate = "28/09/2018";
var paymentType = "2";
var teller = "899";
var tranAmount = "48445.51";
var tranNarration = "BC|UMEME280918200001|200937943|0001";
var tranType = "Cash";
var digitalSignature = SignData(customerRef + customerName + customerTel + customerType + vendorTranId + VendorCode +
pPassword + paymentDate + paymentType + teller + tranAmount + tranNarration + tranType);
the variable "digitalSignature" comes out as an empty string... please help me!
in c#
public static string HashToString(string message, byte[] key)
{
byte[] b=new HMACSHA512(key).ComputeHash(Encoding.UTF8.GetBytes(message));
return Convert.ToBase64String(b);
}
client.DefaultRequestHeaders.Add("X-Hash", hash);
var encryptedContent = DataMotion.Security.Encrypt(key, Convert.FromBase64String(iv), serializedModel);
var request = client.PostAsync(ApiUrlTextBox.Text,encryptedContent,new JsonMediaTypeFormatter());
in java:
protected String hashToString(String serializedModel, byte[] key) {
String result = null;
Mac sha512_HMAC;
try {
sha512_HMAC = Mac.getInstance("HmacSHA512");
SecretKeySpec secretkey = new SecretKeySpec(key, "HmacSHA512");
sha512_HMAC.init(secretkey);
byte[] mac_data = sha512_HMAC.doFinal(serializedModel.getBytes("UTF-8"));
result = Base64.encodeBase64String(mac_data);
}catch(Exception e){
}
}
o/p: ye+AZPqaKrU14pui4U5gBCiAbegNvLVjzVdGK3rwG9QVzqKfIgyWBDTncORkNND3DA8jPba5xmC7B5OUwZEKlQ==
i have written hashtostring method in java based on c# code. is this currect? (output is different because every time process is dynamic in both cases.)
With different C# encoding
public static string SHA512_ComputeHash(string text, string secretKey)
{
var hash = new StringBuilder(); ;
byte[] secretkeyBytes = Encoding.UTF8.GetBytes(secretKey);
byte[] inputBytes = Encoding.UTF8.GetBytes(text);
using (var hmac = new HMACSHA512(secretkeyBytes))
{
byte[] hashValue = hmac.ComputeHash(inputBytes);
foreach (var theByte in hashValue)
{
hash.Append(theByte.ToString("x2"));
}
}
return hash.ToString();
}
Both java and C# code are giving same result(same hash code). You should check again.
Replace following line in java code at end
result = Base64.getEncoder().encodeToString(mac_data);
In c#
public static string HMACSHA512(this string Key, string TextToHash)
{
string HmacHashed = "";
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("HMACSHA512: Key", "Parameter cannot be empty.");
if (string.IsNullOrEmpty(TextToHash))
throw new ArgumentNullException("HMACSHA512: TextToHash", "Parameter cannot be empty.");
if (Key.Length % 2 != 0 || Key.Trim().Length < 2)
{
throw new ArgumentNullException("HMACSHA512: Key", "Parameter cannot be odd or less than 2 characters.");
}
try
{
using (var HMACSHA512 = new HMACSHA512(Encoding.ASCII.GetBytes(Key)))
{
HmacHashed = BitConverter.ToString(HMACSHA512.ComputeHash(Encoding.ASCII.GetBytes(TextToHash))).Replace("-", string.Empty);
}
return HmacHashed;
}
catch (Exception ex)
{
throw new Exception("HMACSHA512: " + ex.Message);
}
}
I've seen a number of posts, followed a number of tutorials but none seems to work. Sometimes, they make reference to some classes which are not found. Can I be pointed to a place where I can get a simple tutorial showing how to encrypt and decrypt a file.
I'm very new to Pgp and any assistance is welcomed.
I know this question is years old but it is still #1 or #2 in Google for searches related to PGP Decryption using Bouncy Castle. Since it seems hard to find a complete, succinct example I wanted to share my working solution here for decrypting a PGP file. This is simply a modified version of the Bouncy Castle example included with their source files.
using System;
using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities.IO;
namespace PGPDecrypt
{
class Program
{
static void Main(string[] args)
{
DecryptFile(
#"path_to_encrypted_file.pgp",
#"path_to_secret_key.asc",
"your_password_here".ToCharArray(),
"output.txt"
);
}
private 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);
}
}
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 = 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);
}
}
}
private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyID, char[] pass)
{
PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyID);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.ExtractPrivateKey(pass);
}
}
}
I have used PgpCore package which is a wrapper around Portable.BouncyCastle.
It is very clean and simple to use. Multiple examples available here.
How's this:
PartialInputStream during Bouncycastle PGP decryption
Also, the zip contains examples here:
http://www.bouncycastle.org/csharp/
Hope this helps. If you're still stuck, post some more detail about what classes the compiler is complaining about and the community will take a look.
Now, in 2021, Nikhil's answer is probably best, since it abstracts out the need for working with BouncyCastle directly. Go give him an upvote.
If you want to work with BouncyCastle directly for some reason, I've got a modern implementation of Dan's answer, and the examples he's working from, that uses BouncyCastle directly in NET5. Take a look:
using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
Installed is Nuget Package Portable.BouncyCastle 1.8.10.
public class EncryptionService
{
public static void EncryptPGPFile(FileInfo inFile, FileInfo keyFile, FileInfo outFile, bool withIntegrityCheck = false, bool withArmor = false)
{
PgpPublicKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var publicKey = keyRing.GetKeyRings()
.Cast<PgpPublicKeyRing>()
.FirstOrDefault()
?.GetPublicKeys()
.Cast<PgpPublicKey>()
.FirstOrDefault(x => x.IsEncryptionKey);
using var outFileStream = outFile.Open(FileMode.Create);
using var armoredStream = new ArmoredOutputStream(outFileStream);
Stream outStream = withArmor ? armoredStream : outFileStream;
byte[] compressedBytes;
var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
using (var byteStream = new MemoryStream())
{
// Annoyingly, this is necessary. The compressorStream needs to be closed before the byteStream is read from, otherwise
// data will be left in the buffer and not written to the byteStream. It would be nice if compressorStream exposed a "Flush"
// method. - AJS
using (var compressorStream = compressor.Open(byteStream))
{
PgpUtilities.WriteFileToLiteralData(compressorStream, PgpLiteralData.Binary, inFile);
}
compressedBytes = byteStream.ToArray();
};
var encrypter = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
encrypter.AddMethod(publicKey);
using var finalOutputStream = encrypter.Open(outStream, compressedBytes.Length);
finalOutputStream.Write(compressedBytes, 0, compressedBytes.Length);
}
public static void DecryptPGPFile(FileInfo inFile, FileInfo keyFile, string password, FileInfo outFile)
{
using var inputFile = inFile.OpenRead();
using var input = PgpUtilities.GetDecoderStream(inputFile);
var pgpFactory = new PgpObjectFactory(input);
var firstObject = pgpFactory.NextPgpObject();
if (firstObject is not PgpEncryptedDataList)
{
firstObject = pgpFactory.NextPgpObject();
}
PgpPrivateKey keyToUse = null;
PgpSecretKeyRingBundle keyRing = null;
using (var keyStream = keyFile.OpenRead())
{
keyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
}
var encryptedData = ((PgpEncryptedDataList)firstObject).GetEncryptedDataObjects()
.Cast<PgpPublicKeyEncryptedData>()
.FirstOrDefault(x =>
{
var key = keyRing.GetSecretKey(x.KeyId);
if (key != null)
{
keyToUse = key.ExtractPrivateKey(password.ToCharArray());
return true;
}
return false;
});
if (keyToUse == null)
{
throw new PgpException("Cannot find secret key for message.");
}
Stream clearText = encryptedData.GetDataStream(keyToUse);
PgpObject message = new PgpObjectFactory(clearText).NextPgpObject();
if (message is PgpCompressedData data)
{
message = new PgpObjectFactory(inputStream: data.GetDataStream()).NextPgpObject();
}
if (message is PgpLiteralData literalData)
{
using var outputFileStream = outFile.Open(FileMode.Create);
Streams.PipeAll(literalData.GetInputStream(), outputFileStream);
}
else
{
throw new PgpException("message is not encoded correctly.");
}
if (encryptedData.IsIntegrityProtected() && !encryptedData.Verify())
{
throw new Exception("message failed integrity check!");
}
}
}