如何使用Bouncy Castle或其他c#库在C#中验证此PGP消息

时间:2011-07-10 04:46:15

标签: c# bouncycastle pgp verify

我需要做的就是验证下面的消息,但是我无法让Bouncy Castle接收数据并给出公钥验证消息。如果它是免费的,我很高兴它成为其他一些使用的Lib。这将嵌入我的应用程序,通过Internet接收数据,所以我希望尽可能保留所有托管代码。

-----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-----

3 个答案:

答案 0 :(得分:4)

对于那些感兴趣的人,我在BouncyCastle源代码中发现了这个确切任务的一个例子。您需要下载源代码而不是二进制文件来获取示例,它似乎有所有不同OpenPGP用例的示例。

答案 1 :(得分:2)

按照Seer的建议来查看this example我终于开始运行消息验证了。

我有方法VerifyFile,它接受​​签名的消息和公钥,并在验证通过后返回消息的内容。例如,它可以像这样使用:

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); }
    }

答案 2 :(得分:1)

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]");
            }
        }
    }
}