C# 用RSA编码字符串

C# 用RSA编码字符串,c#,encryption,rsa,C#,Encryption,Rsa,我正在使用一个API,该API指定某些文本必须通过“RSA/ECB/PKCS1Padding”编码传递。公钥以.PEM格式提供;我想我已经成功地将其转换为XML格式,现在我正在使用以下C代码: 但是,返回的字符串与预期的不匹配-API给出了以下OpenSSL命令作为示例: openssl rsautl -encrypt -in PIN.txt -inkey public_key_for_pin.pem -pubin | openssl base64 > PIN.base64 OpenSS

我正在使用一个API,该API指定某些文本必须通过“RSA/ECB/PKCS1Padding”编码传递。公钥以.PEM格式提供;我想我已经成功地将其转换为XML格式,现在我正在使用以下C代码:

但是,返回的字符串与预期的不匹配-API给出了以下OpenSSL命令作为示例:

openssl rsautl -encrypt -in PIN.txt -inkey public_key_for_pin.pem -pubin | openssl base64 > PIN.base64

OpenSSL的输出与我的代码的输出不匹配。有人能看到我的代码有什么问题吗;我是否需要指定特定的填充,或者在从PEM到XML的转换过程中可能损坏了公钥文件?

我这样做是为了用公钥加密数据

首先将PEM数据加载到
X509Certificate2
类中,它具有可以使用的导入方法

然后我使用以下代码:

using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography;

public static string EncryptString(string clearText, X509Certificate2 cert)
{
    try
    {
        byte[] encodedCypher = EncryptData(Encoding.UTF8.GetBytes(clearText), cert);
        string cipherText = Convert.ToBase64String(encodedCypher);

        return cipherText;
    }
    catch (Exception ex)
    {
        throw new EncryptionException("Could not Encrypt String. See InnerException for details.", ex);
    }
}

private static byte[] EncryptData(byte[] clearText, X509Certificate2 cert)
{
    ContentInfo payloadInfo = new ContentInfo(clearText);
    EnvelopedCms payloadEnvelope = new EnvelopedCms(payloadInfo);
    CmsRecipient certHandle = new CmsRecipient(cert);
    payloadEnvelope.Encrypt(certHandle);
    return payloadEnvelope.Encode();
}

输出不应该匹配。出于安全原因,使用PKCS#1 block type 2的RSA公钥加密会插入随机填充。

在花了一些时间研究公钥加密后,我想我会对这个旧线程有所贡献,这样将来可能会有人发现它有用

使用仅包含public.key的文件初始化
X509Certificate2
对象的实例无效。它需要证书,因此出现“找不到请求的对象”错误

为了只使用
public.key
文件加密某些内容,必须执行以下操作

using System;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
namespace Program
{
    class Program
    {
        static void Main( string[] args ) {

            // Load client's public key file.
            //
            RSAPublicKey = File.ReadAllText( @"public.key" );

            // Decode it
            //
            byte[] PEMPublicKey = DecodeOpenSSLPublicKey( RSAPublicKey );

            if( PEMPublicKey == null ){
                throw new Exception( "Could not decode RSA public key." );
            }

            //Create a new instance of RSACryptoServiceProvider with appropriate public key
            //
            RSACryptoServiceProvider RSA = DecodeX509PublicKey( PEMPublicKey );


            // Should you want to save your XMLPublicKey for future use of RSA.FromXmlString( XMLPublicKey );
            //
            // String XMLPublicKey = RSA.ToXmlString( false );


            byte[] cypher = RSA.Encrypt( Encoding.ASCII.GetBytes( "This is a test" ), false );
            File.WriteAllBytes( @"cypher.txt", cypher );

            return;

        }

        // The following code has been obtained from:
        // http://csslab.s3.amazonaws.com/csslabs/Siva/opensslkey.cs
        // All credits go to the unknown author
        //
        // --------   Get the binary RSA PUBLIC key   --------
        //
        public 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;
        }


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


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

        }

        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 byte[] Combine( byte[] first, byte[] second ) {
            byte[] ret = new byte[ first.Length + second.Length ];
            Buffer.BlockCopy( first, 0, ret, 0, first.Length );
            Buffer.BlockCopy( second, 0, ret, first.Length, second.Length );
            return ret;
        }

    }
}
您可以使用openssl测试结果,如下所示:

您根本没有设置填充。您知道默认值吗?@Henk Holterman:填充由
rsa1.Encrypt()
调用中的
false
参数指定<代码>错误获取PKCS#1块类型2填充。谢谢-这正是我需要的。但是,我现在无法导入PEM文件。PEM文件只有一个公钥-例如:
——BEGIN公钥----migfma0gcsqgsib3dqebaquaa4gnadcbiqbgggqdviel1dgxnkdy4iumdhbivy3oz TlgnU+cfl9hlQgWFdFb2XwYvdj57ApeTbN8Gwaf6g/80+8xliowm+Zrl1n8ebooE TlgnU+cfl9hlqgwfdfb2xwyvdj57apetb8gww6g/80+8xliowm+Zrl1n8ebooE----END公钥----
(不是真正的密钥)。我无法让.Import()方法接受这一点,您知道如何在没有私钥的情况下将PEM文件读入X509Certificate2吗?如果文件系统中有.PEM文件,您可以按文件名导入它,我认为.cer是您可以使用的扩展名(如果不是.cer,请尝试更改扩展名)。还可以将原始字节数组与导入方法一起使用。您使用的导入方法重载不需要密码,它将仅加载带有公钥的X509Certificate2类。在尝试导入文件时,我遇到“找不到请求的对象”错误。我的代码看起来像:
X509Certificate2Cert=newX509Certificate2();cert.Import(“c:\\test\\test.cer”)
。它肯定在查找文件,我尝试将其更改为原始字节数组,但仍然得到相同的错误:(在windows资源管理器中双击test.cer文件时会发生什么情况?也找到了。
using System;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
namespace Program
{
    class Program
    {
        static void Main( string[] args ) {

            // Load client's public key file.
            //
            RSAPublicKey = File.ReadAllText( @"public.key" );

            // Decode it
            //
            byte[] PEMPublicKey = DecodeOpenSSLPublicKey( RSAPublicKey );

            if( PEMPublicKey == null ){
                throw new Exception( "Could not decode RSA public key." );
            }

            //Create a new instance of RSACryptoServiceProvider with appropriate public key
            //
            RSACryptoServiceProvider RSA = DecodeX509PublicKey( PEMPublicKey );


            // Should you want to save your XMLPublicKey for future use of RSA.FromXmlString( XMLPublicKey );
            //
            // String XMLPublicKey = RSA.ToXmlString( false );


            byte[] cypher = RSA.Encrypt( Encoding.ASCII.GetBytes( "This is a test" ), false );
            File.WriteAllBytes( @"cypher.txt", cypher );

            return;

        }

        // The following code has been obtained from:
        // http://csslab.s3.amazonaws.com/csslabs/Siva/opensslkey.cs
        // All credits go to the unknown author
        //
        // --------   Get the binary RSA PUBLIC key   --------
        //
        public 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;
        }


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


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

        }

        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 byte[] Combine( byte[] first, byte[] second ) {
            byte[] ret = new byte[ first.Length + second.Length ];
            Buffer.BlockCopy( first, 0, ret, 0, first.Length );
            Buffer.BlockCopy( second, 0, ret, first.Length, second.Length );
            return ret;
        }

    }
}