<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />

PgpUtilities

public sealed class PgpUtilities
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.IO; using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Org.BouncyCastle.Bcpg.OpenPgp { public sealed class PgpUtilities { private static readonly IDictionary<string, HashAlgorithmTag> NameToHashID = CreateNameToHashID(); private static readonly IDictionary<DerObjectIdentifier, string> OidToName = CreateOidToName(); private const int ReadAhead = 60; private static IDictionary<string, HashAlgorithmTag> CreateNameToHashID() { return new Dictionary<string, HashAlgorithmTag>(StringComparer.OrdinalIgnoreCase) { { "sha1", HashAlgorithmTag.Sha1 }, { "sha224", HashAlgorithmTag.Sha224 }, { "sha256", HashAlgorithmTag.Sha256 }, { "sha384", HashAlgorithmTag.Sha384 }, { "sha512", HashAlgorithmTag.Sha512 }, { "ripemd160", HashAlgorithmTag.RipeMD160 }, { "rmd160", HashAlgorithmTag.RipeMD160 }, { "md2", HashAlgorithmTag.MD2 }, { "tiger", HashAlgorithmTag.Tiger192 }, { "haval", HashAlgorithmTag.Haval5pass160 }, { "md5", HashAlgorithmTag.MD5 } }; } private static IDictionary<DerObjectIdentifier, string> CreateOidToName() { return new Dictionary<DerObjectIdentifier, string> { { EdECObjectIdentifiers.id_X25519, "Curve25519" }, { EdECObjectIdentifiers.id_Ed25519, "Ed25519" }, { SecObjectIdentifiers.SecP256r1, "NIST P-256" }, { SecObjectIdentifiers.SecP384r1, "NIST P-384" }, { SecObjectIdentifiers.SecP521r1, "NIST P-521" } }; } public static MPInteger[] DsaSigToMpi(byte[] encoding) { DerInteger instance2; DerInteger instance3; try { Asn1Sequence instance = Asn1Sequence.GetInstance(encoding); instance2 = DerInteger.GetInstance(instance[0]); instance3 = DerInteger.GetInstance(instance[1]); } catch (Exception innerException) { throw new PgpException("exception encoding signature", innerException); } return new MPInteger[2] { new MPInteger(instance2.Value), new MPInteger(instance3.Value) }; } public static MPInteger[] RsaSigToMpi(byte[] encoding) { return new MPInteger[1] { new MPInteger(new BigInteger(1, encoding)) }; } public static string GetDigestName(HashAlgorithmTag hashAlgorithm) { switch (hashAlgorithm) { case HashAlgorithmTag.Sha1: return "SHA1"; case HashAlgorithmTag.MD2: return "MD2"; case HashAlgorithmTag.MD5: return "MD5"; case HashAlgorithmTag.RipeMD160: return "RIPEMD160"; case HashAlgorithmTag.Sha224: return "SHA224"; case HashAlgorithmTag.Sha256: return "SHA256"; case HashAlgorithmTag.Sha384: return "SHA384"; case HashAlgorithmTag.Sha512: return "SHA512"; default: throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm.ToString()); } } public static int GetDigestIDForName(string name) { if (NameToHashID.TryGetValue(name, out HashAlgorithmTag value)) return (int)value; throw new ArgumentException("unable to map " + name + " to a hash id", "name"); } public static string GetCurveName(DerObjectIdentifier oid) { if (OidToName.TryGetValue(oid, out string value)) return value; return ECNamedCurveTable.GetName(oid); } public static string GetSignatureName(PublicKeyAlgorithmTag keyAlgorithm, HashAlgorithmTag hashAlgorithm) { string str; switch (keyAlgorithm) { case PublicKeyAlgorithmTag.RsaGeneral: case PublicKeyAlgorithmTag.RsaSign: str = "RSA"; break; case PublicKeyAlgorithmTag.Dsa: str = "DSA"; break; case PublicKeyAlgorithmTag.ECDH: str = "ECDH"; break; case PublicKeyAlgorithmTag.ECDsa: str = "ECDSA"; break; case PublicKeyAlgorithmTag.EdDsa: str = "EdDSA"; break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: str = "ElGamal"; break; default: throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm.ToString()); } return GetDigestName(hashAlgorithm) + "with" + str; } public static string GetSymmetricCipherName(SymmetricKeyAlgorithmTag algorithm) { switch (algorithm) { case SymmetricKeyAlgorithmTag.Null: return null; case SymmetricKeyAlgorithmTag.TripleDes: return "DESEDE"; case SymmetricKeyAlgorithmTag.Idea: return "IDEA"; case SymmetricKeyAlgorithmTag.Cast5: return "CAST5"; case SymmetricKeyAlgorithmTag.Blowfish: return "Blowfish"; case SymmetricKeyAlgorithmTag.Safer: return "SAFER"; case SymmetricKeyAlgorithmTag.Des: return "DES"; case SymmetricKeyAlgorithmTag.Aes128: return "AES"; case SymmetricKeyAlgorithmTag.Aes192: return "AES"; case SymmetricKeyAlgorithmTag.Aes256: return "AES"; case SymmetricKeyAlgorithmTag.Twofish: return "Twofish"; case SymmetricKeyAlgorithmTag.Camellia128: return "Camellia"; case SymmetricKeyAlgorithmTag.Camellia192: return "Camellia"; case SymmetricKeyAlgorithmTag.Camellia256: return "Camellia"; default: throw new PgpException("unknown symmetric algorithm: " + algorithm.ToString()); } } public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) { switch (algorithm) { case SymmetricKeyAlgorithmTag.Des: return 64; case SymmetricKeyAlgorithmTag.Idea: case SymmetricKeyAlgorithmTag.Cast5: case SymmetricKeyAlgorithmTag.Blowfish: case SymmetricKeyAlgorithmTag.Safer: case SymmetricKeyAlgorithmTag.Aes128: case SymmetricKeyAlgorithmTag.Camellia128: return 128; case SymmetricKeyAlgorithmTag.TripleDes: case SymmetricKeyAlgorithmTag.Aes192: case SymmetricKeyAlgorithmTag.Camellia192: return 192; case SymmetricKeyAlgorithmTag.Aes256: case SymmetricKeyAlgorithmTag.Twofish: case SymmetricKeyAlgorithmTag.Camellia256: return 256; default: throw new PgpException("unknown symmetric algorithm: " + algorithm.ToString()); } } public static KeyParameter MakeKey(SymmetricKeyAlgorithmTag algorithm, byte[] keyBytes) { return ParameterUtilities.CreateKeyParameter(GetSymmetricCipherName(algorithm), keyBytes); } public static KeyParameter MakeRandomKey(SymmetricKeyAlgorithmTag algorithm, SecureRandom random) { byte[] array = new byte[(GetKeySize(algorithm) + 7) / 8]; random.NextBytes(array); return MakeKey(algorithm, array); } internal static byte[] EncodePassPhrase(char[] passPhrase, bool utf8) { if (passPhrase != null) { if (!utf8) return Strings.ToByteArray(passPhrase); return Encoding.UTF8.GetBytes(passPhrase); } return null; } public static KeyParameter MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, false), true); } public static KeyParameter MakeKeyFromPassPhraseUtf8(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, true), true); } public static KeyParameter MakeKeyFromPassPhraseRaw(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, rawPassPhrase, false); } internal static KeyParameter DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase, bool clearPassPhrase) { byte[] array = new byte[(GetKeySize(algorithm) + 7) / 8]; int num = 0; int num2 = 0; while (num < array.Length) { IDigest digest; if (s2k == null) try { digest = CreateDigest(HashAlgorithmTag.MD5); for (int i = 0; i != num2; i++) { digest.Update(0); } digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); } catch (Exception innerException) { throw new PgpException("can't find MD5 digest", innerException); } else { try { digest = CreateDigest(s2k.HashAlgorithm); } catch (Exception innerException2) { throw new PgpException("can't find S2k digest", innerException2); } for (int j = 0; j != num2; j++) { digest.Update(0); } byte[] iV = s2k.GetIV(); switch (s2k.Type) { case 0: digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); break; case 1: digest.BlockUpdate(iV, 0, iV.Length); digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); break; case 3: { long iterationCount = s2k.IterationCount; digest.BlockUpdate(iV, 0, iV.Length); digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); iterationCount -= iV.Length + rawPassPhrase.Length; while (iterationCount > 0) { if (iterationCount < iV.Length) { digest.BlockUpdate(iV, 0, (int)iterationCount); break; } digest.BlockUpdate(iV, 0, iV.Length); iterationCount -= iV.Length; if (iterationCount < rawPassPhrase.Length) { digest.BlockUpdate(rawPassPhrase, 0, (int)iterationCount); iterationCount = 0; } else { digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); iterationCount -= rawPassPhrase.Length; } } break; } default: throw new PgpException("unknown S2k type: " + s2k.Type.ToString()); } } byte[] array2 = DigestUtilities.DoFinal(digest); if (array2.Length > array.Length - num) Array.Copy(array2, 0, array, num, array.Length - num); else Array.Copy(array2, 0, array, num, array2.Length); num += array2.Length; num2++; } if (clearPassPhrase && rawPassPhrase != null) Array.Clear(rawPassPhrase, 0, rawPassPhrase.Length); return MakeKey(algorithm, array); } public static void WriteFileToLiteralData(Stream output, char fileType, FileInfo file) { using (Stream pOut = new PgpLiteralDataGenerator().Open(output, fileType, file.Name, file.Length, file.LastWriteTime)) PipeFileContents(file, pOut); } public static void WriteFileToLiteralData(Stream output, char fileType, FileInfo file, byte[] buffer) { using (Stream pOut = new PgpLiteralDataGenerator().Open(output, fileType, file.Name, file.LastWriteTime, buffer)) PipeFileContents(file, pOut, buffer.Length); } private static void PipeFileContents(FileInfo file, Stream pOut) { PipeFileContents(file, pOut, Streams.DefaultBufferSize); } private static void PipeFileContents(FileInfo file, Stream pOut, int bufferSize) { using (FileStream source = file.OpenRead()) Streams.CopyTo(source, pOut, bufferSize); } private static bool IsPossiblyBase64(int ch) { if ((ch < 65 || ch > 90) && (ch < 97 || ch > 122) && (ch < 48 || ch > 57) && ch != 43 && ch != 47 && ch != 13) return ch == 10; return true; } public static Stream GetDecoderStream(Stream inputStream) { if (!inputStream.CanSeek) throw new ArgumentException("inputStream must be seek-able", "inputStream"); long position = inputStream.Position; int num = inputStream.ReadByte(); if ((num & 128) != 0) { inputStream.Position = position; return inputStream; } if (!IsPossiblyBase64(num)) { inputStream.Position = position; return new ArmoredInputStream(inputStream); } byte[] array = new byte[60]; int i = 1; int num2 = 1; array[0] = (byte)num; for (; i != 60; i++) { if ((num = inputStream.ReadByte()) < 0) break; if (!IsPossiblyBase64(num)) { inputStream.Position = position; return new ArmoredInputStream(inputStream); } if (num != 10 && num != 13) array[num2++] = (byte)num; } inputStream.Position = position; if (i >= 4) { byte[] array2 = new byte[8]; Array.Copy(array, 0, array2, 0, array2.Length); try { bool hasHeaders = (Base64.Decode(array2)[0] & 128) == 0; return new ArmoredInputStream(inputStream, hasHeaders); } catch (IOException) { throw; } catch (Exception ex2) { throw new IOException(ex2.Message); } } return new ArmoredInputStream(inputStream); } internal static IDigest CreateDigest(HashAlgorithmTag hashAlgorithm) { return DigestUtilities.GetDigest(GetDigestName(hashAlgorithm)); } internal static ISigner CreateSigner(PublicKeyAlgorithmTag publicKeyAlgorithm, HashAlgorithmTag hashAlgorithm, AsymmetricKeyParameter key) { if (publicKeyAlgorithm == PublicKeyAlgorithmTag.EdDsa) { ISigner signer; if (key is Ed25519PrivateKeyParameters || key is Ed25519PublicKeyParameters) signer = new Ed25519Signer(); else { if (!(key is Ed448PrivateKeyParameters) && !(key is Ed448PublicKeyParameters)) throw new InvalidOperationException(); signer = new Ed448Signer(Arrays.EmptyBytes); } return new EdDsaSigner(signer, CreateDigest(hashAlgorithm)); } return SignerUtilities.GetSigner(GetSignatureName(publicKeyAlgorithm, hashAlgorithm)); } internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm) { if ((uint)(encAlgorithm - 7) <= 2) return WrapperUtilities.GetWrapper("AESWRAP"); if ((uint)(encAlgorithm - 11) <= 2) return WrapperUtilities.GetWrapper("CAMELLIAWRAP"); throw new PgpException("unknown wrap algorithm: " + encAlgorithm.ToString()); } internal static byte[] GenerateIV(int length, SecureRandom random) { byte[] array = new byte[length]; random.NextBytes(array); return array; } internal static S2k GenerateS2k(HashAlgorithmTag hashAlgorithm, int s2kCount, SecureRandom random) { byte[] iv = GenerateIV(8, random); return new S2k(hashAlgorithm, iv, s2kCount); } } }