C# 用RSA编码字符串
我正在使用一个API,该API指定某些文本必须通过“RSA/ECB/PKCS1Padding”编码传递。公钥以.PEM格式提供;我想我已经成功地将其转换为XML格式,现在我正在使用以下C代码: 但是,返回的字符串与预期的不匹配-API给出了以下OpenSSL命令作为示例: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
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;
}
}
}