2014-05-19 6 views
22

मैं System.Security.Cryptography.RSACryptoServiceProvider का एक उदाहरण है, मैं की जरूरत है कि यह एक पीईएम स्ट्रिंग की कुंजी है निर्यात करने के लिए - इस तरह:सी # निर्यात निजी/सार्वजनिक RSA कुंजी

-----BEGIN RSA PRIVATE KEY----- 
MIICXAIBAAKBgQDUNPB6Lvx+tlP5QhSikADl71AjZf9KN31qrDpXNDNHEI0OTVJ1 
OaP2l56bSKNo8trFne1NK/B4JzCuNP8x6oGCAG+7bFgkbTMzV2PCoDCRjNH957Q4 
Gxgx1VoS6PjD3OigZnx5b9Hebbp3OrTuqNZaK/oLPGr5swxHILFVeHKupQIDAQAB 
AoGAQk3MOZEGyZy0fjQ8eFKgRTfSBU1wR8Mwx6zKicbAotq0CBz2v7Pj3D+higlX 
LYp7+rUOmUc6WoB8QGJEvlb0YZVxUg1yDLMWYPE7ddsHsOkBIs7zIyS6cqhn0yZD 
VTRFjVST/EduvpUOL5hbyLSwuq+rbv0iPwGW5hkCHNEhx2ECQQDfLS5549wjiFXF 
gcio8g715eMT+20we3YmgMJDcviMGwN/mArvnBgBQsFtCTsMoOxm68SfIrBYlKYy 
BsFxn+19AkEA82q83pmcbGJRJ3ZMC/Pv+/+/XNFOvMkfT9qbuA6Lv69Z1yk7I1ie 
FTH6tOmPUu4WsIOFtDuYbfV2pvpqx7GuSQJAK3SnvRIyNjUAxoF76fGgGh9WNPjb 
DPqtSdf+e5Wycc18w+Z+EqPpRK2T7kBC4DWhcnTsBzSA8+6V4d3Q4ugKHQJATRhw 
a3xxm65kD8CbA2omh0UQQgCVFJwKy8rsaRZKUtLh/JC1h1No9kOXKTeUSmrYSt3N 
OjFp7OHCy84ihc8T6QJBANe+9xkN9hJYNK1pL1kSwXNuebzcgk3AMwHh7ThvjLgO 
jruxbM2NyMM5tl9NZCgh1vKc2v5VaonqM1NBQPDeTTw= 
-----END RSA PRIVATE KEY----- 

लेकिन एमएसडीएन दस्तावेज के अनुसार ऐसा कोई विकल्प नहीं है, केवल कुछ प्रकार का एक्सएमएल निर्यात है। मैं बाउंसीकास्टल जैसी किसी तीसरे पक्ष के पुस्तकालयों का उपयोग नहीं कर सकता। क्या इस स्ट्रिंग को उत्पन्न करने का कोई तरीका है?

+0

कैसे और कहाँ एक करता है उस वर्ग के एन उदाहरण में एक कुंजी है? – rene

+1

टीथ दर्द बिंदु .NET और [आरएफसी 3275] (https://www.ietf.org/rfc/rfc3275.txt) से एक्सएमएल एन्कोडिंग के उनके उपयोग के कारण है। .NET ASN.1/DER या PEM एन्कोडेड कुंजी का उपयोग नहीं करता है। मुझे लगता है कि यह एकमात्र क्रिप्टो लाइब्रेरी है जो इस तरह से चीजें करता है। – jww

उत्तर

42

कृपया ध्यान दें: नीचे दिया गया कोड निजी कुंजी निर्यात करने के लिए है। यदि आप सार्वजनिक कुंजी निर्यात करना चाहते हैं, तो कृपया मेरे उत्तर here दिए गए देखें।

पीईएम प्रारूप बस ASN.1 कुंजी के डीईआर एन्कोडिंग (प्रति PKCS#1) को बेस 64 में परिवर्तित किया गया है। कुंजी का प्रतिनिधित्व करने के लिए आवश्यक फ़ील्ड की सीमित संख्या को देखते हुए, उचित प्रारूप को आउटपुट करने के लिए त्वरित और गंदे डीईआर एन्कोडर बनाने के लिए यह बहुत सरल है, तो बेस 64 इसे एन्कोड करें। जैसे, कोड इस प्रकार है कि विशेष रूप से सुंदर नहीं है, लेकिन काम करता है: (

public static String ExportPublicKeyToPEMFormat(RSACryptoServiceProvider csp) 
{ 
    TextWriter 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); 
      EncodeIntegerBigEndian(innerWriter, new byte[] { 0x00 }); // Version 
      EncodeIntegerBigEndian(innerWriter, parameters.Modulus); 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); 

      //All Parameter Must Have Value so Set Other Parameter Value Whit Invalid Data (for keeping Key Structure use "parameters.Exponent" value for invalid data) 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.D 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.P 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.Q 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.DP 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.DQ 
      EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of 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(); 
     outputStream.WriteLine("-----BEGIN PUBLIC KEY-----"); 
     // Output as Base64 with lines chopped at 64 characters 
     for (var i = 0; i < base64.Length; i += 64) 
     { 
      outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i)); 
     } 
     outputStream.WriteLine("-----END PUBLIC KEY-----"); 

     return outputStream.ToString(); 

    } 
} 

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]); 
     } 
    } 
} 

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)); 
     } 
    } 
} 
+0

बढ़िया, यह पूरी तरह से काम करता है, लेकिन मैं सार्वजनिक कुंजी कैसे निर्यात करूं? क्या इसकी समान संरचना है। मैंने केवल एक्सपोनेंट और मॉड्यूलस (सार्वजनिक कुंजी की सामग्री) के साथ ऐसा करने का प्रयास किया है, लेकिन यह वैध परिणाम नहीं देता है। सार्वजनिक कुंजी स्ट्रिंग कैसे प्राप्त करें? – nidzo732

+1

कोई बात नहीं। मुझे यह काम मिल गया, मैं संस्करण भाग को हटाने के लिए भूल गया। अब यह सार्वजनिक कुंजी भी निर्यात करता है। – nidzo732

+6

रुचि रखने वालों के लिए, यहां सार्वजनिक उत्तर को सही ढंग से निर्यात करने के लिए मेरे उत्तर में कोड के माध्यम से किया जा सकता है: http://stackoverflow.com/questions/28406888/c-sharp-rsa-public-key-output-not-correct/28407693 # 28407693 जो इस उत्तर से कुछ विधियों का फिर से उपयोग करता है। – Iridium

6

PublicKey उपयोग इस कोड को निर्यात के लिए जो बहुत उपयोगी है, मुझे गलत नहीं है), मैं मैं अपने समाधान है जो एक छोटे से अधिक IMO सीधा (लेकिन अभी भी मूल जवाब के आधार पर) है पोस्ट था सोचा:

public class RsaCsp2DerConverter { 
    private const int MaximumLineLength = 64; 

    // Based roughly on: http://stackoverflow.com/a/23739932/1254575 

    public RsaCsp2DerConverter() { 

    } 

    public byte[] ExportPrivateKey(String cspBase64Blob) { 
     if (String.IsNullOrEmpty(cspBase64Blob) == true) 
     throw new ArgumentNullException(nameof(cspBase64Blob)); 

     var csp = new RSACryptoServiceProvider(); 

     csp.ImportCspBlob(Convert.FromBase64String(cspBase64Blob)); 

     if (csp.PublicOnly) 
     throw new ArgumentException("CSP does not contain a private key!", nameof(csp)); 

     var parameters = csp.ExportParameters(true); 

     var list = new List<byte[]> { 
     new byte[] {0x00}, 
     parameters.Modulus, 
     parameters.Exponent, 
     parameters.D, 
     parameters.P, 
     parameters.Q, 
     parameters.DP, 
     parameters.DQ, 
     parameters.InverseQ 
     }; 

     return SerializeList(list); 
    } 

    private byte[] Encode(byte[] inBytes, bool useTypeOctet = true) { 
     int length = inBytes.Length; 
     var bytes = new List<byte>(); 

     if (useTypeOctet == true) 
     bytes.Add(0x02); // INTEGER 

     bytes.Add(0x84); // Long format, 4 bytes 
     bytes.AddRange(BitConverter.GetBytes(length).Reverse()); 
     bytes.AddRange(inBytes); 

     return bytes.ToArray(); 
    } 

    public String PemEncode(byte[] bytes) { 
     if (bytes == null) 
     throw new ArgumentNullException(nameof(bytes)); 

     var base64 = Convert.ToBase64String(bytes); 

     StringBuilder b = new StringBuilder(); 
     b.Append("-----BEGIN RSA PRIVATE KEY-----\n"); 

     for (int i = 0; i < base64.Length; i += MaximumLineLength) 
     b.Append($"{ base64.Substring(i, Math.Min(MaximumLineLength, base64.Length - i)) }\n"); 

     b.Append("-----END RSA PRIVATE KEY-----\n"); 

     return b.ToString(); 
    } 

    private byte[] SerializeList(List<byte[]> list) { 
     if (list == null) 
     throw new ArgumentNullException(nameof(list)); 

     var keyBytes = list.Select(e => Encode(e)).SelectMany(e => e).ToArray(); 

     var binaryWriter = new BinaryWriter(new MemoryStream()); 
     binaryWriter.Write((byte) 0x30); // SEQUENCE 
     binaryWriter.Write(Encode(keyBytes, false)); 
     binaryWriter.Flush(); 

     var result = ((MemoryStream) binaryWriter.BaseStream).ToArray(); 

     binaryWriter.BaseStream.Dispose(); 
     binaryWriter.Dispose(); 

     return result; 
    } 
} 
+0

मुझे 'EncodeIntegerBigEndian (innerWriter, new byte [] {0x00}) को हटाना पड़ा; // संस्करण' लाइन पोको लाइब्रेरी के [Crypto :: RSAKey] (http://pocoproject.org/docs/Poco.Crypto.RSAKey.html) में जेनरेट की गई पीईएम फ़ाइल को लोड करने में सक्षम होने के लिए। – Isaac

+7

यह मेरे लिए काम नहीं किया। इसके बजाय, मैंने अपने जवाब के नीचे अपनी टिप्पणियों के अनुसार @ इरिडियम की अन्य पोस्ट http://stackoverflow.com/questions/28406888/c-sharp-rsa-public-key-output-not-correct/28407693#28407693 का उपयोग किया था। –

+1

छह अतिरिक्त लिखने वाले क्या हैं ... एक आरएसए सार्वजनिक कुंजी '{n, e}' जोड़ी है। आपको उपरोक्त की तरह हैक साझा करने के बजाए तार पर सही प्रारूप के लिए पीकेसीएस # 1 की जांच करनी चाहिए। – jww

1

किसी और को जो मूल जवाब के स्पष्ट जटिलता पर एतराज जताया लिए:

private static void ExportPrivateKey(RSACryptoServiceProvider csp, TextWriter outputStream) 
{ 
    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(); 
     outputStream.WriteLine("-----BEGIN RSA PRIVATE KEY-----"); 
     // Output as Base64 with lines chopped at 64 characters 
     for (var i = 0; i < base64.Length; i += 64) 
     { 
      outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i)); 
     } 
     outputStream.WriteLine("-----END RSA PRIVATE KEY-----"); 
    } 
} 

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)); 
     } 
    } 
} 

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]); 
     } 
    } 
} 
संबंधित मुद्दे