在使用公共密钥在php中加密后,如何使用私有密钥在c#中的块中解密数据?

Kraang Prime

如何使用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示例,其格式已设置为替换上述示例中的以下行以测试分块,因为上述$outputjson太小而无法使分块生效。

$output = json_encode(array('see'=>'me'));

解释上面的代码的作用

上面的代码是此解决方案的修改,它将数据分成较小的块(每个块100个字节),并使用pem格式的公共密钥对其进行加密。

客观的

我正在研究对大于几个字节的数据进行加密以更安全地传输数据,并且发现使用证书进行加密/解密是最好的选择。

目的是加密php中的数据(使用私钥),然后将数据以C#编写的应用程序接收并解密(使用公钥)。

C#-到目前为止的路


以下是我在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;
    }
}

这依赖于其他方法(从opensslkey.cs收获

//--------   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'))使用我当前的公共密钥进行加密的base64解码最终为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进行测试

马丁·波德维斯(Maarten Bodewes)

与问题的作者进行广泛交谈之后,似乎在代码中有两个(主要)问题使该问题无法正常工作:

  1. 未读取公共密钥,因为此stackoverflow解决方案中代码实际上不是创建二进制公共密钥而是证书。为此,X509Certificate可以使用构造函数,后跟GetPublicKeystackoverflow解决方案中的方法应该以不同的方式命名。后来将其更改为私钥(因为使用公钥进行的解密不提供机密性)。

  2. 加密块的大小被认为是100个字节,而密钥大小是4096位(512字节)。但是,RSA(如PKCS#1 v2.1中为PKCS#1 v1.5填充所指定的)始终会加密为以字节为单位的RSA密钥大小(模数大小)。因此,解密的输入也应该是512字节的块。但是,如果输出被加密(在PHP代码中),则输出将为100字节。

为了使这项工作有效,有必要进行一些小的修改,以循环遍历基于其计算的块中的加密数据的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;
}

安全说明:

  • PKCS#1 v1.5填充容易受到oracle填充攻击,最好确保您不允许这样做,尤其是在传输协议中(或使用更新的OAEP填充);
  • 在使用前请先信任您的公钥,否则可能会受到中间人攻击。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在php中加密的C#中解密数据时,如何解决无效的密钥大小

来自分类Dev

如何使用PHP中的对称密钥算法对结果中没有元字符的数据进行加密

来自分类Dev

如何使用NodeJS中的公共密钥仅加密JSON有效负载的选定值?

来自分类Dev

如何使用RSA for Chilkat在C#中加密内容并在Java中解密内容?

来自分类Dev

我如何在使用Rijndael的C#中加密的ios中解密文件

来自分类Dev

如何使用C#加密OpenPGP密钥?

来自分类Dev

如何使用匹配的DER私钥解密公共PEM密钥加密的字节数组?

来自分类Dev

在使用C#加密的PostgreSQL查询中解密数据

来自分类Dev

使用bouncycastle在C#中使用cryptodome解密在python中加密的RSA数据会导致错误块不正确

来自分类Dev

使用AESCrypt在Android中加密数据并在Ruby中解密

来自分类Dev

使用Bouncy Castle在C#中加密和使用AES在Python中解密的问题(EAX模式)

来自分类Dev

PHP中的用户解密/加密| 在会话中存储密钥

来自分类Dev

使用私有指数和模数解密64位块中RSA加密文件的Python代码

来自分类Dev

使用私有指数和模数解密64位块中RSA加密文件的Python代码

来自分类Dev

Flexiprovider-如何使用格式化的密钥对加密/解密

来自分类Dev

Python仅使用公共密钥解密签名

来自分类Dev

使用C#中收到的pem或pkcs密钥解密字符串

来自分类Dev

通用加密-测试解密中的错误密钥?

来自分类Dev

在Java中,如何使用JKS密钥库中的X509证书(公用/专用密钥对)中的专用密钥解密?

来自分类Dev

在Java中,如何使用JKS密钥库中的X509证书(公用/专用密钥对)中的专用密钥解密?

来自分类Dev

使用现有DER / PEM密钥的Javascript中的RSA加密

来自分类Dev

使用现有DER / PEM密钥的Javascript中的RSA加密

来自分类Dev

给定私有SSH密钥,如何恢复我的公共密钥?

来自分类Dev

使用Rijndael(C#)加密的PHP解密数据

来自分类Dev

为什么在C#中加密后,node.js中的RSA解密失败?

来自分类Dev

如何在 Python 的加密模块中访问私有 RSA 密钥的组件?

来自分类Dev

使用C#中的512位公共密钥验证RSA标志

来自分类Dev

加密中密钥使用错误

来自分类Dev

加密中密钥使用错误

Related 相关文章

  1. 1

    在php中加密的C#中解密数据时,如何解决无效的密钥大小

  2. 2

    如何使用PHP中的对称密钥算法对结果中没有元字符的数据进行加密

  3. 3

    如何使用NodeJS中的公共密钥仅加密JSON有效负载的选定值?

  4. 4

    如何使用RSA for Chilkat在C#中加密内容并在Java中解密内容?

  5. 5

    我如何在使用Rijndael的C#中加密的ios中解密文件

  6. 6

    如何使用C#加密OpenPGP密钥?

  7. 7

    如何使用匹配的DER私钥解密公共PEM密钥加密的字节数组?

  8. 8

    在使用C#加密的PostgreSQL查询中解密数据

  9. 9

    使用bouncycastle在C#中使用cryptodome解密在python中加密的RSA数据会导致错误块不正确

  10. 10

    使用AESCrypt在Android中加密数据并在Ruby中解密

  11. 11

    使用Bouncy Castle在C#中加密和使用AES在Python中解密的问题(EAX模式)

  12. 12

    PHP中的用户解密/加密| 在会话中存储密钥

  13. 13

    使用私有指数和模数解密64位块中RSA加密文件的Python代码

  14. 14

    使用私有指数和模数解密64位块中RSA加密文件的Python代码

  15. 15

    Flexiprovider-如何使用格式化的密钥对加密/解密

  16. 16

    Python仅使用公共密钥解密签名

  17. 17

    使用C#中收到的pem或pkcs密钥解密字符串

  18. 18

    通用加密-测试解密中的错误密钥?

  19. 19

    在Java中,如何使用JKS密钥库中的X509证书(公用/专用密钥对)中的专用密钥解密?

  20. 20

    在Java中,如何使用JKS密钥库中的X509证书(公用/专用密钥对)中的专用密钥解密?

  21. 21

    使用现有DER / PEM密钥的Javascript中的RSA加密

  22. 22

    使用现有DER / PEM密钥的Javascript中的RSA加密

  23. 23

    给定私有SSH密钥,如何恢复我的公共密钥?

  24. 24

    使用Rijndael(C#)加密的PHP解密数据

  25. 25

    为什么在C#中加密后,node.js中的RSA解密失败?

  26. 26

    如何在 Python 的加密模块中访问私有 RSA 密钥的组件?

  27. 27

    使用C#中的512位公共密钥验证RSA标志

  28. 28

    加密中密钥使用错误

  29. 29

    加密中密钥使用错误

热门标签

归档