Как мне расшифровать вывод этого кода, используя закрытый ключ (формат pem) в C #?
$output = json_encode(array('see'=>'me'));
define('CIPHER_BLOCK_SIZE', 100);
$encrypted = '';
$key = file_get_contents('public.txt');
$chunks = str_split($output, CIPHER_BLOCK_SIZE);
foreach($chunks as $chunk)
{
$chunkEncrypted = '';
$valid = openssl_public_encrypt($chunk, $chunkEncrypted, $key, OPENSSL_PKCS1_PADDING);
if($valid === false){
$encrypted = '';
break; //also you can return and error. If too big this will be false
} else {
$encrypted .= $chunkEncrypted;
}
}
$output = base64_encode($encrypted); //encoding the whole binary String as MIME base 64
echo $output;
Нажмите здесь, чтобы получить большой образец в формате json заменить следующую строку в приведенном выше примере, чтобы проверить порцию, как $output
json выше слишком мал для того, чтобы фрагменты вступили в силу.
$output = json_encode(array('see'=>'me'));
Приведенный выше код является модификацией это решение который разбивает данные на более мелкие порции (100 байт на порцию) и шифрует их, используя открытый ключ в формате pem.
Я смотрю на шифрование размером более нескольких байтов для более безопасной передачи данных и обнаружил, что шифрование / дешифрование с использованием сертификатов — лучший путь.
Намерение состоит в том, чтобы зашифровать данные в php (используя закрытый ключ), которые затем будут получены в приложении, написанном на C #, и расшифрованы (используя открытый ключ).
Использование :
// location of private certificate
string key = @"C:\path\to\private.txt";
// output from php script (encrypted)
string encrypted = "Bdm4s7aw.....Pvlzg=";
// decrypt and store decrypted string
string decrypted = crypt.decrypt( encrypted, key );
Учебный класс :
public static string decrypt(string encrypted, string privateKey) {
try {
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo( DecodePkcs8PrivateKey( File.ReadAllText( privateKey ) ) );
return Encoding.UTF8.GetString( rsa.Decrypt( Convert.FromBase64String( encrypted ), false ) );
} catch (CryptographicException ce) {
return ce.Message;
} catch (FormatException fe) {
return fe.Message;
} catch (IOException ie) {
return ie.Message;
} catch (Exception e) {
return e.Message;
}
}
//-------- Get the binary PKCS #8 PRIVATE key --------
private static byte[] DecodePkcs8PrivateKey( string instr ) {
const string pemp8header = "-----BEGIN PRIVATE KEY-----";
const string pemp8footer = "-----END PRIVATE KEY-----";
string pemstr = instr.Trim();
byte[] binkey;
if ( !pemstr.StartsWith( pemp8header ) || !pemstr.EndsWith( pemp8footer ) )
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pemp8header, "" ); //remove headers/footers, if present
sb.Replace( pemp8footer, "" );
string pubstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try {
binkey = Convert.FromBase64String( pubstr );
} catch ( FormatException ) { //if can't b64 decode, data is not valid
return null;
}
return binkey;
}
//------- Parses binary asn.1 PKCS #8 PrivateKeyInfo; returns RSACryptoServiceProvider ---
private static RSACryptoServiceProvider DecodePrivateKeyInfo( byte[] pkcs8 ) {
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"// this byte[] includes the sequence byte and terminal encoded null
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( pkcs8 );
int lenstream = (int)mem.Length;
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;bt = binr.ReadByte();
if ( bt != 0x02 )
return null;
twobytes = binr.ReadUInt16();
if ( twobytes != 0x0001 )
return null;
seq = binr.ReadBytes( 15 ); //read the Sequence OID
if ( !CompareBytearrays( seq, SeqOID ) ) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if ( bt != 0x04 ) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if ( bt == 0x81 )
binr.ReadByte();
else
if ( bt == 0x82 )
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes( (int)( lenstream - mem.Position ) );
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey( rsaprivkey );
return rsacsp;
} catch ( Exception ) {
return null;
} finally { binr.Close(); }
}
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
private 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 );
// ------- 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;
}
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;
}
Теперь это все функционально, но все же не включает в себя разбиение на части в процессе расшифровки.
Что я должен сделать, чтобы прочитать эти блоки, так как большие файлы определенно будут больше, чем исходные незашифрованные данные.
Моя предыдущая попытка состояла в том, чтобы попробовать что-то вроде следующего кода, но это кажется ошибочным, так как он всегда дополняет 100 байтов (даже когда общее количество байтов меньше), и декодирование base64 json_encode(array('see'=>'me'))
используя мой текущий открытый ключ для шифрования, получается 512 байт.
byte[] buffer = new byte[100]; // the number of bytes to decrypt at a time
int bytesReadTotal = 0;
int bytesRead = 0;
string decrypted = "";
byte[] decryptedBytes;
using ( Stream stream = new MemoryStream( data ) ) {
while ( ( bytesRead = await stream.ReadAsync( buffer, bytesReadTotal, 100 ) ) > 0 ) {
decryptedBytes = rsa.Decrypt( buffer, false );
bytesReadTotal = bytesReadTotal + bytesRead;
decrypted = decrypted + Encoding.UTF8.GetString( decryptedBytes );
}
}
return decrypted;
Для вашего удобства я поставил php скрипт создать открытый и закрытый ключ для тестирования на tehplayground.com.
После обширного разговора с автором вопроса кажется, что в коде есть две (основные) проблемы, которые мешают его работе:
Открытый ключ не был прочитан как код из этого решения stackoverflow на самом деле не создает двоичный открытый ключ, но сертификат. Для этого X509Certificate
конструктор может быть использован с последующим GetPublicKey
, Метод в решении stackoverflow должен был называться по-другому. Позже это было изменено на закрытый ключ (поскольку расшифровка с использованием открытого ключа не обеспечивает конфиденциальность).
Предполагалось, что зашифрованные блоки имеют размер 100 байт, а размер ключа — 4096 бит (512 байт). Однако RSA (как указано в PKCS # 1 v2.1 для заполнения PKCS # 1 v1.5) всегда шифрует точно до размера ключа RSA (размер модуля) в байтах. Таким образом, вход для дешифрования должен быть также кусками по 512 байт. Вывод, однако, будет 100 байтов, если это было зашифровано (в коде PHP).
Чтобы сделать эту работу, была необходима небольшая модификация, чтобы зациклить хотя base64 декодированных байтов зашифрованных данных в блоках, рассчитанных на основе KeySize / 8
(где 8 — это сколько бит в байте, поскольку KeySize является int
значение, представляющее количество байтов каждого блока).
public static async Task<string> decrypt(string encrypted, string privateKey) {
// read private certificate into RSACryptoServiceProvider from file
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo( DecodePkcs8PrivateKey( File.ReadAllText( privateKey ) ) );
// decode base64 to bytes
byte[] encryptedBytes = Convert.FromBase64String( encrypted );
int bufferSize = (int)(rsa.KeySize / 8);
// initialize byte buffer based on certificate block size
byte[] buffer = new byte[bufferSize]; // the number of bytes to decrypt at a time
int bytesReadTotal = 0; int bytesRead = 0;
string decrypted = ""; byte[] decryptedBytes;
// convert byte array to stream
using ( Stream stream = new MemoryStream( encryptedBytes ) ) {
// loop through stream for each block of 'bufferSize'
while ( ( bytesRead = await stream.ReadAsync( buffer, bytesReadTotal, bufferSize ) ) > 0 ) {
// decrypt this chunk
decryptedBytes = rsa.Decrypt( buffer, false );
// account for bytes read & decrypted
bytesReadTotal = bytesReadTotal + bytesRead;
// append decrypted data as string for return
decrypted = decrypted + Encoding.UTF8.GetString( decryptedBytes );
}
}
return decrypted;
}
Примечания по безопасности:
Других решений пока нет …