Dla @gbbsoft, odpowiedź na pytanie "jak z ecd dostać X509Certificate2":
Robię to "jak matematyk", tzn. sprowadzam zagadnienie do prostszego, którego obsługę już mam zaimplementowaną. Najpierw przekształcam zaszyfrowany PEM na niezaszyfrowany PEM, i ten łączę z certyfikatem w nowa instancję X509Certificate2:
kod (.NET Core 9.0) w głównej procedurze (pkeyText to zawartość pliku *.key, certText to zawartość pliku *.crt):
if (pkeyText != null && pkeyText.Contains("ENCRYPTED")) //jeżeli klucz prywatny jest zaszyfrowany:
{
if (pwd.Length > 0) //Spróbuj odszyfrować podanym hasłem
{
result = X509Certificate2.CreateFromPem(certText); //musimy na chwilę załadować sam certyfikat, by sprawdzić typ jego kluczy:
if (result.GetRSAPublicKey() != null)
pkeyText = DecryptPrivateKey(EncryptionMethodEnum.Rsa, pkeyText, pwd);
else
if (result.GetECDsaPublicKey() != null)
pkeyText = DecryptPrivateKey(EncryptionMethodEnum.ECDsa, pkeyText, pwd);
else
pkeyText = null; //Nieznany typ klucza
}
else //nie podano hasła
pkeyText = null;
}
if (pkeyText == null) return X509Certificate2.CreateFromPem(certText); //Cóż, zwróć certyfikat bez klucza
//OK, mamy i certyfikat, i klucz:
result = X509Certificate2.CreateFromPem(certText, pkeyText);
A tu są procedury deszyfrujące:
private const int BAD_PASSWORD = -2146233087; //msg: "The EncryptedPrivateKeyInfo structure was decoded but was not successfully interpreted,
//the password may be incorrect."
// -2146233296;
//Pomocnicza: zamienia PEM z zaszyfrowanym hasłem kluczem prywatnym na PEM bez zabezpieczeń
private static string? DecryptPrivateKey(EncryptionMethodEnum encryption, string? pkeyText, string pwd)
{
if (pkeyText == null) return null;
else
try
{
byte[] encryptedKey = Convert.FromBase64String(pkeyText.GetPemSection(PemSection.PRIVATE_KEY, asBase64: true) ?? "");
return encryption switch
{
EncryptionMethodEnum.Rsa => DecryptRSAPrivateKey(encryptedKey, pwd),
EncryptionMethodEnum.ECDsa => DecryptECPrivateKey(encryptedKey, pwd),
_ => null,
};
}
catch (CryptographicException ex)
{
if (ex.HResult == BAD_PASSWORD) return null;
else throw;
}
}
//Pomocnicza: odszyfrowuje klucz ECDsa:
//Argumenty:
// encryptedkey: bajty zaszyfrowanego klucza
// pwd: hasło
private static string DecryptECPrivateKey(byte[] encryptedKey, string pwd)
{
var ecd = new ECDsaCng();
ecd.ImportEncryptedPkcs8PrivateKey(Encoding.ASCII.GetBytes(pwd), encryptedKey, out _);
return ecd.ExportPkcs8PrivateKeyPem();
}
//Pomocnicza: odszyfrowuje klucz RSA:
//Argumenty:
// encryptedkey: bajty zaszyfrowanego klucza
// pwd: hasło
private static string DecryptRSAPrivateKey(byte[] encryptedKey, string pwd)
{
var rsa = new RSACng();
rsa.ImportEncryptedPkcs8PrivateKey(Encoding.ASCII.GetBytes(pwd), encryptedKey, out _);
return rsa.ExportPkcs8PrivateKeyPem();
}
Metoda GetPemSection() (moje rozszerzenie klasy string) zwraca w tym przypadku czysty base64 klucza, bez znaczników "----- BEGIN ...", "---- END ..." i znaków nowej linii.




