【问题标题】:RSA read PublicKeyRSA 读取 PublicKey
【发布时间】:2013-08-08 03:00:54
【问题描述】:

我有使用 java 算法生成的公钥 RSA 并且能够使用以下代码进行重构:

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

问题 如何使用 csharp 在 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>

【问题讨论】:

    标签: c# java security


    【解决方案1】:

    你好,你也可以试试这个方法,

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

    【讨论】:

      【解决方案2】:

      在 Java 中,将 publicKeyPublicKey 转换为 RSAPublicKey

      具有getModulusgetExponent 将获得BigIntegers,您可以使用toByteArray 从中获取字节。

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

      使用Apache Commons CodecJava 8's Base64 Encoder 将字节数组编码为base 64。

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

      通过构造此 XML 对这些进行序列化:"&lt;RSAKeyValue&gt;&lt;Modulus&gt;{此处为您的 base 64 编码公共模数}&lt;/Modulus&gt;&lt;Exponent&gt;{此处为您的 base 64 编码公共指数}&lt;/Exponent&gt;&lt;/RSAKeyValue&gt;"

      在 CSharp 中:

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

      现在您已经使用您的公钥加载了一个 RSA CSP。

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

      【讨论】:

        【解决方案3】:

        不幸的是,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;
        }
        

        以上代码基于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 文件。

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

        【讨论】:

        • @gpa:不接受这个答案。它不适合你吗?如果有问题,您能提供一些澄清吗?
        • 抱歉回复晚了,但解决方案对我不起作用,因为公钥是由 java 生成并在文件中序列化的。 Dotnet 应用程序将读取相同的公钥并构造其 PublicKey...
        • 生成然后将公钥序列化为文件的具体 Java 代码是什么? (您可以将它添加到您的问题中)Dotnet 读取 Java 序列化对象并不容易,但如果您将公钥保存为标准化格式,那么应该可以在 C# 中重建它。
        • 答案已更新,添加了一些适合您的代码。请务必在将 arrBytes 传递给 DecodeX509PublicKey 之前对其进行 Base64 解码。
        • @gpa:更新了代码。修复了DecodeX509PublicKey 中指数和模数被转换为大端序的错误。对此感到抱歉,我读过的所有内容都表明需要这样做(而且我显然没有进行足够彻底的测试:(),但您在上面的评论中提出的问题揭示了这个问题。
        猜你喜欢
        • 2011-10-25
        • 1970-01-01
        • 2020-06-29
        • 2014-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多