-
-
Save therightstuff/aa65356e95f8d0aae888e9f61aa29414 to your computer and use it in GitHub Desktop.
using Org.BouncyCastle.Crypto; | |
using Org.BouncyCastle.Crypto.Parameters; | |
using Org.BouncyCastle.OpenSsl; | |
using Org.BouncyCastle.Security; | |
using System; | |
using System.IO; | |
using System.Security.Cryptography; | |
namespace MyProject.Data.Encryption | |
{ | |
public class RSAKeys | |
{ | |
/// <summary> | |
/// Import OpenSSH PEM private key string into MS RSACryptoServiceProvider | |
/// </summary> | |
/// <param name="pem"></param> | |
/// <returns></returns> | |
public static RSACryptoServiceProvider ImportPrivateKey(string pem) { | |
PemReader pr = new PemReader(new StringReader(pem)); | |
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject(); | |
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private); | |
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams); | |
csp.ImportParameters(rsaParams); | |
return csp; | |
} | |
/// <summary> | |
/// Import OpenSSH PEM public key string into MS RSACryptoServiceProvider | |
/// </summary> | |
/// <param name="pem"></param> | |
/// <returns></returns> | |
public static RSACryptoServiceProvider ImportPublicKey(string pem) { | |
PemReader pr = new PemReader(new StringReader(pem)); | |
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject(); | |
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey); | |
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams); | |
csp.ImportParameters(rsaParams); | |
return csp; | |
} | |
/// <summary> | |
/// Export private (including public) key from MS RSACryptoServiceProvider into OpenSSH PEM string | |
/// slightly modified from https://stackoverflow.com/a/23739932/2860309 | |
/// </summary> | |
/// <param name="csp"></param> | |
/// <returns></returns> | |
public static string ExportPrivateKey(RSACryptoServiceProvider csp) | |
{ | |
StringWriter outputStream = new StringWriter(); | |
if (csp.PublicOnly) throw new ArgumentException("CSP does not contain a private key", "csp"); | |
var parameters = csp.ExportParameters(true); | |
using (var stream = new MemoryStream()) | |
{ | |
var writer = new BinaryWriter(stream); | |
writer.Write((byte)0x30); // SEQUENCE | |
using (var innerStream = new MemoryStream()) | |
{ | |
var innerWriter = new BinaryWriter(innerStream); | |
EncodeIntegerBigEndian(innerWriter, new byte[] { 0x00 }); // Version | |
EncodeIntegerBigEndian(innerWriter, parameters.Modulus); | |
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); | |
EncodeIntegerBigEndian(innerWriter, parameters.D); | |
EncodeIntegerBigEndian(innerWriter, parameters.P); | |
EncodeIntegerBigEndian(innerWriter, parameters.Q); | |
EncodeIntegerBigEndian(innerWriter, parameters.DP); | |
EncodeIntegerBigEndian(innerWriter, parameters.DQ); | |
EncodeIntegerBigEndian(innerWriter, parameters.InverseQ); | |
var length = (int)innerStream.Length; | |
EncodeLength(writer, length); | |
writer.Write(innerStream.GetBuffer(), 0, length); | |
} | |
var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); | |
// WriteLine terminates with \r\n, we want only \n | |
outputStream.Write("-----BEGIN RSA PRIVATE KEY-----\n"); | |
// Output as Base64 with lines chopped at 64 characters | |
for (var i = 0; i < base64.Length; i += 64) | |
{ | |
outputStream.Write(base64, i, Math.Min(64, base64.Length - i)); | |
outputStream.Write("\n"); | |
} | |
outputStream.Write("-----END RSA PRIVATE KEY-----"); | |
} | |
return outputStream.ToString(); | |
} | |
/// <summary> | |
/// Export public key from MS RSACryptoServiceProvider into OpenSSH PEM string | |
/// slightly modified from https://stackoverflow.com/a/28407693 | |
/// </summary> | |
/// <param name="csp"></param> | |
/// <returns></returns> | |
public static string ExportPublicKey(RSACryptoServiceProvider csp) | |
{ | |
StringWriter outputStream = new StringWriter(); | |
var parameters = csp.ExportParameters(false); | |
using (var stream = new MemoryStream()) | |
{ | |
var writer = new BinaryWriter(stream); | |
writer.Write((byte)0x30); // SEQUENCE | |
using (var innerStream = new MemoryStream()) | |
{ | |
var innerWriter = new BinaryWriter(innerStream); | |
innerWriter.Write((byte)0x30); // SEQUENCE | |
EncodeLength(innerWriter, 13); | |
innerWriter.Write((byte)0x06); // OBJECT IDENTIFIER | |
var rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; | |
EncodeLength(innerWriter, rsaEncryptionOid.Length); | |
innerWriter.Write(rsaEncryptionOid); | |
innerWriter.Write((byte)0x05); // NULL | |
EncodeLength(innerWriter, 0); | |
innerWriter.Write((byte)0x03); // BIT STRING | |
using (var bitStringStream = new MemoryStream()) | |
{ | |
var bitStringWriter = new BinaryWriter(bitStringStream); | |
bitStringWriter.Write((byte)0x00); // # of unused bits | |
bitStringWriter.Write((byte)0x30); // SEQUENCE | |
using (var paramsStream = new MemoryStream()) | |
{ | |
var paramsWriter = new BinaryWriter(paramsStream); | |
EncodeIntegerBigEndian(paramsWriter, parameters.Modulus); // Modulus | |
EncodeIntegerBigEndian(paramsWriter, parameters.Exponent); // Exponent | |
var paramsLength = (int)paramsStream.Length; | |
EncodeLength(bitStringWriter, paramsLength); | |
bitStringWriter.Write(paramsStream.GetBuffer(), 0, paramsLength); | |
} | |
var bitStringLength = (int)bitStringStream.Length; | |
EncodeLength(innerWriter, bitStringLength); | |
innerWriter.Write(bitStringStream.GetBuffer(), 0, bitStringLength); | |
} | |
var length = (int)innerStream.Length; | |
EncodeLength(writer, length); | |
writer.Write(innerStream.GetBuffer(), 0, length); | |
} | |
var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); | |
// WriteLine terminates with \r\n, we want only \n | |
outputStream.Write("-----BEGIN PUBLIC KEY-----\n"); | |
for (var i = 0; i < base64.Length; i += 64) | |
{ | |
outputStream.Write(base64, i, Math.Min(64, base64.Length - i)); | |
outputStream.Write("\n"); | |
} | |
outputStream.Write("-----END PUBLIC KEY-----"); | |
} | |
return outputStream.ToString(); | |
} | |
/// <summary> | |
/// https://stackoverflow.com/a/23739932/2860309 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <param name="length"></param> | |
private static void EncodeLength(BinaryWriter stream, int length) | |
{ | |
if (length < 0) throw new ArgumentOutOfRangeException("length", "Length must be non-negative"); | |
if (length < 0x80) | |
{ | |
// Short form | |
stream.Write((byte)length); | |
} | |
else | |
{ | |
// Long form | |
var temp = length; | |
var bytesRequired = 0; | |
while (temp > 0) | |
{ | |
temp >>= 8; | |
bytesRequired++; | |
} | |
stream.Write((byte)(bytesRequired | 0x80)); | |
for (var i = bytesRequired - 1; i >= 0; i--) | |
{ | |
stream.Write((byte)(length >> (8 * i) & 0xff)); | |
} | |
} | |
} | |
/// <summary> | |
/// https://stackoverflow.com/a/23739932/2860309 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <param name="value"></param> | |
/// <param name="forceUnsigned"></param> | |
private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true) | |
{ | |
stream.Write((byte)0x02); // INTEGER | |
var prefixZeros = 0; | |
for (var i = 0; i < value.Length; i++) | |
{ | |
if (value[i] != 0) break; | |
prefixZeros++; | |
} | |
if (value.Length - prefixZeros == 0) | |
{ | |
EncodeLength(stream, 1); | |
stream.Write((byte)0); | |
} | |
else | |
{ | |
if (forceUnsigned && value[prefixZeros] > 0x7f) | |
{ | |
// Add a prefix zero to force unsigned if the MSB is 1 | |
EncodeLength(stream, value.Length - prefixZeros + 1); | |
stream.Write((byte)0); | |
} | |
else | |
{ | |
EncodeLength(stream, value.Length - prefixZeros); | |
} | |
for (var i = prefixZeros; i < value.Length; i++) | |
{ | |
stream.Write(value[i]); | |
} | |
} | |
} | |
public static void runTests(){ | |
Console.WriteLine("Starting RSAKeys tests"); | |
RSACryptoServiceProvider initialProvider = new RSACryptoServiceProvider(2048); | |
String privateKey = RSAKeys.ExportPrivateKey(initialProvider); | |
String publicKey = RSAKeys.ExportPublicKey(initialProvider); | |
Console.WriteLine("-----------------------"); | |
Console.Write("Private Key exported: "); | |
Console.WriteLine(privateKey); | |
Console.WriteLine("-----------------------"); | |
Console.Write("Public Key exported: "); | |
Console.WriteLine(publicKey); | |
RSACryptoServiceProvider importedProvider = RSAKeys.ImportPrivateKey(privateKey); | |
privateKey = RSAKeys.ExportPrivateKey(importedProvider); | |
publicKey = RSAKeys.ExportPublicKey(importedProvider); | |
Console.WriteLine("-----------------------"); | |
Console.Write("Private Key imported/exported: "); | |
Console.WriteLine(privateKey); | |
Console.WriteLine("-----------------------"); | |
Console.Write("Public Key imported/exported: "); | |
Console.WriteLine(publicKey); | |
Console.WriteLine("RSAKeys tests completed"); | |
} | |
} | |
} |
That's part of BouncyCastle.
The name 'DotNetUtilities' does not exist in the current context , can you please tell me what is library for that in uwp app .
The name 'DotNetUtilities' does not exist in the current context
Where did you get this?
Doesn't seem to be in the Nuget Package version, https://github.com/chrishaly/bc-csharp/search?q=DotNetUtilities&unscoped_q=DotNetUtilities but in the master version https://github.com/bcgit/bc-csharp/blob/f18a2dbbc2c1b4277e24a2e51f09cac02eedf1f5/crypto/src/security/DotNetUtilities.cs
Method "RSACryptoServiceProvider ImportPublicKey", Doesn't work
It will be easier, and this way and this option works.
public static RSACryptoServiceProvider ImportPublicKey(string pub) { PemReader pr2 = new PemReader(new StringReader(pub)); AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr2.ReadObject(); RSAParameters rsaParam = DotNetUtilities.ToRSAParameters((RsaKeyParameters)KeyPair.Public); RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams); csp.ImportParameters(rsaParam); Console.WriteLine(csp.ToXmlString(**false**)); return csp; }
Method "RSACryptoServiceProvider ImportPublicKey", Doesn't work
It will be easier, and this way and this option works.
public static RSACryptoServiceProvider ImportPublicKey(string pub) { PemReader pr2 = new PemReader(new StringReader(pub)); AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr2.ReadObject(); RSAParameters rsaParam = DotNetUtilities.ToRSAParameters((RsaKeyParameters)KeyPair.Public); RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams); csp.ImportParameters(rsaParam); Console.WriteLine(csp.ToXmlString(**false**)); return csp; }
I've just added a couple of tests (added to this gist) and run it on VSCode on a Mac using the latest BouncyCastle version from NuGet, works perfectly for me. Could you please provide more context? Also, your code block doesn't let me know which packages you're including, it would be great if you created a gist with the complete class file and posted a link here.
If there's an easier way to do this, I'm all for it!
I apologize for my English:))
In my project, I use the following components:
Microsoft Visual Studio Community 2019 / VisualStudio.16.Release/16.2.5+29306.81
Microsoft.CSharp: v4.0.30319
BouncyCastle.Crypto: 1.8.1.0 / v1.1.4322 (latest version from NuGet)
When I try use your codeblock, this leads to an unhandled exception:
System.InvalidCastException: "Failed to cast object type "Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair" to type "Org.BouncyCastle.Crypto.AsymmetricKeyParameter"."
Thank you for the good job! Hope our efforts help someone else.. ))
Best Regards!
Your English is just fine :)
The error you're receiving is because you're providing the ImportPrivateKey
method a public key, and it must receive a private key. If you are trying to import a public key, you need to use ImportPublicKey
.
you sir, are a lifesaver
you sir, are a lifesaver
Glad to be able to help!
FYI, for anyone who is stuck by not finding the DotNetUtilities
: see this comment: bcgit/bc-csharp#113 (comment). You are probably using the wrong NuGet package.
work like a charm !! 2021 :v Lifesaver!
@ridhoq - thank you!
@ledunguit - great stuff!
Where did you get this?