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

PemUtilities

sealed class PemUtilities
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.OpenSsl { internal sealed class PemUtilities { private enum PemBaseAlg { AES_128, AES_192, AES_256, BF, DES, DES_EDE, DES_EDE3, RC2, RC2_40, RC2_64 } private enum PemMode { CBC, CFB, ECB, OFB } static PemUtilities() { Enums.GetArbitraryValue<PemBaseAlg>().ToString(); Enums.GetArbitraryValue<PemMode>().ToString(); } private static void ParseDekAlgName(string dekAlgName, out PemBaseAlg baseAlg, out PemMode mode) { if (dekAlgName == "DES-EDE" || dekAlgName == "DES-EDE3") { if (Enums.TryGetEnumValue(dekAlgName, out baseAlg)) { mode = PemMode.ECB; return; } } else { int num = dekAlgName.LastIndexOf('-'); if (num >= 0 && Enums.TryGetEnumValue(dekAlgName.Substring(0, num), out baseAlg) && Enums.TryGetEnumValue(dekAlgName.Substring(num + 1), out mode)) return; } throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); } internal static byte[] Crypt(bool encrypt, ReadOnlySpan<byte> bytes, ReadOnlySpan<char> password, string dekAlgName, ReadOnlySpan<byte> iv) { ParseDekAlgName(dekAlgName, out PemBaseAlg baseAlg, out PemMode mode); string text; switch (mode) { case PemMode.CBC: case PemMode.ECB: text = "PKCS5Padding"; break; case PemMode.CFB: case PemMode.OFB: text = "NoPadding"; break; default: throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); } ReadOnlySpan<byte> salt = iv; string text2; switch (baseAlg) { case PemBaseAlg.AES_128: case PemBaseAlg.AES_192: case PemBaseAlg.AES_256: text2 = "AES"; if (salt.Length > 8) salt = iv.Slice(0, 8).ToArray(); break; case PemBaseAlg.BF: text2 = "BLOWFISH"; break; case PemBaseAlg.DES: text2 = "DES"; break; case PemBaseAlg.DES_EDE: case PemBaseAlg.DES_EDE3: text2 = "DESede"; break; case PemBaseAlg.RC2: case PemBaseAlg.RC2_40: case PemBaseAlg.RC2_64: text2 = "RC2"; break; default: throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); } IBufferedCipher cipher = CipherUtilities.GetCipher(text2 + "/" + mode.ToString() + "/" + text); ICipherParameters parameters = GetCipherParameters(password, baseAlg, salt); if (mode != PemMode.ECB) parameters = new ParametersWithIV(parameters, iv); cipher.Init(encrypt, parameters); int outputSize = cipher.GetOutputSize(bytes.Length); byte[] array = new byte[outputSize]; int num = cipher.DoFinal(bytes, array); if (num < outputSize) array = Arrays.CopyOfRange(array, 0, num); return array; } private static ICipherParameters GetCipherParameters(ReadOnlySpan<char> password, PemBaseAlg baseAlg, ReadOnlySpan<byte> salt) { int keySize; string algorithm; switch (baseAlg) { case PemBaseAlg.AES_128: keySize = 128; algorithm = "AES128"; break; case PemBaseAlg.AES_192: keySize = 192; algorithm = "AES192"; break; case PemBaseAlg.AES_256: keySize = 256; algorithm = "AES256"; break; case PemBaseAlg.BF: keySize = 128; algorithm = "BLOWFISH"; break; case PemBaseAlg.DES: keySize = 64; algorithm = "DES"; break; case PemBaseAlg.DES_EDE: keySize = 128; algorithm = "DESEDE"; break; case PemBaseAlg.DES_EDE3: keySize = 192; algorithm = "DESEDE3"; break; case PemBaseAlg.RC2: keySize = 128; algorithm = "RC2"; break; case PemBaseAlg.RC2_40: keySize = 40; algorithm = "RC2"; break; case PemBaseAlg.RC2_64: keySize = 64; algorithm = "RC2"; break; default: return null; } OpenSslPbeParametersGenerator openSslPbeParametersGenerator = new OpenSslPbeParametersGenerator(); openSslPbeParametersGenerator.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt); return openSslPbeParametersGenerator.GenerateDerivedParameters(algorithm, keySize); } } }