[英]How can I decrypt data in chunks in c# using a private key after encrypting in php using a public key?
如何使用C#中的私鑰(pem格式)解密此代碼的輸出?
$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個字節(即使總字節數更少),也可以對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 v1.5中針對PKCS#1 v1.5填充所指定的) 始終會加密為以字節為單位的RSA密鑰大小(模數大小)。 因此,解密的輸入也應該是512字節的塊。 但是,如果將其加密(在PHP代碼中),則輸出為100字節。
為了使這項工作有效,有必要進行少量修改以循環遍歷基於KeySize / 8
計算的塊中的加密數據的base64解碼字節(其中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;
}
安全說明:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.