如何在C#中使用Bouncy Castle或其他C#库验证此PGP消息
我所需要做的就是验证下面的消息,但我无法让Bouncy Castle接收数据,并在给定公钥的情况下验证消息。如果它是免费的,我很高兴它能成为其他的库。这将嵌入到我的应用程序中,通过互联网接收数据,因此如果可能的话,我更愿意保留所有托管代码如何在C#中使用Bouncy Castle或其他C#库验证此PGP消息,c#,bouncycastle,pgp,verify,C#,Bouncycastle,Pgp,Verify,我所需要做的就是验证下面的消息,但我无法让Bouncy Castle接收数据,并在给定公钥的情况下验证消息。如果它是免费的,我很高兴它能成为其他的库。这将嵌入到我的应用程序中,通过互联网接收数据,因此如果可能的话,我更愿意保留所有托管代码 -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SCI Version: 1.0 SCI Code: 1 SCI Reason: OK SCI Balance: 0.00050000 -----BEGIN PGP
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
SCI Version: 1.0
SCI Code: 1
SCI Reason: OK
SCI Balance: 0.00050000
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MBC v1.0
iQEcBAEBAgAGBQJOGSuYAAoJEJ+5g06lAnqFkdsH/0NoqQbNvR8ZPe8D4gL4gvff
6K1t2LOt0sQGj+RSPeEbag7ZnVNI65LiES/yie1N6cXMkFgb9/ttjxi9/wlbxD/j
gSkuZ6mT9Oc5ExLsRZq9ygytvVs7Ol7uQm6oxDzJX1JMs0ls2EwJbmmpTEOHn8Av
dGlxdZeh+3RlqHJmOdssQCJ0cw5VXuj5vfP35OYz2zO2+sNg0eCXdR5Ml+2S7n3U
n9VHPEECg72LvpxF/y/nApopXoHpwECXoBwHgyd9QIIw1IJgalyRLDmAJ2WXdROV
ln2Mkt/km3KtBS3h4QL407wi/KhgZ4tFohZupt7zq2zUwtHWOhbL2KSUu939OKk=
=mIjM
-----END PGP SIGNATURE-----
对于那些感兴趣的人,我在BouncyCastle源代码中发现了一个具体任务的示例。您需要下载源代码而不是二进制文件才能获得示例,而且它似乎有所有不同OpenPGP用例的示例。
使用系统;
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
{
/**
* A simple utility class that signs and verifies files.
* <p>
* To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.<br/>
* If -a is specified the output file will be "ascii-armored".</p>
* <p>
* To decrypt: SignedFileProcessor -v fileName publicKeyFile.</p>
* <p>
* <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to
* the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
* will have been used.</p>
* <p>
* <b>Note</b>: the example also makes use of PGP compression. If you are having difficulty Getting it
* to interoperate with other PGP programs try removing the use of compression first.</p>
*/
public sealed class SignedFileProcessor
{
private SignedFileProcessor() {}
/**
* verify the passed in file as being correctly signed.
*/
private static void VerifyFile(
Stream inputStream,
Stream keyIn)
{
inputStream = PgpUtilities.GetDecoderStream(inputStream);
PgpObjectFactory pgpFact = new PgpObjectFactory(inputStream);
PgpCompressedData c1 = (PgpCompressedData) pgpFact.NextPgpObject();
pgpFact = new PgpObjectFactory(c1.GetDataStream());
PgpOnePassSignatureList p1 = (PgpOnePassSignatureList) pgpFact.NextPgpObject();
PgpOnePassSignature ops = p1[0];
PgpLiteralData p2 = (PgpLiteralData) pgpFact.NextPgpObject();
Stream dIn = p2.GetInputStream();
PgpPublicKeyRingBundle pgpRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn));
PgpPublicKey key = pgpRing.GetPublicKey(ops.KeyId);
Stream fos = File.Create(p2.FileName);
ops.InitVerify(key);
int ch;
while ((ch = dIn.ReadByte()) >= 0)
{
ops.Update((byte)ch);
fos.WriteByte((byte) ch);
}
fos.Close();
PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject();
PgpSignature firstSig = p3[0];
if (ops.Verify(firstSig))
{
Console.Out.WriteLine("signature verified.");
}
else
{
Console.Out.WriteLine("signature verification failed.");
}
}
/**
* Generate an encapsulated signed file.
*
* @param fileName
* @param keyIn
* @param outputStream
* @param pass
* @param armor
*/
private static void SignFile(
string fileName,
Stream keyIn,
Stream outputStream,
char[] pass,
bool armor,
bool compress)
{
if (armor)
{
outputStream = new ArmoredOutputStream(outputStream);
}
PgpSecretKey pgpSec = PgpExampleUtilities.ReadSecretKey(keyIn);
PgpPrivateKey pgpPrivKey = pgpSec.ExtractPrivateKey(pass);
PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
foreach (string userId in pgpSec.PublicKey.GetUserIds())
{
PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
spGen.SetSignerUserId(false, userId);
sGen.SetHashedSubpackets(spGen.Generate());
// Just the first one!
break;
}
Stream cOut = outputStream;
PgpCompressedDataGenerator cGen = null;
if (compress)
{
cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);
cOut = cGen.Open(cOut);
}
BcpgOutputStream bOut = new BcpgOutputStream(cOut);
sGen.GenerateOnePassVersion(false).Encode(bOut);
FileInfo file = new FileInfo(fileName);
PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);
FileStream fIn = file.OpenRead();
int ch = 0;
while ((ch = fIn.ReadByte()) >= 0)
{
lOut.WriteByte((byte) ch);
sGen.Update((byte)ch);
}
fIn.Close();
lGen.Close();
sGen.Generate().Encode(bOut);
if (cGen != null)
{
cGen.Close();
}
if (armor)
{
outputStream.Close();
}
}
public static void Main(
string[] args)
{
// TODO provide command-line option to determine whether to use compression in SignFile
if (args[0].Equals("-s"))
{
Stream keyIn, fos;
if (args[1].Equals("-a"))
{
keyIn = File.OpenRead(args[3]);
fos = File.Create(args[2] + ".asc");
SignFile(args[2], keyIn, fos, args[4].ToCharArray(), true, true);
}
else
{
keyIn = File.OpenRead(args[2]);
fos = File.Create(args[1] + ".bpg");
SignFile(args[1], keyIn, fos, args[3].ToCharArray(), false, true);
}
keyIn.Close();
fos.Close();
}
else if (args[0].Equals("-v"))
{
using (Stream fis = File.OpenRead(args[1]),
keyIn = File.OpenRead(args[2]))
{
VerifyFile(fis, keyIn);
}
}
else
{
Console.Error.WriteLine("usage: SignedFileProcessor -v|-s [-a] file keyfile [passPhrase]");
}
}
}
}
使用系统集合;
使用System.IO;
使用Org.BouncyCastle.Bcpg.OpenPgp;
名称空间Org.bounchycastle.Bcpg.OpenPgp.Examples
{
/**
*对文件进行签名和验证的简单实用程序类。
*
*签署文件:SignedFileProcessor-s[-a]fileName secretKey密码短语。
*如果指定了-a,则输出文件将为“ascii铠装”
*
*要解密:SignedFileProcessor-v fileName publicKeyFile
*
*注意:此示例将以静默方式覆盖文件,也不会注意
*文件名中“_CONSOLE”的规范。它还需要一个传递短语
*将被使用
*
*注意:该示例还使用PGP压缩。如果您在获取它时遇到困难
*要与其他PGP程序进行互操作,请先尝试取消使用压缩
*/
公共密封类SignedFileProcessor
{
私有SignedFileProcessor(){}
/**
*验证传入的文件是否已正确签名。
*/
私有静态无效验证文件(
流输入流,
流键控)
{
inputStream=PgpUtilities.GetDecoderStream(inputStream);
PgpObjectFactory pgpFact=新的PgpObjectFactory(inputStream);
PgpCompressedData c1=(PgpCompressedData)pgpFact.nextpgobject();
pgpFact=新的PgpObjectFactory(c1.GetDataStream());
PgpOnePassSignatureList p1=(PgpOnePassSignatureList)pgpFact.nextPGObject();
PgpOnePassSignature ops=p1[0];
PgpLiteralData p2=(PgpLiteralData)pgpFact.nextpgobject();
Stream dIn=p2.GetInputStream();
pgppublickeringbundle pgpRing=新的pgppublickeringbundle(PgpUtilities.GetDecoderStream(keyIn));
PgpPublicKey=pgpRing.GetPublicKey(ops.KeyId);
Stream fos=File.Create(p2.FileName);
操作初始验证(密钥);
int-ch;
而((ch=dIn.ReadByte())>=0)
{
操作更新((字节)ch);
fos.WriteByte((字节)ch);
}
fos.Close();
PgpSignatureList p3=(PgpSignatureList)pgpFact.NextPgpObject();
PgpSignature firstSig=p3[0];
if(运行验证(第一信号))
{
Console.Out.WriteLine(“签名已验证”);
}
其他的
{
Console.Out.WriteLine(“签名验证失败”);
}
}
/**
*生成一个封装的签名文件。
*
*@param文件名
*@param-keyIn
*@param outputStream
*@param pass
*@param装甲
*/
私有静态无效签名文件(
字符串文件名,
流键因,
流输出流,
char[]pass,
布尔盔甲,
bool(压缩)
{
if(装甲)
{
outputStream=新ArmoreOutputStream(outputStream);
}
PgpSecretKey pgpSec=PgpExampleUtilities.ReadSecretKey(keyIn);
PgpPrivateKey pgpPrivKey=pgpSec.ExtractPrivateKey(通过);
PgpSignatureGenerator sGen=新的PgpSignatureGenerator(pgpSec.PublicKey.Algorithm,HashAlgorithmTag.Sha1);
sGen.InitSign(PgpSignature.BinaryDocument,pgpPrivKey);
foreach(pgpSec.PublicKey.GetUserIds()中的字符串userId)
{
PgpSignatureSubpacketGenerator spGen=新的PgpSignatureSubpacketGenerator();
spGen.SetSignerUserId(false,userId);
sGen.SetHashedSubpackets(spGen.Generate());
//就第一个!
打破
}
Stream cOut=outputStream;
PgpCompressedDataGenerator cGen=null;
如果(压缩)
{
cGen=新的PGP压缩数据发生器(CompressionAlgorithmTag.ZLib);
cOut=cGen.Open(cOut);
}
BcpgOutputStream bOut=新的BcpgOutputStream(cOut);
sGen.GenerateOnePassVersion(false).编码(bOut);
FileInfo file=新的FileInfo(文件名);
PgpLiteralDataGenerator lGen=新的PgpLiteralDataGenerator();
Stream lOut=lGen.Open(bOut,PgpLiteralData.Binary,file);
FileStream fIn=file.OpenRead();
int ch=0;
而((ch=fIn.ReadByte())>=0)
{
lOut.WriteByte((字节)ch);
sGen.Update((字节)ch);
}
fIn.Close();
lGen.Close();
sGen.Generate().Encode(bOut);
如果(cGen!=null)
{
cGen.Close();
}
if(装甲)
{
outputStream.Close();
}
}
公共静态真空总管(
字符串[]args)
{
//TODO提供命令行选项以确定是否在SignFile中使用压缩
如果(args[0]。等于(“-
string key = @"-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG C# v1.6.1.0
mQENBFpc87wBCACK5FG6Z70iovzSzQF7OEB/YbKF7SPS1/GZAQhp/2n2G8x5Lxj5
/CKqR8JLj1+222unuWgfqvfny0fLvttt1r6lAH/kqDYMBg26GTbZy93R5BYatBjd
pzYl/lIyKxc/QwDdZm8zNxeUpDSfoe9jVULOg0MiMDtdQupOf6CanlEioXfyf88F
1BLcJyFSckaYieosBw5hnnI+1cZZ3k+4HpDJJslVzngfTPWRibtN5PKff1CKP55E
ME99XkuPDaNL7XZmu7lZSEUN3jJFVydZQrDkvxddihzV4pTgRI3gDAFoJxxIYZX3
JsQAJItlqq8bBsQ+bKPikgAiMySGcEi+ilI5ABEBAAG0GnNoYWxhbWFub3YubWFy
aW5AZ21haWwuY29tiQEcBBABAgAGBQJaXPO8AAoJEBvHdfmVFHzkvHEH/179VAdH
gWRN5HVprsp4vHP3q1CJV9j+fPlQIZU3JEwrM+INxzpfSqZeN4NwB7yoo2NCdCNP
Ndg8zhiuEYM51hNtqU5cwYBcaAbm1so6TSVo8i4nrfN3+oDYEfYPqglNrd1V233J
oyLriwpGkR6RBYMY2q2Re+EFNR1bxUmeE0wnb8FOodRCSh0Wd3Iy9mvmhv5voHIr
aZzgsuifGw1JilSu9+RoC6b1CHb9jUkWQ/odkTvl5/rxA14TKstgoLoSLHktYQfw
le6B8+lPtmODtagWoDEeR/M0zm/wyCOt5wqjjJCgvaipUaA+oiijIYwCpqUBwfm3
DZ9DStGHGVxQQnc=
=s91O
-----END PGP PUBLIC KEY BLOCK-----
";
string message = @"-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
test tes tes ts tse tse t
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v1.0.1
Comment: http://openpgpjs.org
wsBcBAEBCAAQBQJaXP5WCRAbx3X5lRR85AAAUcoH/jtcyWcpTVyXyz/ptWLo
Hx+g51EeeA0Hpq7kZCXu4FuyhNn/QvnvKyt9qegxQoRSZhT37ln8t80NW6sS
B4XVFziq8TBkjPuaYBI/ijbLigdPMEi81PsOpIXx3BXKCt27TLmUVHpFTWPa
u2NQUQl3k3Xc0H1sy1A+jmjfvCyqWxTOU1IY4rlzRKHdp+D1oCz5iKfyfUko
ktAZgqOMx5pWL975YhM793MS8aYqhOdQpeuW401fm18xxwE4x6svSSys+qq8
MdkL/i7YVjUKr/M8SIrGPb/IjKwClM7jfpN+sHv0p/GcQ7J1kmXYUdA6AJp5
Z0vYk4aPcMSlrPwdRX21I9w=
=zXfe
-----END PGP SIGNATURE-----
";
MemoryStream messageStream = new MemoryStream(Encoding.ASCII.GetBytes(message));
MemoryStream keyStream = new MemoryStream(Encoding.ASCII.GetBytes(key));
try {
var msg= VerifyFile(messageStream,
PgpUtilities.GetDecoderStream(keyStream));
// verification passes msg is the content of the message
} catch (Exception e) {
// verification fails
}
private static String VerifyFile(Stream inputStream, Stream keyIn)
{
ArmoredInputStream aIn = new ArmoredInputStream(inputStream);
MemoryStream outStr = new MemoryStream(); // File.Create(resultName);
//
// write out signed section using the local line separator.
// note: trailing white space needs to be removed from the end of
// each line RFC 4880 Section 7.1
//
MemoryStream lineOut = new MemoryStream();
int lookAhead = ReadInputLine(lineOut, aIn);
byte[] lineSep = LineSeparator;
if (lookAhead != -1 && aIn.IsClearText())
{
byte[] line = lineOut.ToArray();
outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
outStr.Write(lineSep, 0, lineSep.Length);
while (lookAhead != -1 && aIn.IsClearText())
{
lookAhead = ReadInputLine(lineOut, lookAhead, aIn);
line = lineOut.ToArray();
outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
outStr.Write(lineSep, 0, lineSep.Length);
}
}
else
{
// a single line file
if (lookAhead != -1)
{
byte[] line = lineOut.ToArray();
outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
outStr.Write(lineSep, 0, lineSep.Length);
}
}
outStr.Flush();
//outStr.Close();
PgpPublicKeyRingBundle pgpRings = new PgpPublicKeyRingBundle(keyIn);
PgpObjectFactory pgpFact = new PgpObjectFactory(aIn);
PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject();
PgpSignature sig = p3[0];
var key = pgpRings.GetPublicKey(sig.KeyId);
if (key == null)
{
throw new Exception("Can't verify the message signature.");
}
sig.InitVerify(key);
//
// read the input, making sure we ignore the last newline.
//
outStr.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(outStr);
string messageContent = reader.ReadToEnd();
outStr.Seek(0, SeekOrigin.Begin);
Stream sigIn = outStr; //File.OpenRead(resultName);
lookAhead = ReadInputLine(lineOut, sigIn);
ProcessLine(sig, lineOut.ToArray());
if (lookAhead != -1)
{
do
{
lookAhead = ReadInputLine(lineOut, lookAhead, sigIn);
sig.Update((byte)'\r');
sig.Update((byte)'\n');
ProcessLine(sig, lineOut.ToArray());
}
while (lookAhead != -1);
}
sigIn.Close();
if (sig.Verify()) {
// signature verified
return messageContent;
} else {
// signature verification failed
throw new Exception("Can't verify the message signature.");
}
}
private static int ReadInputLine(
MemoryStream bOut,
Stream fIn)
{
bOut.SetLength(0);
int lookAhead = -1;
int ch;
while ((ch = fIn.ReadByte()) >= 0)
{
bOut.WriteByte((byte)ch);
if (ch == '\r' || ch == '\n')
{
lookAhead = ReadPassedEol(bOut, ch, fIn);
break;
}
}
return lookAhead;
}
private static int ReadPassedEol(
MemoryStream bOut,
int lastCh,
Stream fIn)
{
int lookAhead = fIn.ReadByte();
if (lastCh == '\r' && lookAhead == '\n')
{
bOut.WriteByte((byte)lookAhead);
lookAhead = fIn.ReadByte();
}
return lookAhead;
}
private static void ProcessLine(
PgpSignature sig,
byte[] line)
{
// note: trailing white space needs to be removed from the end of
// each line for signature calculation RFC 4880 Section 7.1
int length = GetLengthWithoutWhiteSpace(line);
if (length > 0)
{
sig.Update(line, 0, length);
}
}
private static void ProcessLine(
Stream aOut,
PgpSignatureGenerator sGen,
byte[] line)
{
int length = GetLengthWithoutWhiteSpace(line);
if (length > 0)
{
sGen.Update(line, 0, length);
}
aOut.Write(line, 0, line.Length);
}
private static int GetLengthWithoutSeparatorOrTrailingWhitespace(byte[] line)
{
int end = line.Length - 1;
while (end >= 0 && IsWhiteSpace(line[end]))
{
end--;
}
return end + 1;
}
private static bool IsLineEnding(
byte b)
{
return b == '\r' || b == '\n';
}
private static int GetLengthWithoutWhiteSpace(
byte[] line)
{
int end = line.Length - 1;
while (end >= 0 && IsWhiteSpace(line[end]))
{
end--;
}
return end + 1;
}
private static bool IsWhiteSpace(
byte b)
{
return IsLineEnding(b) || b == '\t' || b == ' ';
}
private static int ReadInputLine(
MemoryStream bOut,
int lookAhead,
Stream fIn)
{
bOut.SetLength(0);
int ch = lookAhead;
do
{
bOut.WriteByte((byte)ch);
if (ch == '\r' || ch == '\n')
{
lookAhead = ReadPassedEol(bOut, ch, fIn);
break;
}
}
while ((ch = fIn.ReadByte()) >= 0);
if (ch < 0)
{
lookAhead = -1;
}
return lookAhead;
}
private static byte[] LineSeparator
{
get { return Encoding.ASCII.GetBytes(Environment.NewLine); }
}