RSA read PublicKey

2019-01-09 13:23发布

I have public key generated using java with algorithm RSA and able to reconstruct using following code:

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

QUESTION How to construct PublicKey on dotnet side using csharp?

sample public key would be:, In above code i pass data contained in element 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
3条回答
来,给爷笑一个
2楼-- · 2019-01-09 14:07

Unfortunately, C# doesn't provide any simple way to do this. But this will correctly decode an x509 public key (make sure to Base64 decode the x509key parameter first):

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

The above code is based off of this question (which only worked for certain key sizes). The above code will work for pretty much any RSA key size though, and has been tested with the key you provided as well as 2048-bit and 4096-bit keys.

An alternative solution would be to generate a certificate using a tool (XCA is a good one), export the cert to a p12 (PKCS12) file and then load the cert in both Java and C# to get at the keys.

In C# you can load a PKCS12 file using the X509Certificate2 class.

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

In Java you can load a PKCS12 file using the KeyStore class.

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);
查看更多
smile是对你的礼貌
3楼-- · 2019-01-09 14:13

In Java, cast publicKey from PublicKey to RSAPublicKey.

That has getModulus and getExponent will get you BigIntegers, from which you use toByteArray to get the bytes.

I don't know Java keeps leading 0s in the BigInteger class, so check if you have to strip leading null (0x00) bytes from the public exponent.

Encode the byte arrays into base 64 using either the Apache Commons Codec or Java 8's Base64 Encoder.

You may need to check byte order (maybe reverse the modulus, not sure).

Serialize these by constructing this XML: "<RSAKeyValue><Modulus>{your base 64 encoded public modulus here}</Modulus><Exponent>{your base 64 encoded public exponent here}</Exponent></RSAKeyValue>".

In CSharp:

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

Now you have an RSA CSP loaded with your public key.

The same process can be extended to load a private key by adding P, Q, DP, DQ and InverseQ XML elements.

查看更多
劫难
4楼-- · 2019-01-09 14:14

Hello you can try this way also,

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;
}
查看更多
登录 后发表回答