如何在 C# 中从文件中加载 RSA 公钥

Posted

技术标签:

【中文标题】如何在 C# 中从文件中加载 RSA 公钥【英文标题】:How to load the RSA public key from file in C# 【发布时间】:2012-07-15 10:52:32 【问题描述】:

我需要从文件中加载以下 RSA 公钥以用于 RSACryptoServiceProvider 类。我该怎么做?

-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXX
-----END PUBLIC KEY-----

此代码适用于我的 pub 密钥:http://www.jensign.com/opensslkey/

这是我正在使用的代码

        static string RSA(string input)
        
            RSACryptoServiceProvider rsa = DecodeX509PublicKey(Convert.FromBase64String(GetKey()));

            return (Convert.ToBase64String(rsa.Encrypt(Encoding.ASCII.GetBytes(input), false)));
        

        static string GetKey()
        
            return File.ReadAllText("master.pub").Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
            //.Replace("\n", "");
        

        private static bool CompareBytearrays(byte[] a, byte[] b)
        
            if (a.Length != b.Length)
                return false;
            int i = 0;
            foreach (byte c in a)
            
                if (c != b[i])
                    return false;
                i++;
            
            return true;
        

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

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

        

只需使用要加密的文本调用“RSA”方法即可。

【问题讨论】:

使用IO.StreamReader? 可能重复? ***.com/questions/243646/… 我尝试使用 Convert.FromBase64String 将字符串转换为字节,然后将字节传递给 RSACryptoServiceProvider 构造函数,但它会引发异常。 X509Certificate.CreateFromFile 也会发生同样的情况 也许您可以先尝试针对已知的有效公钥文件测试您的代码,然后针对您上面的密钥进行测试。 我现在使用下面的代码jensign.com/opensslkey 似乎可以工作... 【参考方案1】:

您可以使用以下类(GetRSAProviderFromPemFile 方法)从 PEM 文件创建 RSACryptoServiceProvider

警告: 不要未经验证就从 *** 复制代码!尤其不是加密代码!此代码有错误(请参阅 cmets)。您可能希望在生产中使用它之前编写和运行测试(如果您真的没有更好的选择)。我拒绝编辑代码来修复它,因为如果没有测试和积极的维护者。

来源: 这段代码似乎取自opensslkey 来自this site。 版权所有 (c) 2000 JavaScience Consulting, Michel Gallant。 原始包是在类似 BSD 的许可证下发布的,因此使用它可能是可以的(但您可能需要仔细检查)。还有一个NuGet package是同一作者的。

这是最初发布到此答案的复制粘贴源代码:

RSACryptoServiceProvider provider = PemKeyUtils.GetRSAProviderFromPemFile(  @"public_key.pem" );


public class PemKeyUtils

    const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
    const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
    const String pempubheader = "-----BEGIN PUBLIC KEY-----";
    const String pempubfooter = "-----END PUBLIC KEY-----";
    const String pemp8header = "-----BEGIN PRIVATE KEY-----";
    const String pemp8footer = "-----END PRIVATE KEY-----";
    const String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
    const String pemp8encfooter = "-----END ENCRYPTED PRIVATE KEY-----";

    static bool verbose = false;

    public static RSACryptoServiceProvider GetRSAProviderFromPemFile( String pemfile )
    
        bool isPrivateKeyFile = true;
        string pemstr = File.ReadAllText( pemfile ).Trim();
        if (pemstr.StartsWith( pempubheader ) && pemstr.EndsWith( pempubfooter ))
            isPrivateKeyFile = false;

        byte[] pemkey;
        if (isPrivateKeyFile)
            pemkey = DecodeOpenSSLPrivateKey( pemstr );
        else
            pemkey = DecodeOpenSSLPublicKey( pemstr );

        if (pemkey == null)
            return null;

        if (isPrivateKeyFile)
            return DecodeRSAPrivateKey( pemkey );
        else
            return DecodeX509PublicKey( pemkey );

    



    //--------   Get the binary RSA PUBLIC key   --------
    static byte[] DecodeOpenSSLPublicKey( String instr )
    
        const String pempubheader = "-----BEGIN PUBLIC KEY-----";
        const String pempubfooter = "-----END PUBLIC KEY-----";
        String pemstr = instr.Trim();
        byte[] binkey;
        if (!pemstr.StartsWith( pempubheader ) || !pemstr.EndsWith( pempubfooter ))
            return null;
        StringBuilder sb = new StringBuilder( pemstr );
        sb.Replace( pempubheader, "" );  //remove headers/footers, if present
        sb.Replace( pempubfooter, "" );

        String pubstr = sb.ToString().Trim();   //get string after removing leading/trailing whitespace

        try
        
            binkey = Convert.FromBase64String( pubstr );
        
        catch (System.FormatException)
               //if can't b64 decode, data is not valid
            return null;
        
        return binkey;
    

    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 ;
        // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
        using (var mem = new MemoryStream(x509Key))
        
            using (var binr = new BinaryReader(mem))    //wrap Memory Stream with BinaryReader for easy reading
            
                try
                
                    var twobytes = binr.ReadUInt16();
                    switch (twobytes)
                    
                        case 0x8130:
                            binr.ReadByte();    //advance 1 byte
                            break;
                        case 0x8230:
                            binr.ReadInt16();   //advance 2 bytes
                            break;
                        default:
                            return null;
                    

                    var seq = binr.ReadBytes(15);
                    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;

                    var 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 = binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
                    byte[] exponent = binr.ReadBytes(expbytes);

                    // We don't really need to print anything but if we insist to...
                    //showBytes("\nExponent", exponent);
                    //showBytes("\nModulus", modulus);

                    // ------- create RSACryptoServiceProvider instance and initialize with public key -----
                    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                    RSAParameters rsaKeyInfo = new RSAParameters
                    
                        Modulus = modulus,
                        Exponent = exponent
                    ;
                    rsa.ImportParameters(rsaKeyInfo);
                    return rsa;
                
                catch (Exception)
                
                    return null;
                
            
        
    

    //------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider  ---
    static RSACryptoServiceProvider DecodeRSAPrivateKey( byte[] privkey )
    
        byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

        // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
        MemoryStream mem = new MemoryStream( privkey );
        BinaryReader binr = new BinaryReader( mem );    //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;
        int elems = 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;

            twobytes = binr.ReadUInt16();
            if (twobytes != 0x0102) //version number
                return null;
            bt = binr.ReadByte();
            if (bt != 0x00)
                return null;


            //------  all private key components are Integer sequences ----
            elems = GetIntegerSize( binr );
            MODULUS = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            E = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            D = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            P = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            Q = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            DP = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            DQ = binr.ReadBytes( elems );

            elems = GetIntegerSize( binr );
            IQ = binr.ReadBytes( elems );

            Console.WriteLine( "showing components .." );
            if (verbose)
            
                showBytes( "\nModulus", MODULUS );
                showBytes( "\nExponent", E );
                showBytes( "\nD", D );
                showBytes( "\nP", P );
                showBytes( "\nQ", Q );
                showBytes( "\nDP", DP );
                showBytes( "\nDQ", DQ );
                showBytes( "\nIQ", IQ );
            

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSAParameters RSAparams = new RSAParameters();
            RSAparams.Modulus = MODULUS;
            RSAparams.Exponent = E;
            RSAparams.D = D;
            RSAparams.P = P;
            RSAparams.Q = Q;
            RSAparams.DP = DP;
            RSAparams.DQ = DQ;
            RSAparams.InverseQ = IQ;
            RSA.ImportParameters( RSAparams );
            return RSA;
        
        catch (Exception)
        
            return null;
        
        finally  binr.Close(); 
    

    private static int GetIntegerSize( BinaryReader binr )
    
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)     //expect integer
            return 0;
        bt = binr.ReadByte();

        if (bt == 0x81)
            count = binr.ReadByte();    // data size in next byte
        else
            if (bt == 0x82)
            
                highbyte = binr.ReadByte(); // data size in next 2 bytes
                lowbyte = binr.ReadByte();
                byte[] modint =  lowbyte, highbyte, 0x00, 0x00 ;
                count = BitConverter.ToInt32( modint, 0 );
            
            else
            
                count = bt;     // we already have the data size
            



        while (binr.ReadByte() == 0x00)
           //remove high order zeros in data
            count -= 1;
        
        binr.BaseStream.Seek( -1, SeekOrigin.Current );     //last ReadByte wasn't a removed zero, so back up a byte
        return count;
    

    //-----  Get the binary RSA PRIVATE key, decrypting if necessary ----
    static byte[] DecodeOpenSSLPrivateKey( String instr )
    
        const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
        const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
        String pemstr = instr.Trim();
        byte[] binkey;
        if (!pemstr.StartsWith( pemprivheader ) || !pemstr.EndsWith( pemprivfooter ))
            return null;

        StringBuilder sb = new StringBuilder( pemstr );
        sb.Replace( pemprivheader, "" );  //remove headers/footers, if present
        sb.Replace( pemprivfooter, "" );

        String pvkstr = sb.ToString().Trim();   //get string after removing leading/trailing whitespace

        try
                // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
            binkey = Convert.FromBase64String( pvkstr );
            return binkey;
        
        catch (System.FormatException)
               //if can't b64 decode, it must be an encrypted private key
            //Console.WriteLine("Not an unencrypted OpenSSL PEM private key");  
        

        StringReader str = new StringReader( pvkstr );

        //-------- read PEM encryption info. lines and extract salt -----
        if (!str.ReadLine().StartsWith( "Proc-Type: 4,ENCRYPTED" ))
            return null;
        String saltline = str.ReadLine();
        if (!saltline.StartsWith( "DEK-Info: DES-EDE3-CBC," ))
            return null;
        String saltstr = saltline.Substring( saltline.IndexOf( "," ) + 1 ).Trim();
        byte[] salt = new byte[saltstr.Length / 2];
        for (int i = 0; i < salt.Length; i++)
            salt[i] = Convert.ToByte( saltstr.Substring( i * 2, 2 ), 16 );
        if (!(str.ReadLine() == ""))
            return null;

        //------ remaining b64 data is encrypted RSA key ----
        String encryptedstr = str.ReadToEnd();

        try
           //should have b64 encrypted RSA key now
            binkey = Convert.FromBase64String( encryptedstr );
        
        catch (System.FormatException)
          // bad b64 data.
            return null;
        

        //------ Get the 3DES 24 byte key using PDK used by OpenSSL ----

        SecureString despswd = GetSecPswd( "Enter password to derive 3DES key==>" );
        //Console.Write("\nEnter password to derive 3DES key: ");
        //String pswd = Console.ReadLine();
        byte[] deskey = GetOpenSSL3deskey( salt, despswd, 1, 2 );    // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
        if (deskey == null)
            return null;
        //showBytes("3DES key", deskey) ;

        //------ Decrypt the encrypted 3des-encrypted RSA private key ------
        byte[] rsakey = DecryptKey( binkey, deskey, salt ); //OpenSSL uses salt value in PEM header also as 3DES IV
        if (rsakey != null)
            return rsakey;  //we have a decrypted RSA private key
        else
        
            Console.WriteLine( "Failed to decrypt RSA private key; probably wrong password." );
            return null;
        
    


    // ----- Decrypt the 3DES encrypted RSA private key ----------

    static byte[] DecryptKey( byte[] cipherData, byte[] desKey, byte[] IV )
    
        MemoryStream memst = new MemoryStream();
        TripleDES alg = TripleDES.Create();
        alg.Key = desKey;
        alg.IV = IV;
        try
        
            CryptoStream cs = new CryptoStream( memst, alg.CreateDecryptor(), CryptoStreamMode.Write );
            cs.Write( cipherData, 0, cipherData.Length );
            cs.Close();
        
        catch (Exception exc)
        
            Console.WriteLine( exc.Message );
            return null;
        
        byte[] decryptedData = memst.ToArray();
        return decryptedData;
    

    //-----   OpenSSL PBKD uses only one hash cycle (count); miter is number of iterations required to build sufficient bytes ---
    static byte[] GetOpenSSL3deskey( byte[] salt, SecureString secpswd, int count, int miter )
    
        IntPtr unmanagedPswd = IntPtr.Zero;
        int HASHLENGTH = 16;    //MD5 bytes
        byte[] keymaterial = new byte[HASHLENGTH * miter];     //to store contatenated Mi hashed results


        byte[] psbytes = new byte[secpswd.Length];
        unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi( secpswd );
        Marshal.Copy( unmanagedPswd, psbytes, 0, psbytes.Length );
        Marshal.ZeroFreeGlobalAllocAnsi( unmanagedPswd );

        //UTF8Encoding utf8 = new UTF8Encoding();
        //byte[] psbytes = utf8.GetBytes(pswd);

        // --- contatenate salt and pswd bytes into fixed data array ---
        byte[] data00 = new byte[psbytes.Length + salt.Length];
        Array.Copy( psbytes, data00, psbytes.Length );      //copy the pswd bytes
        Array.Copy( salt, 0, data00, psbytes.Length, salt.Length ); //concatenate the salt bytes

        // ---- do multi-hashing and contatenate results  D1, D2 ...  into keymaterial bytes ----
        MD5 md5 = new MD5CryptoServiceProvider();
        byte[] result = null;
        byte[] hashtarget = new byte[HASHLENGTH + data00.Length];   //fixed length initial hashtarget

        for (int j = 0; j < miter; j++)
        
            // ----  Now hash consecutively for count times ------
            if (j == 0)
                result = data00;    //initialize 
            else
            
                Array.Copy( result, hashtarget, result.Length );
                Array.Copy( data00, 0, hashtarget, result.Length, data00.Length );
                result = hashtarget;
                //Console.WriteLine("Updated new initial hash target:") ;
                //showBytes(result) ;
            

            for (int i = 0; i < count; i++)
                result = md5.ComputeHash( result );
            Array.Copy( result, 0, keymaterial, j * HASHLENGTH, result.Length );  //contatenate to keymaterial
        
        //showBytes("Final key material", keymaterial);
        byte[] deskey = new byte[24];
        Array.Copy( keymaterial, deskey, deskey.Length );

        Array.Clear( psbytes, 0, psbytes.Length );
        Array.Clear( data00, 0, data00.Length );
        Array.Clear( result, 0, result.Length );
        Array.Clear( hashtarget, 0, hashtarget.Length );
        Array.Clear( keymaterial, 0, keymaterial.Length );

        return deskey;
    

    static SecureString GetSecPswd( String prompt )
    
        SecureString password = new SecureString();

        Console.ForegroundColor = ConsoleColor.Gray;
        Console.Write( prompt );
        Console.ForegroundColor = ConsoleColor.Magenta;

        while (true)
        
            ConsoleKeyInfo cki = Console.ReadKey( true );
            if (cki.Key == ConsoleKey.Enter)
            
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine();
                return password;
            
            else if (cki.Key == ConsoleKey.Backspace)
            
                // remove the last asterisk from the screen...
                if (password.Length > 0)
                
                    Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
                    Console.Write( " " );
                    Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
                    password.RemoveAt( password.Length - 1 );
                
            
            else if (cki.Key == ConsoleKey.Escape)
            
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine();
                return password;
            
            else if (Char.IsLetterOrDigit( cki.KeyChar ) || Char.IsSymbol( cki.KeyChar ))
            
                if (password.Length < 20)
                
                    password.AppendChar( cki.KeyChar );
                    Console.Write( "*" );
                
                else
                
                    Console.Beep();
                
            
            else
            
                Console.Beep();
            
        
    

    static bool CompareBytearrays( byte[] a, byte[] b )
    
        if (a.Length != b.Length)
            return false;
        int i = 0;
        foreach (byte c in a)
        
            if (c != b[i])
                return false;
            i++;
        
        return true;
    

    static void showBytes( String info, byte[] data )
    
        Console.WriteLine( "0  [1 bytes]", info, data.Length );
        for (int i = 1; i <= data.Length; i++)
        
            Console.Write( "0:X2  ", data[i - 1] );
            if (i % 16 == 0)
                Console.WriteLine();
        
        Console.WriteLine( "\n\n" );
    


【讨论】:

很抱歉,OP 没有将您的答案标记为已接受。这是一个非常好的。 嗯...例如,这个openssl键不起作用,也不是来自某些库,你可以看看:github.com/dvsekhvalnov/jose-jwt/issues/…^^' 此代码(尤其是在GetIntegerSize 中跳过前导零)会由于值太短而导致ImportParameters 中的CryptographicException。它只是偶尔发生。由于值太长,不跳过前导零可能会导致相同的异常。如果不跳过GetIntegerSize 中的前导零,值是否会变得太短尚不清楚,因为这取决于输入。有关详细信息,请参阅 ***.com/a/39135984/2279059。 字符串 private_key = File.ReadAllText(private_key_file).Trim();正则表达式 regex = new Regex(@"-----(BEGIN|END) (RSA|OPENSSH|ENCRYPTED) PRIVATE KEY-----[\W]*"); string private_key_base64 = regex.Replace(private_key, ""); byte[] bytes_privake_key = Convert.FromBase64String(private_key_base64); rsa.ImportRSAPrivateKey(new ReadOnlySpan(bytes_privake_key), out bytesRead);【参考方案2】:

如果您说的是 X509 证书:

FileStream fs = new FileStream("your_cert_file.crt", FileMode.Open);
byte[] certBytes = new byte[fs.Length];
fs.Read(certBytes, 0, (Int32)fs.Length);
fs.Close();
System.Security.Cryptography.X509Certificates.X509Certificate x509cert = 
    new X509Certificate(certBytes);
Console.WriteLine(x509cert.GetPublicKey());
Console.WriteLine(x509cert.GetPublicKeyString());

在“7/16/2012 15:04:58 Z”中完成@hkproj 的评论后编辑:

环顾四周,我找到了“Reading PEM RSA Public Key Only using Bouncy Castle”。 我想你想要的是这样的:

using (StreamReader reader = File.OpenText(@"c:\RSA.txt"))

    Org.BouncyCastle.OpenSsl.PemReader pr = 
        new Org.BouncyCastle.OpenSsl.PemReader(reader);
    Org.BouncyCastle.Utilities.IO.Pem.PemObject po = pr.ReadPemObject();

    Console.WriteLine("PemObject, Type: " + po.Type);
    Console.WriteLine("PemObject, Length: " + po.Content.Length);

但是,对于您的文件,我收到一个错误:System.IO.IOException : base64 data appears to be truncated

因此将您的文件更改为:

-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXXZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
-----END PUBLIC KEY-----

结果是:

PemObject, Type: PUBLIC KEY
PemObject, Length: 192

【讨论】:

已经试过了。没用。抛出“CryptographicException”并显示以下消息:找不到请求的对象。【参考方案3】:

您是在谈论存储在文件中的证书吗?

如果你有这样的对象:

X509Certificate2 certificate;

您可以使用以下代码:

RSACryptoServiceProvider rsaprovider =
                    (RSACryptoServiceProvider)certificate.PublicKey.Key;

然后使用类 RSACryptoServiceProvider(参见http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx)。

要加载 X509Certificate2,请使用其构造函数(请参阅http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx)。

这个程序对我来说很好用:

    static void Main(string[] args)
    
        try 
        
            X509Certificate2 certificate = 
                new X509Certificate2("<PFX Certificate Path", "<Certificate-Password>");
            RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)certificate.PublicKey.Key;
        
        catch(Exception e)
        

        
    

【讨论】:

抛出 'CryptographicException' 并显示此消息:找不到请求的对象 我知道构造函数等待二进制证书(DER 格式),而不是 base64 一个...阅读您的解决方案我注意到这个“FromBase64String”所以您使用的是 Base64 二进制格式? 证书保存为base64格式。所以我使用 FromBase64String 将其转换为字节数组,以便可以将其传递给构造函数。顺便说一句,我解决了。如果您查看我的解决方案,我正在使用一种从“opensslkey”工具(它是开源的)中“窃取”的方法。希望对您有所帮助。【参考方案4】:

从 .Net 5.0 开始,您可以像这样从字符串中导入 RSA 公钥:

var rsaPublicKey = RSA.Create();
rsaPublicKey.ImportFromPem(publicKeyString);

如果您不知道如何将文件读取到字符串,请参阅

How to read an entire file to a string using C#?

【讨论】:

【参考方案5】:
string private_key = File.ReadAllText(private_key_file).Trim();
Regex regex = new Regex(@"-----(BEGIN|END) (RSA|OPENSSH|ENCRYPTED) PRIVATE KEY-----[\W]*");
string private_key_base64 = regex.Replace(private_key, "");
byte[] bytes_privake_key = Convert.FromBase64String(private_key_base64);
rsa.ImportRSAPrivateKey(new ReadOnlySpan<byte>(bytes_privake_key), out bytesRead);

【讨论】:

以上是关于如何在 C# 中从文件中加载 RSA 公钥的主要内容,如果未能解决你的问题,请参考以下文章

如何从 RSA 公钥计算指纹?

如何在反应中从文件中加载数据?

如何在 Qt 中从 txt 文件中加载大数据

如何在 C# 中加载私有 ecdsa 密钥 es256?

如何在 Symfony 2 中从功能测试中加载固定装置

如何从 RSA 公钥 XML 文件 .net core c# 中获取 n 值