Instantly share code, notes, and snippets.
Save valep27/4a720c25b35fff83fbf872516f847863 to your computer and use it in GitHub Desktop.
Import and export RSA Keys between C# and PEM format using BouncyCastle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
usingOrg.BouncyCastle.Crypto; | |
usingOrg.BouncyCastle.Crypto.Parameters; | |
usingOrg.BouncyCastle.OpenSsl; | |
usingOrg.BouncyCastle.Security; | |
usingSystem; | |
usingSystem.IO; | |
usingSystem.Security.Cryptography; | |
namespaceMyProject.Data.Encryption | |
{ | |
publicclassRSAKeys | |
{ | |
/// <summary> | |
/// Import OpenSSH PEM private key string into MS RSACryptoServiceProvider | |
/// </summary> | |
/// <param name="pem"></param> | |
/// <returns></returns> | |
publicstaticRSACryptoServiceProviderImportPrivateKey(stringpem){ | |
PemReaderpr=newPemReader(newStringReader(pem)); | |
AsymmetricCipherKeyPairKeyPair=(AsymmetricCipherKeyPair)pr.ReadObject(); | |
RSAParametersrsaParams=DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private); | |
RSACryptoServiceProvidercsp=newRSACryptoServiceProvider();// cspParams); | |
csp.ImportParameters(rsaParams); | |
returncsp; | |
} | |
/// <summary> | |
/// Import OpenSSH PEM public key string into MS RSACryptoServiceProvider | |
/// </summary> | |
/// <param name="pem"></param> | |
/// <returns></returns> | |
publicstaticRSACryptoServiceProviderImportPublicKey(stringpem){ | |
PemReaderpr=newPemReader(newStringReader(pem)); | |
AsymmetricKeyParameterpublicKey=(AsymmetricKeyParameter)pr.ReadObject(); | |
RSAParametersrsaParams=DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey); | |
RSACryptoServiceProvidercsp=newRSACryptoServiceProvider();// cspParams); | |
csp.ImportParameters(rsaParams); | |
returncsp; | |
} | |
/// <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> | |
publicstaticstringExportPrivateKey(RSACryptoServiceProvidercsp) | |
{ | |
StringWriteroutputStream=newStringWriter(); | |
if(csp.PublicOnly)thrownewArgumentException("CSP does not contain a private key","csp"); | |
varparameters=csp.ExportParameters(true); | |
using(varstream=newMemoryStream()) | |
{ | |
varwriter=newBinaryWriter(stream); | |
writer.Write((byte)0x30);// SEQUENCE | |
using(varinnerStream=newMemoryStream()) | |
{ | |
varinnerWriter=newBinaryWriter(innerStream); | |
EncodeIntegerBigEndian(innerWriter,newbyte[]{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); | |
varlength=(int)innerStream.Length; | |
EncodeLength(writer,length); | |
writer.Write(innerStream.GetBuffer(),0,length); | |
} | |
varbase64=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(vari=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-----"); | |
} | |
returnoutputStream.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> | |
publicstaticstringExportPublicKey(RSACryptoServiceProvidercsp) | |
{ | |
StringWriteroutputStream=newStringWriter(); | |
varparameters=csp.ExportParameters(false); | |
using(varstream=newMemoryStream()) | |
{ | |
varwriter=newBinaryWriter(stream); | |
writer.Write((byte)0x30);// SEQUENCE | |
using(varinnerStream=newMemoryStream()) | |
{ | |
varinnerWriter=newBinaryWriter(innerStream); | |
innerWriter.Write((byte)0x30);// SEQUENCE | |
EncodeLength(innerWriter,13); | |
innerWriter.Write((byte)0x06);// OBJECT IDENTIFIER | |
varrsaEncryptionOid=newbyte[]{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(varbitStringStream=newMemoryStream()) | |
{ | |
varbitStringWriter=newBinaryWriter(bitStringStream); | |
bitStringWriter.Write((byte)0x00);// # of unused bits | |
bitStringWriter.Write((byte)0x30);// SEQUENCE | |
using(varparamsStream=newMemoryStream()) | |
{ | |
varparamsWriter=newBinaryWriter(paramsStream); | |
EncodeIntegerBigEndian(paramsWriter,parameters.Modulus);// Modulus | |
EncodeIntegerBigEndian(paramsWriter,parameters.Exponent);// Exponent | |
varparamsLength=(int)paramsStream.Length; | |
EncodeLength(bitStringWriter,paramsLength); | |
bitStringWriter.Write(paramsStream.GetBuffer(),0,paramsLength); | |
} | |
varbitStringLength=(int)bitStringStream.Length; | |
EncodeLength(innerWriter,bitStringLength); | |
innerWriter.Write(bitStringStream.GetBuffer(),0,bitStringLength); | |
} | |
varlength=(int)innerStream.Length; | |
EncodeLength(writer,length); | |
writer.Write(innerStream.GetBuffer(),0,length); | |
} | |
varbase64=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(vari=0;i<base64.Length;i+=64) | |
{ | |
outputStream.Write(base64,i,Math.Min(64,base64.Length-i)); | |
outputStream.Write("\n"); | |
} | |
outputStream.Write("-----END PUBLIC KEY-----"); | |
} | |
returnoutputStream.ToString(); | |
} | |
/// <summary> | |
/// https://stackoverflow.com/a/23739932/2860309 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <param name="length"></param> | |
privatestaticvoidEncodeLength(BinaryWriterstream,intlength) | |
{ | |
if(length<0)thrownewArgumentOutOfRangeException("length","Length must be non-negative"); | |
if(length<0x80) | |
{ | |
// Short form | |
stream.Write((byte)length); | |
} | |
else | |
{ | |
// Long form | |
vartemp=length; | |
varbytesRequired=0; | |
while(temp>0) | |
{ | |
temp>>=8; | |
bytesRequired++; | |
} | |
stream.Write((byte)(bytesRequired|0x80)); | |
for(vari=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> | |
privatestaticvoidEncodeIntegerBigEndian(BinaryWriterstream,byte[]value,boolforceUnsigned=true) | |
{ | |
stream.Write((byte)0x02);// INTEGER | |
varprefixZeros=0; | |
for(vari=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(vari=prefixZeros;i<value.Length;i++) | |
{ | |
stream.Write(value[i]); | |
} | |
} | |
} | |
} | |
} |
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment