diff --git a/crypto/src/openssl/MiscPemGenerator.cs b/crypto/src/openssl/MiscPemGenerator.cs
index ada0b84ed..0e918f793 100644
--- a/crypto/src/openssl/MiscPemGenerator.cs
+++ b/crypto/src/openssl/MiscPemGenerator.cs
@@ -128,20 +128,62 @@ namespace Org.BouncyCastle.OpenSsl
return new PemObject(type, encoding);
}
-// private string GetHexEncoded(byte[] bytes)
-// {
-// bytes = Hex.Encode(bytes);
-//
-// char[] chars = new char[bytes.Length];
-//
-// for (int i = 0; i != bytes.Length; i++)
-// {
-// chars[i] = (char)bytes[i];
-// }
-//
-// return new string(chars);
-// }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private static PemObject CreatePemObject(object obj, string algorithm, ReadOnlySpan<char> password,
+ SecureRandom random)
+ {
+ if (obj == null)
+ throw new ArgumentNullException("obj");
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (random == null)
+ throw new ArgumentNullException("random");
+
+ if (obj is AsymmetricCipherKeyPair keyPair)
+ {
+ return CreatePemObject(keyPair.Private, algorithm, password, random);
+ }
+
+ string type = null;
+ byte[] keyData = null;
+ if (obj is AsymmetricKeyParameter akp)
+ {
+ if (akp.IsPrivate)
+ {
+ keyData = EncodePrivateKey(akp, out type);
+ }
+ }
+
+ if (type == null || keyData == null)
+ {
+ // TODO Support other types?
+ throw new PemGenerationException("Object type not supported: " + Platform.GetTypeName(obj));
+ }
+
+
+ string dekAlgName = algorithm.ToUpperInvariant();
+
+ // Note: For backward compatibility
+ if (dekAlgName == "DESEDE")
+ {
+ dekAlgName = "DES-EDE3-CBC";
+ }
+
+ int ivLength = Platform.StartsWith(dekAlgName, "AES-") ? 16 : 8;
+
+ byte[] iv = new byte[ivLength];
+ random.NextBytes(iv);
+
+ byte[] encData = PemUtilities.Crypt(true, keyData, password, dekAlgName, iv);
+
+ var headers = new List<PemHeader>(2);
+ headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
+ headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv).ToUpperInvariant()));
+
+ return new PemObject(type, headers, encData);
+ }
+#else
private static PemObject CreatePemObject(
object obj,
string algorithm,
@@ -201,6 +243,7 @@ namespace Org.BouncyCastle.OpenSsl
return new PemObject(type, headers, encData);
}
+#endif
private static byte[] EncodePrivateKey(
AsymmetricKeyParameter akp,
diff --git a/crypto/src/openssl/PEMUtilities.cs b/crypto/src/openssl/PEMUtilities.cs
index 332768083..4ff340b12 100644
--- a/crypto/src/openssl/PEMUtilities.cs
+++ b/crypto/src/openssl/PEMUtilities.cs
@@ -50,6 +50,84 @@ namespace Org.BouncyCastle.OpenSsl
throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static byte[] Crypt(bool encrypt, ReadOnlySpan<byte> bytes, ReadOnlySpan<char> password,
+ string dekAlgName, ReadOnlySpan<byte> iv)
+ {
+ PemBaseAlg baseAlg;
+ PemMode mode;
+ ParseDekAlgName(dekAlgName, out baseAlg, out mode);
+
+ string padding;
+ switch (mode)
+ {
+ case PemMode.CBC:
+ case PemMode.ECB:
+ padding = "PKCS5Padding";
+ break;
+ case PemMode.CFB:
+ case PemMode.OFB:
+ padding = "NoPadding";
+ break;
+ default:
+ throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
+ }
+
+ string algorithm;
+
+ ReadOnlySpan<byte> salt = iv;
+ switch (baseAlg)
+ {
+ case PemBaseAlg.AES_128:
+ case PemBaseAlg.AES_192:
+ case PemBaseAlg.AES_256:
+ algorithm = "AES";
+ if (salt.Length > 8)
+ {
+ salt = iv[..8].ToArray();
+ }
+ break;
+ case PemBaseAlg.BF:
+ algorithm = "BLOWFISH";
+ break;
+ case PemBaseAlg.DES:
+ algorithm = "DES";
+ break;
+ case PemBaseAlg.DES_EDE:
+ case PemBaseAlg.DES_EDE3:
+ algorithm = "DESede";
+ break;
+ case PemBaseAlg.RC2:
+ case PemBaseAlg.RC2_40:
+ case PemBaseAlg.RC2_64:
+ algorithm = "RC2";
+ break;
+ default:
+ throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
+ }
+
+ string cipherName = algorithm + "/" + mode + "/" + padding;
+ IBufferedCipher cipher = CipherUtilities.GetCipher(cipherName);
+
+ ICipherParameters cParams = GetCipherParameters(password, baseAlg, salt);
+
+ if (mode != PemMode.ECB)
+ {
+ cParams = new ParametersWithIV(cParams, iv);
+ }
+
+ cipher.Init(encrypt, cParams);
+
+ int outputSize = cipher.GetOutputSize(bytes.Length);
+ byte[] output = new byte[outputSize];
+ int length = cipher.DoFinal(bytes, output);
+ if (length < outputSize)
+ {
+ output = Arrays.CopyOfRange(output, 0, length);
+ }
+ return output;
+ }
+#else
internal static byte[] Crypt(
bool encrypt,
byte[] bytes,
@@ -124,7 +202,37 @@ namespace Org.BouncyCastle.OpenSsl
return cipher.DoFinal(bytes);
}
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private static ICipherParameters GetCipherParameters(ReadOnlySpan<char> password, PemBaseAlg baseAlg,
+ ReadOnlySpan<byte> salt)
+ {
+ string algorithm;
+ int keyBits;
+ switch (baseAlg)
+ {
+ case PemBaseAlg.AES_128: keyBits = 128; algorithm = "AES128"; break;
+ case PemBaseAlg.AES_192: keyBits = 192; algorithm = "AES192"; break;
+ case PemBaseAlg.AES_256: keyBits = 256; algorithm = "AES256"; break;
+ case PemBaseAlg.BF: keyBits = 128; algorithm = "BLOWFISH"; break;
+ case PemBaseAlg.DES: keyBits = 64; algorithm = "DES"; break;
+ case PemBaseAlg.DES_EDE: keyBits = 128; algorithm = "DESEDE"; break;
+ case PemBaseAlg.DES_EDE3: keyBits = 192; algorithm = "DESEDE3"; break;
+ case PemBaseAlg.RC2: keyBits = 128; algorithm = "RC2"; break;
+ case PemBaseAlg.RC2_40: keyBits = 40; algorithm = "RC2"; break;
+ case PemBaseAlg.RC2_64: keyBits = 64; algorithm = "RC2"; break;
+ default:
+ return null;
+ }
+
+ OpenSslPbeParametersGenerator pGen = new OpenSslPbeParametersGenerator();
+
+ pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt);
+ return pGen.GenerateDerivedParameters(algorithm, keyBits);
+ }
+#else
private static ICipherParameters GetCipherParameters(
char[] password,
PemBaseAlg baseAlg,
@@ -154,5 +262,6 @@ namespace Org.BouncyCastle.OpenSsl
return pGen.GenerateDerivedParameters(algorithm, keyBits);
}
- }
+#endif
+ }
}
|