Skip to content

Instantly share code, notes, and snippets.

@e23z
Last active August 3, 2017 14:43
Show Gist options
  • Save e23z/ebaec2311d5c244b6d04793caa79dc45 to your computer and use it in GitHub Desktop.
Save e23z/ebaec2311d5c244b6d04793caa79dc45 to your computer and use it in GitHub Desktop.
[RSA Keys Import & Export] How to import and export C# generated RSA keys to and from the PEM format. #cryptography #security #rsa #csharp #function
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace myapp {
public static class PEMExporter {
public static string Export(RSA rsa, bool exportPrivateKey) {
var parameters = rsa.ExportParameters(includePrivateParameters : exportPrivateKey);
var key = new StringBuilder();
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, exportPrivateKey ? parameters.D : parameters.Exponent);
EncodeIntegerBigEndian(innerWriter, exportPrivateKey ? parameters.P : parameters.Exponent);
EncodeIntegerBigEndian(innerWriter, exportPrivateKey ? parameters.Q : parameters.Exponent);
EncodeIntegerBigEndian(innerWriter, exportPrivateKey ? parameters.DP : parameters.Exponent);
EncodeIntegerBigEndian(innerWriter, exportPrivateKey ? parameters.DQ : parameters.Exponent);
EncodeIntegerBigEndian(innerWriter, exportPrivateKey ? parameters.InverseQ : parameters.Exponent);
var length = (int) innerStream.Length;
EncodeLength(writer, length);
ArraySegment<byte> innerBuffer = new ArraySegment<byte>();
innerStream.TryGetBuffer(out innerBuffer);
writer.Write(innerBuffer.Array, 0, length);
}
ArraySegment<byte> buffer = new ArraySegment<byte>();
stream.TryGetBuffer(out buffer);
var base64 = Convert.ToBase64String(buffer.Array, 0, (int) stream.Length);
key.AppendLine(exportPrivateKey ? "-----BEGIN RSA PRIVATE KEY-----" : "-----BEGIN PUBLIC KEY-----");
for (var i = 0; i < base64.Length; i += 64) {
key.AppendLine(base64.Substring(i, Math.Min(64, base64.Length - i)));
}
key.AppendLine(exportPrivateKey ? "-----END RSA PRIVATE KEY-----" : "-----END PUBLIC KEY-----");
}
return key.ToString();
}
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));
}
}
}
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]);
}
}
}
}
}
using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Text;
namespace myapp {
public static class PEMLoader {
const string PrivateHeader = "-----BEGIN RSA PRIVATE KEY-----";
const string PrivateFooter = "-----END RSA PRIVATE KEY-----";
const string PublicHeader = "-----BEGIN PUBLIC KEY-----";
const string PublicFooter = "-----END PUBLIC KEY-----";
public static RSA GetRSAFromPEM(string pemStr) {
pemStr = pemStr.Trim();
var isPrivateKey = pemStr.StartsWith(PrivateHeader) && pemStr.EndsWith(PrivateFooter);
var keyBytes = isPrivateKey ? DecodePrivateKey(pemStr) : DecodePublicKey(pemStr);
if (keyBytes == null)
return null;
return ConvertToRSA(keyBytes);
}
static byte[] DecodePublicKey(string publicKey) {
publicKey = publicKey.Trim();
if (!publicKey.StartsWith(PublicHeader) || !publicKey.EndsWith(PublicFooter))
return null;
publicKey = publicKey.Replace(PublicHeader, "").Replace(PublicFooter, "").Trim();
byte[] keyBytes = null;
try { keyBytes = publicKey.FromBase64(); } catch { }
return keyBytes;
}
static byte[] DecodePrivateKey(string privateKey) {
privateKey = privateKey.Trim();
if (!privateKey.StartsWith(PrivateHeader) || !privateKey.EndsWith(PrivateFooter))
return null;
privateKey = privateKey.Replace(PrivateHeader, "").Replace(PrivateFooter, "").Trim();
byte[] keyBytes = null;
try { keyBytes = privateKey.FromBase64(); } catch { }
return keyBytes;
}
static RSA ConvertToRSA(byte[] key) {
RSA rsa = null;
using(var ms = new MemoryStream(key))
using(var br = new BinaryReader(ms)) {
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try {
twobytes = br.ReadUInt16();
if (twobytes == 0x8130) br.ReadByte();
else if (twobytes == 0x8230) br.ReadInt16();
else throw new InvalidDataException();
twobytes = br.ReadUInt16();
if (twobytes != 0x0102) throw new InvalidDataException();
bt = br.ReadByte();
if (bt != 0x00) throw new InvalidDataException();
elems = GetIntegerSize(br);
MODULUS = br.ReadBytes(elems);
elems = GetIntegerSize(br);
E = br.ReadBytes(elems);
elems = GetIntegerSize(br);
D = br.ReadBytes(elems);
elems = GetIntegerSize(br);
P = br.ReadBytes(elems);
elems = GetIntegerSize(br);
Q = br.ReadBytes(elems);
elems = GetIntegerSize(br);
DP = br.ReadBytes(elems);
elems = GetIntegerSize(br);
DQ = br.ReadBytes(elems);
elems = GetIntegerSize(br);
IQ = br.ReadBytes(elems);
rsa = RSA.Create();
rsa.ImportParameters(new RSAParameters {
Modulus = MODULUS,
Exponent = E,
D = D,
P = P,
Q = Q,
DP = DP,
DQ = DQ,
InverseQ = IQ
});
} catch { }
}
return rsa;
}
static int GetIntegerSize(BinaryReader br) {
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = br.ReadByte();
if (bt != 0x02) return 0;
bt = br.ReadByte();
if (bt == 0x81) count = br.ReadByte();
else if (bt == 0x82) {
highbyte = br.ReadByte();
lowbyte = br.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else count = bt;
while (br.ReadByte() == 0x00) { count -= 1; }
br.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment