2013-08-06 216 views
5

我用java與算法RSA 使用能夠下面的代碼重構生成的公鑰:RSA閱讀公鑰

X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(arrBytes); 
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
publicKey = keyFactory.generatePublic(pubKeySpec); 

問題 如何使用csharp的構建上DOTNET方公鑰?

樣品公鑰將:,在上面的代碼中我通過包含在元件數據編碼

<sun.security.rsa.RSAPublicKeyImpl resolves-to="java.security.KeyRep"> 
    <type>PUBLIC</type> 
    <algorithm>RSA</algorithm> 
    <format>X.509</format> 
    <encoded>MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye 
0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ==</encoded> 
    </sun.security.rsa.RSAPublicKeyImpl> 

回答

17

不幸的是,C#不提供任何簡單的方法來做到這一點。但是,這將正確解碼的X509公鑰(確保爲Base64首先解碼x509key參數):

public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key) 
{ 
    byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 

    MemoryStream ms = new MemoryStream(x509key); 
    BinaryReader reader = new BinaryReader(ms); 

    if (reader.ReadByte() == 0x30) 
     ReadASNLength(reader); //skip the size 
    else 
     return null; 

    int identifierSize = 0; //total length of Object Identifier section 
    if (reader.ReadByte() == 0x30) 
     identifierSize = ReadASNLength(reader); 
    else 
     return null; 

    if (reader.ReadByte() == 0x06) //is the next element an object identifier? 
    { 
     int oidLength = ReadASNLength(reader); 
     byte[] oidBytes = new byte[oidLength]; 
     reader.Read(oidBytes, 0, oidBytes.Length); 
     if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1? 
      return null; 

     int remainingBytes = identifierSize - 2 - oidBytes.Length; 
     reader.ReadBytes(remainingBytes); 
    } 

    if (reader.ReadByte() == 0x03) //is the next element a bit string? 
    { 
     ReadASNLength(reader); //skip the size 
     reader.ReadByte(); //skip unused bits indicator 
     if (reader.ReadByte() == 0x30) 
     { 
      ReadASNLength(reader); //skip the size 
      if (reader.ReadByte() == 0x02) //is it an integer? 
      { 
       int modulusSize = ReadASNLength(reader); 
       byte[] modulus = new byte[modulusSize]; 
       reader.Read(modulus, 0, modulus.Length); 
       if (modulus[0] == 0x00) //strip off the first byte if it's 0 
       { 
        byte[] tempModulus = new byte[modulus.Length - 1]; 
        Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1); 
        modulus = tempModulus; 
       } 

       if (reader.ReadByte() == 0x02) //is it an integer? 
       { 
        int exponentSize = ReadASNLength(reader); 
        byte[] exponent = new byte[exponentSize]; 
        reader.Read(exponent, 0, exponent.Length); 

        RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
        RSAParameters RSAKeyInfo = new RSAParameters(); 
        RSAKeyInfo.Modulus = modulus; 
        RSAKeyInfo.Exponent = exponent; 
        RSA.ImportParameters(RSAKeyInfo); 
        return RSA; 
       } 
      } 
     } 
    } 
    return null; 
} 

public static int ReadASNLength(BinaryReader reader) 
{ 
    //Note: this method only reads lengths up to 4 bytes long as 
    //this is satisfactory for the majority of situations. 
    int length = reader.ReadByte(); 
    if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte 
    { 
     int count = length & 0x0000000f; 
     byte[] lengthBytes = new byte[4]; 
     reader.Read(lengthBytes, 4 - count, count); 
     Array.Reverse(lengthBytes); // 
     length = BitConverter.ToInt32(lengthBytes, 0); 
    } 
    return length; 
} 

上面的代碼是基於關閉的this question(這只是工作的一些關鍵尺寸)。上面的代碼幾乎適用於任何RSA密鑰大小,並且已經用您提供的密鑰以及2048位和4096位密鑰進行了測試。

另一種解決方案是使用工具生成證書(XCA是一個好的方法),將證書導出到p12(PKCS12)文件,然後在Java和C#中加載證書以獲取密鑰。

在C#中,您可以使用X509Certificate2類加載PKCS12文件。

X509Certificate2 cert = new X509Certificate2(certificateFile, certificatePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); 
RSACryptoServiceProvider provider1 = (RSACryptoServiceProvider)cert.PublicKey.Key; 
RSACryptoServiceProvider provider2 = (RSACryptoServiceProvider)cert.PrivateKey; 

在Java中,您可以使用KeyStore類加載PKCS12文件。

​​
+1

@gpa:未接受此答案。它不適合你嗎?如果有問題,您能否提供一些解釋? – Syon

+0

對不起,我遲到了,但解決方案不適合我,因爲公鑰由java生成並在文件中序列化。 Dotnet應用程序將讀取相同的公鑰並構造其PublicKey ... – gpa

+1

什麼是生成特定的Java代碼然後將公鑰序列化爲文件? (你可以將它添加到你的問題中)Dotnet讀取Java序列化對象並不容易,但如果你將公鑰保存爲標準格式,那麼應該可以在C#中重新構建它。 – Syon

0

在Java中,投publicKeyPublicKeyRSAPublicKey

那有getModulusgetExponent會讓你BigIntegers,從中你用toByteArray來獲取字節。

我不知道Java在BigInteger類中保持領先0,因此請檢查是否必須從公共指數中刪除前導空(0x00)字節。

使用Apache Commons CodecJava 8's Base64 Encoder將字節數組編碼爲基數64。

您可能需要檢查字節順序(可能反轉模數,不確定)。

通過構造這個XML序列化這些數據:"<RSAKeyValue><Modulus> {你的base 64編碼的公共模數} </Modulus><Exponent> {你的base 64編碼公共指數在這裏} </Exponent></RSAKeyValue>"

在CSHARP:

var rsaCsp = new RSACryptoServiceProvider(o.BitLength); 
rsaCsp.FromXmlString(xmlRsaKeyValue); 

現在你有一個RSA CSP載有你的公鑰。

可以通過添加P,Q,DP,DQ和InverseQ XML元素來擴展相同的過程以加載私鑰。