2013-08-06 135 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元素来扩展相同的过程以加载私钥。