Используя код подписи закрытого ключа для шифрования в PHP, необходимо расшифровать в C # с помощью открытого ключа

Я написал метод, который использует мой закрытый ключ (оттепель что закрытый ключ не подлежит распространению. Подписание происходит с использованием закрытого ключа и встраивает открытый ключ в файл для проверки подписи).

Ниже приведен код 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 );
}

Вот скриншот ошибки, которую я получаю:

Ключ не существует

Поэтому я попытался попробовать что-то более сложное, используя код, полученный из Извлечение модулей и компонентов (RSAParameter) из сертификата X509 PublicKey в попытке заполнить RSAParameter необходимым модулем / etc, чтобы завершить расшифровку ……, это все еще терпит неудачу.

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), однако их утверждение «это невозможно» не является логичным или обоснованным.

Я ищу серьезный продуманный ответ на это. Из того, что я проверил, загрузка сертификата / и т. Д. Не так сложна, это просто поиск правильной комбинации команд для задачи.

Заранее спасибо.

(Добавление примечания, что я не ищу использования сторонних библиотек. Это возможно только с использованием .NET Framework.)

0

Решение

То, что вы на самом деле делаете здесь, это проверка и восстановление короткого подписанного сообщения — это не сработает для сообщений, размер которых превышает 200 байт, поскольку он напрямую использует пару ключей RSA.

РЕДАКТИРОВАТЬЭто может сработать. Я не знал о PublicKey.Key выполняя за вас работу по сглаживанию параметров, так что я был занят написанием экрана, полного гадости на побитие байтов. Вам также не нужен код преобразования байтов в строку. К моему крайнему удивлению, конечный результат на самом деле получается короче. 🙂

public string Decrypt( string Key, string Data ) {
var cert = new X509Certificate2( Key );
var rsa = (RSACryptoServiceProvider) cert.PublicKey.Key;
return Encoding.Default.GetString( rsa.Decrypt( Convert.FromBase64String( Data ), false ) );
}

Кроме того, я настоятельно рекомендую кастинг в стиле C вместо as в этом случае, так как он бросает InvalidCastException незамедлительно у оператора, если приведение не удалось. asтрансляции молча возвращаются null, вызывая следующую строку, чтобы бросить NullReferenceException вместо.


Я могу предложить вариант, который использует Надувной Замок библиотека, поскольку она предоставляет обширную поверхность API, включая некоторые возможности, подобные OpenSSL.

using System.IO;

using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.X509;

internal class Example
{
internal static string Decode(string data)
{
try
{
X509Certificate k;

// read in the certificate
using (var textReader = File.OpenText("public.cer"))
{
// I'm assuming here that the certificate is PEM-encoded.
var pemReader = new PemReader(textReader);
k = (X509Certificate)pemReader.ReadObject();
}

// un-base64 the input
var dataBytes = Convert.FromBase64String(data);

// what php openssl_public_decrypt does
var cipher = new OaepEncoding(new RsaEngine());
cipher.Init(false, k.GetPublicKey());
var returnBytes = cipher.ProcessBlock(dataBytes, 0, dataBytes.Length);

// What is the character-encoding of the bytes? UTF-8?
return Encoding.UTF8.GetString(returnBytes);
}
catch (Exception ex)
{
return "Failed:" + ex.Message;
}
}
}
1

Другие решения

Других решений пока нет …