如何使用Bouncy Castle库在C#中使用PGP密钥签署txt文件

有没有人有一个如何在C#和Bouncy Castle库中使用PGP密钥签署txt文件的示例。 不加密文件,只添加签名。

public void SignFile(String fileName, Stream privateKeyStream, String privateKeyPassword, Stream outStream) { PgpSecretKey pgpSec = ReadSigningSecretKey(privateKeyStream); PgpPrivateKey pgpPrivKey = null; pgpPrivKey = pgpSec.ExtractPrivateKey(privateKeyPassword.ToCharArray()); PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, KeyStore.ParseHashAlgorithm(this.hash.ToString())); sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey); foreach (string userId in pgpSec.PublicKey.GetUserIds()) { PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator(); spGen.SetSignerUserId(false, userId); sGen.SetHashedSubpackets(spGen.Generate()); } CompressionAlgorithmTag compression = PreferredCompression(pgpSec.PublicKey); PgpCompressedDataGenerator cGen = new PgpCompressedDataGenerator(compression); BcpgOutputStream bOut = new BcpgOutputStream(cGen.Open(outStream)); sGen.GenerateOnePassVersion(false).Encode(bOut); FileInfo file = new FileInfo(fileName); FileStream fIn = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator(); Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file); int ch = 0; while ((ch = fIn.ReadByte()) >= 0) { lOut.WriteByte((byte)ch); sGen.Update((byte) ch); } fIn.Close(); sGen.Generate().Encode(bOut); lGen.Close(); cGen.Close(); outStream.Close(); } public PgpSecretKey ReadSigningSecretKey(Stream inStream) // throws IOException, PGPException, WrongPrivateKeyException { PgpSecretKeyRingBundle pgpSec = CreatePgpSecretKeyRingBundle(inStream); PgpSecretKey key = null; IEnumerator rIt = pgpSec.GetKeyRings().GetEnumerator(); while (key == null && rIt.MoveNext()) { PgpSecretKeyRing kRing = (PgpSecretKeyRing)rIt.Current; IEnumerator kIt = kRing.GetSecretKeys().GetEnumerator(); while (key == null && kIt.MoveNext()) { PgpSecretKey k = (PgpSecretKey)kIt.Current; if(k.IsSigningKey) key = k; } } if(key == null) throw new WrongPrivateKeyException("Can't find signing key in key ring."); else return key; } 

DidiSoft的代码以工作方式:

 using Org.BouncyCastle.Bcpg; using Org.BouncyCastle.Bcpg.OpenPgp; using Org.BouncyCastle.Security; namespace BouncyCastleTest.PGP { // http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.bcpg.HashAlgorithmTags // http://stackoverflow.com/questions/6337985/how-to-sign-a-txt-file-with-a-pgp-key-in-c-sharp-using-bouncy-castle-library class SignOnly { public void SignFile(string hashAlgorithm, string fileName, System.IO.Stream privateKeyStream , string privateKeyPassword, System.IO.Stream outStream) { PgpSecretKey pgpSec = ReadSigningSecretKey(privateKeyStream); PgpPrivateKey pgpPrivKey = null; pgpPrivKey = pgpSec.ExtractPrivateKey(privateKeyPassword.ToCharArray()); PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, ParseHashAlgorithm(hashAlgorithm)); sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey); foreach (string userId in pgpSec.PublicKey.GetUserIds()) { PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator(); spGen.SetSignerUserId(false, userId); sGen.SetHashedSubpackets(spGen.Generate()); } CompressionAlgorithmTag compression = PreferredCompression(pgpSec.PublicKey); PgpCompressedDataGenerator cGen = new PgpCompressedDataGenerator(compression); BcpgOutputStream bOut = new BcpgOutputStream(cGen.Open(outStream)); sGen.GenerateOnePassVersion(false).Encode(bOut); System.IO.FileInfo file = new System.IO.FileInfo(fileName); System.IO.FileStream fIn = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator(); System.IO.Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file); int ch = 0; while ((ch = fIn.ReadByte()) >= 0) { lOut.WriteByte((byte)ch); sGen.Update((byte)ch); } fIn.Close(); sGen.Generate().Encode(bOut); lGen.Close(); cGen.Close(); outStream.Close(); } public static PgpSecretKeyRingBundle CreatePgpSecretKeyRingBundle(System.IO.Stream keyInStream) { return new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyInStream)); } public PgpSecretKey ReadSigningSecretKey(System.IO.Stream keyInStream) { PgpSecretKeyRingBundle pgpSec = CreatePgpSecretKeyRingBundle(keyInStream); PgpSecretKey key = null; System.Collections.IEnumerator rIt = pgpSec.GetKeyRings().GetEnumerator(); while (key == null && rIt.MoveNext()) { PgpSecretKeyRing kRing = (PgpSecretKeyRing)rIt.Current; System.Collections.IEnumerator kIt = kRing.GetSecretKeys().GetEnumerator(); while (key == null && kIt.MoveNext()) { PgpSecretKey k = (PgpSecretKey)kIt.Current; if (k.IsSigningKey) key = k; } } if (key == null) throw new System.Exception("Wrong private key - Can't find signing key in key ring."); else return key; } public static string GetDigestName(int hashAlgorithm) { switch ((Org.BouncyCastle.Bcpg.HashAlgorithmTag)hashAlgorithm) { case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha1: return "SHA1"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD2: return "MD2"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD5: return "MD5"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.RipeMD160: return "RIPEMD160"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha256: return "SHA256"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha384: return "SHA384"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha512: return "SHA512"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha224: return "SHA224"; case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Tiger192: return "TIGER"; default: throw new Org.BouncyCastle.Bcpg.OpenPgp.PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm); } } public static Org.BouncyCastle.Bcpg.HashAlgorithmTag ParseHashAlgorithm(string hashAlgorithm) { switch (hashAlgorithm.ToUpper()) { case "SHA1": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha1; case "MD2": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD2; case "MD5": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD5; case "RIPEMD160": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.RipeMD160; case "SHA256": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha256; case "SHA384": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha384; case "SHA512": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha512; case "SHA224": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha224; case "TIGER": return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Tiger192; default: throw new Org.BouncyCastle.Bcpg.OpenPgp.PgpException("unknown hash algorithm name in ParseHashAlgorithm: " + hashAlgorithm); } } public static CompressionAlgorithmTag PreferredCompression(PgpPublicKey publicKey) { return CompressionAlgorithmTag.BZip2; } } } 

BouncyCastle源代码中还有一个示例(包括如何validation):

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

* To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.
* If -a is specified the output file will be "ascii-armored".

*

* To decrypt: SignedFileProcessor -v fileName publicKeyFile.

*

* Note: 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.

*

* Note: 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.

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