RSA读取公钥

8

我有一个使用Java生成的公钥,加密算法为RSA,可以使用以下代码进行重建:

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

问题

如何在C#中构建dotnet端的PublicKey?

样本公钥将是:在上述代码中,我传递包含元素encoded中的数据。

    <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>
3个回答

18

不幸的是,C#没有提供任何简单的方法来实现这一点。但是,以下代码可以正确地解码x509公钥(请确保先对x509key参数进行Base64解码):

不幸的是,C#没有提供任何简单的方法来实现这一点。但是,以下代码可以正确地解码x509公钥(请确保先对x509key参数进行Base64解码):

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

以上代码基于这个问题(只适用于某些密钥大小)。 以上代码将适用于几乎任何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文件。

KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(certificateFile), certificatePassword.toCharArray());
Key key = keystore.getKey(certName, certificatePassword.toCharArray());
Certificate cert = keystore.getCertificate(certName);
PublicKey publicKey = cert.getPublicKey();
KeyPair keys = new KeyPair(publicKey, (PrivateKey) key);

1
@gpa:取消接受此答案。它对您无效吗?如果有问题,您能提供一些澄清吗? - Syon
1
生成并将公钥序列化到文件的具体Java代码是什么?(您能将其添加到您的问题中吗?)在Dotnet中读取Java序列化对象不容易,但如果您将公钥保存为标准格式,则应该可以在C#中重构它。 - Syon
2
答案已更新,添加了一些代码应该适用于您。在将arrBytes传递给DecodeX509PublicKey之前,请确保对其进行Base64解码。 - Syon
你可以看一下这个:[http://stackoverflow.com/questions/18344670/rsa-and-publickey-interop-with-dotnet] - gpa
2
@gpa:更新了代码。修复了DecodeX509PublicKey中的一个错误,即指数和模数被转换为大端字节序。对此我感到很抱歉,因为我所阅读的所有内容都表明需要这样做(显然我没有测试得够充分:( ),但是你在上面的评论中提出的问题揭示了这个问题。 - Syon
显示剩余5条评论

0

在Java中,将publicKeyPublicKey转换为RSAPublicKey

这样就可以使用getModulusgetExponent获取BigIntegers,然后使用toByteArray获取字节。

我不知道Java是否在BigInteger类中保留前导0,因此请检查是否必须从公共指数中剥离前导空(0x00)字节。

使用Apache Commons CodecJava 8的Base64编码器将字节数组编码为base 64。

您可能需要检查字节顺序(也许要反转模数,不确定)。

通过构建以下 XML 序列化这些内容:"<RSAKeyValue><Modulus>{在此处放置您的 Base64 编码公共模数}</Modulus><Exponent>{在此处放置您的 Base64 编码公共指数}</Exponent></RSAKeyValue>"

在 CSharp 中:

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

现在您已经加载了包含公钥的RSA CSP。

通过添加P、Q、DP、DQ和InverseQ XML元素,可以扩展相同的过程以加载私钥。


0

你好,你也可以试试这种方式:

private static string[] GenerateXMLPrivatePublicKeys(){
    string[] keys = new string[2];
    RSA rsa = new RSACryptoServiceProvider(2048);
    string publicKey = rsa.ToXmlString(false);
    string privateKey = rsa.ToXmlString(true);
    keys[0] = publicKey;
    keys[1] = privateKey;
    return keys;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接