你没有。
.NET 不支持它。
你看,PEM 编码确实支持多种密钥类型(例如 ECDSA)。
因此,如果有人为您提供了一种从 PEM 文件中读取 .NET RSA 密钥的方法,您也许能够读取该 RSA 密钥(如果 PEM 也有 D、P、Q、DP DQ、InverseQ),但就是这样距离阅读PEM还有很长的路要走。
此外,.NET 版本的 RSA 仅基于(模数、指数和D、P、Q、DP、DQ、InverseQ)实现 RSA。真正的 PEM 编码 RSA 密钥不需要支持参数 D、P、Q、DP、DQ、InverseQ。它可以,但不是必须的。RSA 真正需要的只是模数和指数。并且仅包含模数和指数的 PEM 编码 RSA 密钥是完全有效的 PEM 密钥,尤其是当您需要与执行此类操作的 Python 互操作时。
但是,您可以使用 BouncyCastle 读取 PEM 编码的私钥和公钥。
见下文。
要在不添加对 BouncyCastle 的依赖的情况下将这些(任何类型的)加密密钥通用地获取到 .NET,最好的办法是读取 BouncyCastle 中的 PEM 文件,创建一个 PFX 文件(包含私钥和公钥),然后读取带有 System.Security.Cryptography.X509Certificates.X509Certificate2 的 PFX 文件。然后,您可以从该证书获取已解码的私钥和公钥,假设它们使用 .NET 支持的加密算法。
namespace SslCertificateGenerator
{
// https://gist.github.com/therightstuff/aa65356e95f8d0aae888e9f61aa29414
public class KeyImportExport
{
// KeyImportExport.GetPemKeyPair
public static PrivatePublicPemKeyPair GetPemKeyPair(Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair)
{
PrivatePublicPemKeyPair result = new PrivatePublicPemKeyPair();
// id_rsa
using (System.IO.TextWriter textWriter = new System.IO.StringWriter())
{
Org.BouncyCastle.OpenSsl.PemWriter pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
pemWriter.WriteObject(keyPair.Private);
pemWriter.Writer.Flush();
result.PrivateKey = textWriter.ToString();
} // End Using textWriter
// id_rsa.pub
using (System.IO.TextWriter textWriter = new System.IO.StringWriter())
{
Org.BouncyCastle.OpenSsl.PemWriter pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
pemWriter.WriteObject(keyPair.Public);
pemWriter.Writer.Flush();
result.PublicKey = textWriter.ToString();
} // End Using textWriter
// // This writes the same as private key, not both
//using (System.IO.TextWriter textWriter = new System.IO.StringWriter())
//{
// Org.BouncyCastle.OpenSsl.PemWriter pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
// pemWriter.WriteObject(keyPair);
// pemWriter.Writer.Flush();
// bothKeys = textWriter.ToString();
//} // End Using textWriter
return result;
} // End Sub GetPemKeyPair
// KeyImportExport.ReadPublicKey
public static Org.BouncyCastle.Crypto.AsymmetricKeyParameter ReadPublicKey(string publicKey)
{
Org.BouncyCastle.Crypto.AsymmetricKeyParameter keyParameter = null;
using (System.IO.TextReader reader = new System.IO.StringReader(publicKey))
{
Org.BouncyCastle.OpenSsl.PemReader pemReader =
new Org.BouncyCastle.OpenSsl.PemReader(reader);
object obj = pemReader.ReadObject();
if ((obj is Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair))
throw new System.ArgumentException("The given publicKey is actually a private key.", "publicKey");
if (!(obj is Org.BouncyCastle.Crypto.AsymmetricKeyParameter))
throw new System.ArgumentException("The given publicKey is not a valid assymetric key.", "publicKey");
keyParameter = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)obj;
}
return keyParameter;
} // End Function ReadPublicKey
public static Org.BouncyCastle.Crypto.AsymmetricKeyParameter ReadPrivateKey(string privateKey)
{
Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair = null;
using (System.IO.TextReader reader = new System.IO.StringReader(privateKey))
{
Org.BouncyCastle.OpenSsl.PemReader pemReader =
new Org.BouncyCastle.OpenSsl.PemReader(reader);
object obj = pemReader.ReadObject();
if (obj is Org.BouncyCastle.Crypto.AsymmetricKeyParameter)
throw new System.ArgumentException("The given privateKey is a public key, not a privateKey...", "privateKey");
if (!(obj is Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair))
throw new System.ArgumentException("The given privateKey is not a valid assymetric key.", "privateKey");
keyPair = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)obj;
} // End using reader
// Org.BouncyCastle.Crypto.AsymmetricKeyParameter priv = keyPair.Private;
// Org.BouncyCastle.Crypto.AsymmetricKeyParameter pub = keyPair.Public;
// Note:
// cipher.Init(false, key);
// !!!
return keyPair.Private;
} // End Function ReadPrivateKey
public static Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair ReadKeyPair(string privateKey)
{
Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair = null;
using (System.IO.TextReader reader = new System.IO.StringReader(privateKey))
{
Org.BouncyCastle.OpenSsl.PemReader pemReader =
new Org.BouncyCastle.OpenSsl.PemReader(reader);
object obj = pemReader.ReadObject();
if (obj is Org.BouncyCastle.Crypto.AsymmetricKeyParameter)
throw new System.ArgumentException("The given privateKey is a public key, not a privateKey...", "privateKey");
if (!(obj is Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair))
throw new System.ArgumentException("The given privateKey is not a valid assymetric key.", "privateKey");
keyPair = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)obj;
}
// Org.BouncyCastle.Crypto.AsymmetricKeyParameter priv = keyPair.Private;
// Org.BouncyCastle.Crypto.AsymmetricKeyParameter pub = keyPair.Public;
// Note:
// cipher.Init(false, key);
// !!!
return keyPair;
} // End Function ReadPrivateKey
public static Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair ReadKeyPairFromFile(string fileName)
{
Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair KeyPair = null;
// Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
using (System.IO.FileStream fs = System.IO.File.OpenRead(fileName))
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(fs))
{
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
KeyPair = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)pemReader.ReadObject();
// System.Security.Cryptography.RSAParameters rsa = Org.BouncyCastle.Security.
// DotNetUtilities.ToRSAParameters((Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters)KeyPair.Private);
} // End Using sr
} // End Using fs
return KeyPair;
} // End Function ImportKeyPair
//public static void ReadPrivateKeyFile(string privateKeyFileName)
//{
// Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters key = null;
// using (System.IO.StreamReader streamReader = System.IO.File.OpenText(privateKeyFileName))
// {
// Org.BouncyCastle.OpenSsl.PemReader pemReader =
// new Org.BouncyCastle.OpenSsl.PemReader(streamReader);
// key = (Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters) pemReader.ReadObject();
// } // End Using streamReader
// // Note:
// // cipher.Init(false, key);
// // !!!
//} // End Function ReadPrivateKey
public Org.BouncyCastle.Crypto.AsymmetricKeyParameter ReadPublicKeyFile(string pemFilename)
{
Org.BouncyCastle.Crypto.AsymmetricKeyParameter keyParameter = null;
using (System.IO.StreamReader streamReader = System.IO.File.OpenText(pemFilename))
{
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(streamReader);
keyParameter = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)pemReader.ReadObject();
} // End Using fileStream
return keyParameter;
} // End Function ReadPublicKey
public static void ExportKeyPair(Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair)
{
string privateKey = null;
using (System.IO.TextWriter textWriter = new System.IO.StringWriter())
{
Org.BouncyCastle.OpenSsl.PemWriter pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
pemWriter.WriteObject(keyPair.Private);
pemWriter.Writer.Flush();
privateKey = textWriter.ToString();
} // End Using textWriter
System.Console.WriteLine(privateKey);
} // End Sub ExportKeyPair
// https://stackoverflow.com/questions/22008337/generating-keypair-using-bouncy-castle
// https://stackoverflow.com/questions/14052485/converting-a-public-key-in-subjectpublickeyinfo-format-to-rsapublickey-format-ja
// https://stackoverflow.com/questions/10963756/get-der-encoded-public-key
// http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory
public static void CerKeyInfo(Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair)
{
Org.BouncyCastle.Asn1.Pkcs.PrivateKeyInfo pkInfo = Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
string privateKey = System.Convert.ToBase64String(pkInfo.GetDerEncoded());
// and following for public:
Org.BouncyCastle.Asn1.X509.SubjectPublicKeyInfo info = Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
string publicKey = System.Convert.ToBase64String(info.GetDerEncoded());
System.Console.WriteLine(privateKey);
System.Console.WriteLine(publicKey);
} // End Sub CerKeyInfo
} // End Class KeyImportExport
} // End Namespace RedmineMailService.CertSSL
哦,这里是如何使用 PFX 做的事情:
namespace SelfSignedCertificateGenerator
{
public class PfxData
{
public Org.BouncyCastle.X509.X509Certificate Certificate;
public Org.BouncyCastle.Crypto.AsymmetricKeyParameter PrivateKey;
}
public class PfxFile
{
// System.Security.Cryptography.X509Certificates.X509Certificate2.Import (string fileName);
// https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2.import?view=netframework-4.7.2
// https://gist.github.com/yutopio/a217a4af63cf6bcf0a530c14c074cf8f
// https://gist.githubusercontent.com/yutopio/a217a4af63cf6bcf0a530c14c074cf8f/raw/42b2f8cb27f6d22b7e22d65da5bbd0f1ce9b2fff/cert.cs
// https://stackoverflow.com/questions/44755155/store-pkcs12-container-pfx-with-bouncycastle
// https://github.com/Worlaf/RSADemo/blob/328692e28e48db92340d55563480c8724d916384/RSADemo_WinForms/frmRsaDemo.cs
public static void Create(
string fileName
, Org.BouncyCastle.X509.X509Certificate certificate
, Org.BouncyCastle.Crypto.AsymmetricKeyParameter privateKey
, string password = "")
{
// create certificate entry
Org.BouncyCastle.Pkcs.X509CertificateEntry certEntry =
new Org.BouncyCastle.Pkcs.X509CertificateEntry(certificate);
string friendlyName = certificate.SubjectDN.ToString();
if (!friendlyName.Contains("obelix", System.StringComparison.InvariantCultureIgnoreCase))
friendlyName = "Skynet Certification Authority";
else
friendlyName = "Coopérative Ménhir Obelix Gmbh & Co. KGaA";
// get bytes of private key.
Org.BouncyCastle.Asn1.Pkcs.PrivateKeyInfo keyInfo = Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
//byte[] keyBytes = keyInfo.ToAsn1Object().GetEncoded();
Org.BouncyCastle.Pkcs.Pkcs12StoreBuilder builder = new Org.BouncyCastle.Pkcs.Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true);
Org.BouncyCastle.Pkcs.Pkcs12Store store = builder.Build();
store.SetCertificateEntry(friendlyName, certEntry);
// create store entry
store.SetKeyEntry(
//keyFriendlyName
friendlyName
, new Org.BouncyCastle.Pkcs.AsymmetricKeyEntry(privateKey)
, new Org.BouncyCastle.Pkcs.X509CertificateEntry[] { certEntry }
);
byte[] pfxBytes = null;
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
// Cert is contained in store
// null: no password, "": an empty passwords
// note: Linux needs empty password on null...
store.Save(stream, password == null ? "".ToCharArray() : password.ToCharArray(), new Org.BouncyCastle.Security.SecureRandom());
// stream.Position = 0;
pfxBytes = stream.ToArray();
} // End Using stream
#if WITH_MS_PFX
WithMsPfx(pfxBytes, fileName, password);
#else
byte[] result = Org.BouncyCastle.Pkcs.Pkcs12Utilities.ConvertToDefiniteLength(pfxBytes);
// this.StoreCertificate(System.Convert.ToBase64String(result));
using (System.IO.BinaryWriter writer = new System.IO.BinaryWriter(System.IO.File.Open(fileName, System.IO.FileMode.Create)))
{
writer.Write(result);
} // End Using writer
#endif
} // End Sub Create
private static void WithMsPfx(byte[] pfxBytes, string fileName, string password)
{
System.Security.Cryptography.X509Certificates.X509Certificate2 convertedCertificate =
new System.Security.Cryptography.X509Certificates.X509Certificate2(pfxBytes,
"", // PW
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.PersistKeySet | System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable);
byte[] bytes = convertedCertificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pfx, password);
System.IO.File.WriteAllBytes(fileName, bytes);
} // End Sub WithMsPfx
public static PfxData Read(string pfxFilePath, string password = "")
{
Org.BouncyCastle.Pkcs.Pkcs12Store store = null;
using (System.IO.Stream pfxStream = System.IO.File.OpenRead(pfxFilePath))
{
store = new Org.BouncyCastle.Pkcs.Pkcs12Store(pfxStream, password.ToCharArray());
}
// System.Console.WriteLine(store);
foreach (string alias in store.Aliases)
{
Org.BouncyCastle.Pkcs.X509CertificateEntry certEntry = store.GetCertificate(alias);
Org.BouncyCastle.X509.X509Certificate cert = certEntry.Certificate;
// Org.BouncyCastle.Crypto.AsymmetricKeyParameter publicKey = cert.GetPublicKey();
// System.Console.WriteLine(publicKey);
// https://7thzero.com/blog/bouncy-castle-convert-a-bouncycastle-asymmetrickeyentry-to-a-.ne
if (store.IsKeyEntry(alias))
{
Org.BouncyCastle.Pkcs.AsymmetricKeyEntry keyEntry = store.GetKey(alias);
Org.BouncyCastle.Crypto.AsymmetricKeyParameter privateKey = keyEntry.Key;
if (privateKey.IsPrivate)
return new PfxData()
{
Certificate = cert,
PrivateKey = privateKey
};
} // End if (store.IsKeyEntry((string)alias))
} // Next alias
return null;
} // End Sub Read
public static System.Security.Cryptography.X509Certificates.X509Certificate2
MicrosoftCertificateFromPfx(string pfxFilePath, string password = "")
{
System.Security.Cryptography.X509Certificates.X509Certificate2 cert =
new System.Security.Cryptography.X509Certificates.X509Certificate2(
pfxFilePath
, password
);
return cert;
}
}
}