C#解码pem格式的公钥

时间:2015-10-11 20:59:11

标签: c# python cryptography rsa pem

我编写了这段代码用于解码我的公钥,但这无法解码。我转换的base64公钥是Convert.FromBase64String("MCgCIQCWeC4U3lLF7JCJePjZpK9rd1TOsqbT+/8TT++WL1kuFwIDAQAB)",这是DecodeX509PublicKey函数的输入。密钥由python rsa模块生成。

python代码:

pub, prv = rsa.newkeys(256)
pub_pem = pub.save_pkcs1(format='PEM')
pub_pem.replace('-----BEGIN RSA PUBLIC KEY-----\n', '').replace('-----END RSA PUBLIC KEY-----\n', '')

C#代码:

//------- Parses binary asn.1 X509 SubjectPublicKeyInfo; returns RSACryptoServiceProvider ---
    public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
    {
        // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
        byte[] SeqOID = {0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00} ;
        byte[] seq = new byte[15];
        // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
        MemoryStream  mem = new MemoryStream(x509key) ;
        BinaryReader binr = new BinaryReader(mem) ;    //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;

        try{

            twobytes = binr.ReadUInt16();
            if(twobytes == 0x8130)  //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if(twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return null;
            seq = binr.ReadBytes(15);       //read the Sequence OID
            if(!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
                return null;

            twobytes = binr.ReadUInt16();
            if(twobytes == 0x8103)  //data read as little endian order (actual data order for Bit String is 03 81)
                binr.ReadByte();    //advance 1 byte
            else if(twobytes == 0x8203)
                binr.ReadInt16();   //advance 2 bytes
            else
                return null;

            bt = binr.ReadByte();
            if(bt != 0x00)      //expect null byte next
                return null;

            twobytes = binr.ReadUInt16();
            if(twobytes == 0x8130)  //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if(twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return null;

            twobytes = binr.ReadUInt16();
            byte lowbyte = 0x00;
            byte highbyte = 0x00;

            if(twobytes == 0x8102)  //data read as little endian order (actual data order for Integer is 02 81)
                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
            else if(twobytes == 0x8202) {
                highbyte = binr.ReadByte(); //advance 2 bytes
                lowbyte = binr.ReadByte();
            }
            else
                return null;
            byte[] modint = {lowbyte, highbyte, 0x00, 0x00} ;   //reverse byte order since asn.1 key uses big endian order
            int modsize = BitConverter.ToInt32(modint, 0) ;

            byte firstbyte = binr.ReadByte();
            binr.BaseStream.Seek(-1, SeekOrigin.Current);

            if(firstbyte == 0x00)   {   //if first byte (highest order) of modulus is zero, don't include it
                binr.ReadByte();    //skip this null byte
                modsize -=1  ;  //reduce modulus buffer size by 1
            }

            byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes

            if(binr.ReadByte() != 0x02)         //expect an Integer for the exponent data
                return null;
            int expbytes = (int) binr.ReadByte() ;      // should only need one byte for actual exponent data (for all useful values)
            byte[] exponent = binr.ReadBytes(expbytes);


            //Console.WriteLine("\nExponent", exponent);
            //Console.WriteLine("\nModulus", modulus) ;    

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSAParameters RSAKeyInfo = new RSAParameters();
            RSAKeyInfo.Modulus = modulus;
            RSAKeyInfo.Exponent = exponent;
            RSA.ImportParameters(RSAKeyInfo);
            return RSA;
        }
        catch(Exception){
            return null; 
        }

        finally { binr.Close(); }

    }

为什么DecodeX509PublicKey函数无法解码公钥?

1 个答案:

答案 0 :(得分:0)

您已创建一个位为256位的测试公钥。这太小了,无法发挥作用。现在的问题是BER编码长度:

  • 1字节,如果值字段的大小是< 128字节(只是长度)
  • 2个字节,如果值字段的大小是< 256个字节(81 XX,其中XX只是无符号数的长度,以十六进制表示)
  • 3字节,如果值字段的大小是< 65536字节(82 XX YY,其中XX YY是无符号大端编码数字的长度,再以十六进制表示)

当然,如果该值非常小,则长度的第一个字节不会以8182开头。请注意,例如84 00 00 XX YY有时也有有效的陈述。

处理此问题的三种方法:

  1. 实现模数的一个字节长度(快速修复)
  2. 仅测试1024位或更高的RSA密钥(总是一个好主意)
  3. 使用BER解码器甚至是完整的ASN.1库(也总是一个好主意)
  4. 我个人会将2.和3.

    结合起来