【发布时间】:2015-07-24 02:20:56
【问题描述】:
我编写了一个使用我的私钥的方法(thawte states 表示不会分发私钥。使用私钥进行签名,并将公钥嵌入文件中以验证签名)。
以下是我用来加密的 PHP 代码(我可以使用我在 PHP 中的公钥成功解密)
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
echo decode(sign("hello world"));
function sign($data) {
$k = openssl_pkey_get_private(file_get_contents("key.pem"));
$ret = "";
$output = "";
try{
if( openssl_private_encrypt( $data, $output, $k ) ) {
$ret = base64_encode($output);
} else {
$ret = "Failed";
}
} catch(Exception $e) {
$ret = "Failed:" . $e->getMessage();
}
return $ret;
}
function decode($data) {
$k = openssl_get_publickey(file_get_contents("public.cer"));
$ret = "";
$output = "";
try{
if( openssl_public_decrypt( base64_decode($data), $output, $k ) ) {
$ret = $output;
} else {
$ret = "Failed";
}
} catch(Exception $e) {
$ret = "Failed:" . $e->getMessage();
}
return $ret;
}
我也尝试过使用这里提供的库phpseclib,它声称是一种纯 PHP 加密方法,使用以下代码创建加密数据:
include('Math/BigInteger.php');
include('Crypt/RSA.php');
echo sign2("Hello World");
function sign2($data) {
$rsa = new Crypt_RSA();
extract($rsa->createKey());
$rsa->loadKey(file_get_contents("key.pem"));
$ciphertext = $rsa->encrypt($data);
return base64_encode($ciphertext);
}
这是我目前在 C# 中的内容,但它导致 rsa.DecryptValue() 出现异常
public string Decrypt( string Key, string Data ) {
X509Certificate2 cert=new X509Certificate2( Key, "", X509KeyStorageFlags.Exportable );
RSACryptoServiceProvider rsa=cert.PublicKey.Key as RSACryptoServiceProvider;
return GetString( rsa.DecryptValue( Convert.FromBase64String( Data ) ) );
}
static string GetString( byte[] bytes ) {
char[] chars=new char[bytes.Length/sizeof( char )];
System.Buffer.BlockCopy( bytes, 0, chars, 0, bytes.Length );
return new string( chars );
}
这是我收到的错误的屏幕截图:
所以我尝试使用从Extracting Modules and Component(RSAParameter) from X509Certificate PublicKey 获得的代码尝试一些更复杂的东西,试图用所需的模数/等填充 RSAParameter 以完成解密......它仍然失败。
using System;
using System.Threading;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace TestCase
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class TestCase
{
public static void Main(string[] certfilex)
{
string[] certfile = { @"D:\Server\www\127.0.0.1\htdocs\sign\public.cer" };
string enc = ""; // Base64 encoded string is here, but removed for posting on SO
if (certfile == null)
{
Console.WriteLine("Certificate filename is not valid");
Environment.Exit(0);
}
try
{
BinaryReader br = new BinaryReader(File.OpenRead(certfile[0]));
byte[] buf = new byte[br.BaseStream.Length];
br.Read(buf, 0, buf.Length);
X509Certificate cert = new X509Certificate(buf);
RSAParameters p = X509PublicKeyParser.GetRSAPublicKeyParameters(cert);
X509Certificate2 crt=new X509Certificate2( certfile[0], "", X509KeyStorageFlags.Exportable );
using ( RSACryptoServiceProvider rsa=crt.PublicKey.Key as RSACryptoServiceProvider ) {
rsa.ImportParameters(p);
byte[] unb64 = Convert.FromBase64String(enc);
// Array.Reverse(unb64);
try {
byte[] results=rsa.Decrypt( unb64, false );
Console.WriteLine( GetString( results ) );
} catch ( CryptographicException ce ) {
Console.WriteLine(ce.Message);
}
}
// HexDump("Modulus:", p.Modulus);
// HexDump("Exponent:", p.Exponent);
}
catch (Exception e)
{
Console.WriteLine(e.Message + " \r\n " + e.StackTrace);
Environment.Exit(0);
}
Console.ReadLine();
}
static byte[] GetBytes( string str ) {
byte[] bytes=new byte[str.Length*sizeof( char )];
System.Buffer.BlockCopy( str.ToCharArray(), 0, bytes, 0, bytes.Length );
return bytes;
}
static string GetString( byte[] bytes ) {
char[] chars=new char[bytes.Length/sizeof( char )];
System.Buffer.BlockCopy( bytes, 0, chars, 0, bytes.Length );
return new string( chars );
}
/// <summary>
/// Converts and writes the bytes as hexadecimal values
/// </summary>
/// <param name="label">Dump header</param>
/// <param name="ba">value to be printed</param>
public static void HexDump(string label, byte[] ba)
{
const string SPC = " ";
string tid = Thread.CurrentThread.Name;
if (ba == null)
{
ba = new byte[0];
}
Console.WriteLine(label + " [" + tid + "], " + ba.Length);
StringBuilder buf = new StringBuilder();
string LINE_SEP = Environment.NewLine;
for (int i = 0; i < ba.Length; i++)
{
if (i > 0 && (i % 16) == 0)
{
buf.Append(LINE_SEP);
}
if (i % 8 == 0)
{
buf.Append(SPC);
}
string str = Convert.ToString(ba[i], 16);
if (str.Trim().Length == 1)
{
str = "0" + str.Trim();
}
buf.Append(str + SPC);
}
if (ba.Length > 0)
{
Console.WriteLine(buf.ToString().ToUpper());
}
}
}
internal class IntegerContainer : AbstractAsn1Container { internal IntegerContainer( byte[] abyte, int i ) : base( abyte, i, 0x2 ) { } }
internal class SequenceContainer : AbstractAsn1Container { internal SequenceContainer( byte[] abyte, int i ) : base( abyte, i, 0x30 ) { } }
public class X509PublicKeyParser
{
public static RSAParameters GetRSAPublicKeyParameters(byte[] bytes) { return GetRSAPublicKeyParameters(bytes, 0); }
public static RSAParameters GetRSAPublicKeyParameters(byte[] bytes, int i) {
SequenceContainer seq = new SequenceContainer(bytes, i);
IntegerContainer modContainer = new IntegerContainer(seq.Bytes, 0);
IntegerContainer expContainer = new IntegerContainer(seq.Bytes, modContainer.Offset);
return LoadKeyData(modContainer.Bytes, 0, modContainer.Bytes.Length, expContainer.Bytes, 0, expContainer.Bytes.Length);
}
public static RSAParameters GetRSAPublicKeyParameters(X509Certificate cert) { return GetRSAPublicKeyParameters(cert.GetPublicKey(), 0); }
private static RSAParameters LoadKeyData(byte[] abyte0, int i, int j, byte[] abyte1, int k, int l) {
byte[] modulus = null;
byte[] publicExponent = null;
for(; abyte0[i] == 0; i++)
j--;
modulus = new byte[j];
Array.Copy(abyte0, i, modulus, 0, j);
int i1 = modulus.Length * 8;
int j1 = modulus[0] & 0xff;
for(int k1 = j1 & 0x80; k1 == 0; k1 = j1 << 1 & 0xff)
i1--;
if(i1 < 256 || i1 > 2048)
throw new X509ParserException("Invalid RSA modulus size.");
for(; abyte1[k] == 0; k++)
l--;
publicExponent = new byte[l];
Array.Copy(abyte1, k, publicExponent, 0, l);
RSAParameters p = new RSAParameters();
p.Modulus = modulus;
p.Exponent = publicExponent;
return p;
}
}
public class X509ParserException : SystemException {
public X509ParserException() : base() { }
public X509ParserException( string msg ) : base( msg ) { }
public X509ParserException( string msg, Exception e ) : base( msg, e ) { }
}
/// <summary>
/// Summary description for AbstractAsn1Container.
/// </summary>
internal abstract class AbstractAsn1Container {
private int offset;
private byte[] data;
private byte tag;
internal protected AbstractAsn1Container( byte[] abyte, int i, byte tag ) {
this.tag=tag;
if ( abyte[i]!=tag ) {
throw new X509ParserException( "Invalid data. The tag byte is not valid" );
}
int length=DetermineLength( abyte, i+1 );
int bytesInLengthField=DetermineLengthLen( abyte, i+1 );
int start=i+bytesInLengthField+1;
this.offset=start+length;
data=new byte[length];
Array.Copy( abyte, start, data, 0, length );
}
internal int Offset {
get {
return offset;
}
}
internal byte[] Bytes {
get {
return this.data;
}
}
internal protected virtual int DetermineLengthLen( byte[] abyte0, int i ) {
int j=abyte0[i]&0xff;
switch ( j ) {
case 129:
return 2;
case 130:
return 3;
case 131:
return 4;
case 132:
return 5;
case 128:
default:
return 1;
}
}
internal protected virtual int DetermineLength( byte[] abyte0, int i ) {
int j=abyte0[i]&0xff;
switch ( j ) {
case 128:
return DetermineIndefiniteLength( abyte0, i );
case 129:
return abyte0[i+1]&0xff;
case 130:
int k=( abyte0[i+1]&0xff )<<8;
k|=abyte0[i+2]&0xff;
return k;
case 131:
int l=( abyte0[i+1]&0xff )<<16;
l|=( abyte0[i+2]&0xff )<<8;
l|=abyte0[i+3]&0xff;
return l;
}
return j;
}
internal protected virtual int DetermineIndefiniteLength( byte[] abyte0, int i ) {
if ( ( abyte0[i-1]&0xff&0x20 )==0 )
throw new X509ParserException( "Invalid indefinite length." );
int j=0;
int k;
int l;
for ( i++; abyte0[i]!=0&&abyte0[i+1]!=0; i+=1+k+l ) {
j++;
k=DetermineLengthLen( abyte0, i+1 );
j+=k;
l=DetermineLength( abyte0, i+1 );
j+=l;
}
return j;
}
}
}
我想要做的是与上面的解码方法等效,但是在 C# 中。我已经看到有人在互联网上(包括在 StackOverflow 上)向他们的回复发送垃圾邮件,但是他们声称“不可能”是不合逻辑或无效的。
我正在寻找一个经过深思熟虑的严肃答案。根据我的测试,加载证书/等并不复杂,它只是为任务找到正确的命令组合。
提前致谢。
(添加说明我不是在寻找使用 3rd 方库。这仅使用 .NET 框架是可行的。)
【问题讨论】:
-
phpseclib 默认使用 OAEP 模式进行加密。它比 PKCS1 模式的替代方案更安全,但也不太常用。要使用 PKCS1 模式,请在
$rsa->encrypt()之前执行$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);。
标签: c# php encryption certificate