i have a .PEM file that includes public key and a private key for SSL data transfer like this:
-----BEGIN RSA PRIVATE KEY-----
private key data
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
public key data
-----END CERTIFICATE-----
when i want to load the .PEM file by the following code:
X509Certificate2 xx = new X509Certificate2("c:\\myKey.pem");
i get an exception that says: "Cannot find the requested object." , with full stack:
System.Security.Cryptography.CryptographicException was unhandled
Message=Cannot find the requested object.
Source=mscorlib
StackTrace:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._QueryCertFileType(String fileName)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName)
at DLLTest.SSL_Test.test() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 165
at DLLTest.SSL_Test.Run() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 21
at DLLTest.Program.Main(String[] args) in E:\Projects\DLLTest\DLLTest\Program.cs:line 21
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at 开发者_Python百科Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
if i swap place of private key section and public key section, the code works and load data, and i can get just public key info from the object, eg. IssuerName, and its HasPrivateKey is false. why? am i misunderstood and doing wrong something?
There's an article on the Code Project that has all the code you need to do this. It's just a couple of classes so it's a light-weight solution.
To get the bytes for either a certificate or a key from the PEM file the following method will work, regardless of the order of the key and certificate in the file.
byte[] GetBytesFromPEM( string pemString, string section )
{
var header = String.Format("-----BEGIN {0}-----", section);
var footer = String.Format("-----END {0}-----", section);
var start= pemString.IndexOf(header, StringComparison.Ordinal);
if( start < 0 )
return null;
start += header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if( end < 0 )
return null;
return Convert.FromBase64String( pemString.Substring( start, end ) );
}
Load the PEM file into a string and call the method above to get the bytes that represent the certificate. Next you pass the obtained bytes to the constructor of an X509Certificate2 :
var pem = System.IO.File.ReadAllText( "c:\\myKey.pem" );
byte[] certBuffer = GetBytesFromPEM( pem, "CERTIFICATE" );
var certificate = new X509Certificate2( certBuffer );
Loading the (RSA) private key from the PEM file is a bit more complicated but you'll find support for that in the above mentioned article as well using the Crypto.DecodeRsaPrivateKey
method.
AFAIK the .NET framework does not support PEM anywhere.
You can hack around this easily for the X509Certificate
part since you can extract the base64 string between the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- lines, convert it into a byte[]
and create the X509Certificate
from it.
An easy solution is to copy-paste code from Mono.Security's X509Certificate.cs to do this.
Getting the private key is a bit tricky since getting the byte[]
won't be of much help to reconstruct the RSA instance (which we can assume since the PEM header states it's RSA).
This time you better copy-paste from Mono.Security's PKCS8.cs file and sioply call the decode method.
Disclaimer: I'm the main author of the Mono code discussed above and it is all available under the MIT.X11 license
I had the same problem and - for the record - I post here a complete, working code sample (the key is cut for known reasons). It's mostly a compilation of stuff found on the Internet and my home project requirements.
Following code's features
- Loads a PEM certificate ("-----BEGIN CERTIFICATE-----") from openssl that may contain "-----BEGIN RSA PRIVATE KEY-----"
- returns X509Certificate2
- private key for x509 is stored in the machine store (windows feature), with access rule for everyone
- private key cannot be exported from the store
The code:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Security.AccessControl;
namespace Test1
{
public static class Test
{
public static int Main()
{
string pemCertWithPrivateKeyText = @"-----BEGIN CERTIFICATE-----
...
bjEdMBsGA1UEChQUVGV4YXMgQSZNIFV5jZTESMBAGA1UEAxMJVXNlciBOYW1lMSA
...
YXMgQSZNIFV5jZTESMBAGA1e2yX28ERsgBD6xx7mJDrPxkqWyV/a9tCF8W6jGSs=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEow..................
jZMxBWg+imTpbGb+TpR2kxBWctnzFOWRuVYdSQIDAQABAoIBAFSKz/RLtkmZKE1d
....
BWctnzFOWRuVYdSdsf+WDqNxEzrL08SU1w5WuSxIsbxchUvG4
-----END RSA PRIVATE KEY-----
"; // just an example
X509Certificate2 cert = PEMToX509.Convert(pemCertWithPrivateKeyText);
return (cert.HasPrivateKey ? 1 : -1);
}
}
internal static class PEMToX509
{
const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
const string KEY_FOOTER = "-----END RSA PRIVATE KEY-----";
internal static X509Certificate2 Convert(string pem)
{
try
{
byte[] pemCertWithPrivateKey = System.Text.Encoding.ASCII.GetBytes(pem);
RSACryptoServiceProvider rsaPK = GetRSA(pem);
X509Certificate2 cert = new X509Certificate2();
cert.Import(pemCertWithPrivateKey, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
if (rsaPK != null)
{
cert.PrivateKey = rsaPK;
}
return cert;
}
catch
{
return null;
}
}
private static RSACryptoServiceProvider GetRSA(string pem)
{
RSACryptoServiceProvider rsa = null;
if (IsPrivateKeyAvailable(pem))
{
RSAParameters privateKey = DecodeRSAPrivateKey(pem);
SecurityIdentifier everyoneSI = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
CryptoKeyAccessRule rule = new CryptoKeyAccessRule(everyoneSI, CryptoKeyRights.FullControl, AccessControlType.Allow);
CspParameters cspParameters = new CspParameters();
cspParameters.KeyContainerName = "MY_C_NAME";
cspParameters.ProviderName = "Microsoft Strong Cryptographic Provider";
cspParameters.ProviderType = 1;
cspParameters.Flags = CspProviderFlags.UseNonExportableKey | CspProviderFlags.UseMachineKeyStore;
cspParameters.CryptoKeySecurity = new CryptoKeySecurity();
cspParameters.CryptoKeySecurity.SetAccessRule(rule);
rsa = new RSACryptoServiceProvider(cspParameters);
rsa.PersistKeyInCsp = true;
rsa.ImportParameters(privateKey);
}
return rsa;
}
private static bool IsPrivateKeyAvailable(string privateKeyInPEM)
{
return (privateKeyInPEM != null && privateKeyInPEM.Contains(KEY_HEADER)
&& privateKeyInPEM.Contains(KEY_FOOTER));
}
private static RSAParameters DecodeRSAPrivateKey(string privateKeyInPEM)
{
if (IsPrivateKeyAvailable(privateKeyInPEM) == false)
throw new ArgumentException("bad format");
string keyFormatted = privateKeyInPEM;
int cutIndex = keyFormatted.IndexOf(KEY_HEADER);
keyFormatted = keyFormatted.Substring(cutIndex, keyFormatted.Length - cutIndex);
cutIndex = keyFormatted.IndexOf(KEY_FOOTER);
keyFormatted = keyFormatted.Substring(0, cutIndex + KEY_FOOTER.Length);
keyFormatted = keyFormatted.Replace(KEY_HEADER, "");
keyFormatted = keyFormatted.Replace(KEY_FOOTER, "");
keyFormatted = keyFormatted.Replace("\r", "");
keyFormatted = keyFormatted.Replace("\n", "");
keyFormatted = keyFormatted.Trim();
byte[] privateKeyInDER = System.Convert.FromBase64String(keyFormatted);
byte[] paramModulus;
byte[] paramDP;
byte[] paramDQ;
byte[] paramIQ;
byte[] paramE;
byte[] paramD;
byte[] paramP;
byte[] paramQ;
MemoryStream memoryStream = new MemoryStream(privateKeyInDER);
BinaryReader binaryReader = new BinaryReader(memoryStream);
ushort twobytes = 0;
int elements = 0;
byte bt = 0;
try
{
twobytes = binaryReader.ReadUInt16();
if (twobytes == 0x8130)
binaryReader.ReadByte();
else if (twobytes == 0x8230)
binaryReader.ReadInt16();
else
throw new CryptographicException("Wrong data");
twobytes = binaryReader.ReadUInt16();
if (twobytes != 0x0102)
throw new CryptographicException("Wrong data");
bt = binaryReader.ReadByte();
if (bt != 0x00)
throw new CryptographicException("Wrong data");
elements = GetIntegerSize(binaryReader);
paramModulus = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramE = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramD = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramP = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramQ = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramDP = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramDQ = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramIQ = binaryReader.ReadBytes(elements);
EnsureLength(ref paramD, 256);
EnsureLength(ref paramDP, 128);
EnsureLength(ref paramDQ, 128);
EnsureLength(ref paramE, 3);
EnsureLength(ref paramIQ, 128);
EnsureLength(ref paramModulus, 256);
EnsureLength(ref paramP, 128);
EnsureLength(ref paramQ, 128);
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = paramModulus;
rsaParameters.Exponent = paramE;
rsaParameters.D = paramD;
rsaParameters.P = paramP;
rsaParameters.Q = paramQ;
rsaParameters.DP = paramDP;
rsaParameters.DQ = paramDQ;
rsaParameters.InverseQ = paramIQ;
return rsaParameters;
}
finally
{
binaryReader.Close();
}
}
private static int GetIntegerSize(BinaryReader binary)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binary.ReadByte();
if (bt != 0x02)
return 0;
bt = binary.ReadByte();
if (bt == 0x81)
count = binary.ReadByte();
else if (bt == 0x82)
{
highbyte = binary.ReadByte();
lowbyte = binary.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
count = bt;
while (binary.ReadByte() == 0x00)
count -= 1;
binary.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
private static void EnsureLength(ref byte[] data, int desiredLength)
{
if (data == null || data.Length >= desiredLength)
return;
int zeros = desiredLength - data.Length;
byte[] newData = new byte[desiredLength];
Array.Copy(data, 0, newData, zeros, data.Length);
data = newData;
}
}
}
A different approach is to convert the client PEM certificate to the PFX format supported by Windows. This can be done using, for example, openssl, by running:
openssl pkcs12 -export -out cert.pfx -inkey cert.key -in cert.pem -certfile ca.pem
(where "cert.pfx" is the output file, "cert.key" contains the private key, "cert.pem" contains the input certificate, and "ca.pem" contains the certificate of the signer).
You don't.
.NET doesn't support it.
You see, the PEM-encoding does support multiple keys types (e.g. ECDSA).
So if somebody provides you with a way to read a .NET RSA key from a PEM-file, you maybe able to read that RSA-key (if the PEM also has D, P, Q, DP DQ, InverseQ), but that's a long way from reading PEM.
Also the .NET version of RSA only implements RSA based on (Modulus,Exponent and D, P, Q, DP, DQ, InverseQ). A real PEM-encoded RSA key does not need to support parameters D, P, Q, DP, DQ, InverseQ. It can, but it doesn't have to. All RSA really needs is Modulus and Exponent. And a PEM-encoded RSA key containing only modulus and Exponent is a perferctly valid PEM-key, especially when you need interop with Python, which does such things.
However, you can read a PEM-encoded private and public key with BouncyCastle.
See below.
To get these cryptographic keys (of any kind) generically to .NET without adding a dependency on BouncyCastle, your best bet is to read the PEM-files in BouncyCastle, create a PFX-file (which contains private and public key), then read that PFX-file with System.Security.Cryptography.X509Certificates.X509Certificate2. From that certificate, you can then get the private and public key already decoded, assuming they use a cryptographic algorithm of a kind that .NET does support.
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
Oh, and here's how to do the things with 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://learn.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;
}
}
}
I don't know .NET (but Java) but the answer should be the same.
Your pem file contains both the certificate and private key.
This is a usual export in OpenSSL.
To instantiate an object of X509Certificate
in Java you would use only the part of the file that says:
-----BEGIN CERTIFICATE-----
certificate data
-----END CERTIFICATE-----
It should be the same in .NET.
Just load the file and load that part of PEM.
Do the same for private key.
In java you would use the corresponding object i.e. PrivateKey to load it.
Use the appropriate for .NET
I encountered the same issue and found a solution as below:
First convert prkey.pem into prkey.xml with this tool //https://superdry.apphb.com/tools/online-rsa-key-converter
var dataString = "test";
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(dataString);
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.FromXmlString(File.ReadAllText("C:\prkey.xml"));
byte[] signedBytes = provider.SignData(dataToEncrypt, new SHA256CryptoServiceProvider());
textBox3.Text = BitConverter.ToString(signedBytes);
精彩评论