Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

William Reis Fernandes

PHP Rijndael - C# Rijndael - Não combina o encrypt

Recommended Posts

Boa tarde Pessoal,

 

Vou explicar da melhor maneira possível sobre o problema, hoje eu tenho que "consumir" um Webservices criado em C# que realiza uma tratativa de dados enviados por encript ou seja, eu preciso enviar meu "POST" que é um JSON encriptado.

O código é esse que segue logo abaixo, ele cria um IV (Inicializador de Vetor), setando no Array de BYTES (_algorithm.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9, 0x5, 0x46, 0x9c, 0xea, 0xa8, 0x4b, 0x73, 0xcc };), e gera também uma chave apartir de uma string gerando outro array de bytes na função GetKey(), agora deixo o código para ler e sigo em baixo com o problema.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Security;
using System.Security.Cryptography;



namespace Criptografia
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        
    /// <summary>
    /// Enumerator com os tipos de classes para criptografia.
    /// </summary>

    public enum CryptProvider
    {
        /// <summary>
        /// Representa a classe base para implementações criptografia dos algoritmos simétricos Rijndael.
        /// </summary>
        Rijndael,
        /// <summary>
        /// Representa a classe base para implementações do algoritmo RC2.
        /// </summary>
        RC2,
        /// <summary>
        /// Representa a classe base para criptografia de dados padrões (DES - Data Encryption Standard).
        /// </summary>
        DES,
        /// <summary>
        /// Representa a classe base (TripleDES - Triple Data Encryption Standard).
        /// </summary>
        TripleDES

    }


    public class Criptografia

    {
        #region Variáveis e Métodos Privados

        private string _key = string.Empty;
        private CryptProvider _cryptProvider;
        private SymmetricAlgorithm _algorithm;


        /// <summary>
        /// Inicialização do vetor do algoritmo simétrico
        /// </summary>
        private void SetIV()
        {
            switch (_cryptProvider)
            {
                case CryptProvider.Rijndael:

                    _algorithm.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9, 0x5, 0x46, 0x9c, 0xea, 0xa8, 0x4b, 0x73, 0xcc };
                    break;

                default:

                    _algorithm.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9 };
                    break;
            }
        }


        #endregion


        #region Properties
        /// <summary>
        /// Chave secreta para o algoritmo simétrico de criptografia.
        /// </summary>
        public string Key

        {
            get { return _key; }
            set { _key = value; }
        }


        #endregion


        #region Constructors
        /// <summary>
        /// Contrutor padrão da classe, é setado um tipo de criptografia padrão (Rijndael).
        /// </summary>
        public Criptografia()
        {
            _algorithm = new RijndaelManaged();
            _algorithm.Mode = CipherMode.CBC;
            _cryptProvider = CryptProvider.Rijndael;

        }
        /// <summary>
        /// Construtor com o tipo de criptografia a ser usada Você pode escolher o tipo pelo Enum chamado CryptProvider.
        /// </summary>
        /// <param name="cryptProvider">Tipo de criptografia.</param>
        public Criptografia(CryptProvider cryptProvider)
             {      
                    // Seleciona algoritmo simétrico
                    switch(cryptProvider)
                    {
                           case CryptProvider.Rijndael:

                                  _algorithm = new RijndaelManaged();
                                 _cryptProvider = CryptProvider.Rijndael;
                                  break;

                           case CryptProvider.RC2:

                                  _algorithm = new RC2CryptoServiceProvider();
                                  _cryptProvider = CryptProvider.RC2;
                                  break;

                           case CryptProvider.DES:

                                  _algorithm = new DESCryptoServiceProvider();
                                  _cryptProvider = CryptProvider.DES;
                                  break;

                           case CryptProvider.TripleDES:

                                  _algorithm = new TripleDESCryptoServiceProvider();
                                  _cryptProvider = CryptProvider.TripleDES;
                                  break;
                    }
                    _algorithm.Mode = CipherMode.CBC;
             }

        #endregion


        #region Public methods
        /// <summary>
        /// Gera a chave de criptografia válida dentro do array.
        /// </summary>
        /// <returns>Chave com array de bytes.</returns>
        public virtual byte[] GetKey()
        {
            char pad = '*';
            string salt = string.Empty;
            // Ajusta o tamanho da chave se necessário e retorna uma chave válida
            if (_algorithm.LegalKeySizes.Length > 0)
            {
                // Tamanho das chaves em bits
                int keySize = _key.Length * 8;
                int minSize = _algorithm.LegalKeySizes[0].MinSize;
                int maxSize = _algorithm.LegalKeySizes[0].MaxSize;
                int skipSize = _algorithm.LegalKeySizes[0].SkipSize;
                if (keySize > maxSize)
                {
                    // Busca o valor máximo da chave
                    _key = _key.Substring(0, maxSize / 8);
                }
                else if (keySize < maxSize)
                {
                    // Seta um tamanho válido
                    int validSize = (keySize <= minSize) ? minSize : (keySize - keySize % skipSize) + skipSize;
                    if (keySize < validSize)
                    {
                        // Preenche a chave com arterisco para corrigir o tamanho
                        _key = _key.PadRight(validSize / 8, pad);
                    }
                }
            }
            PasswordDeriveBytes key = new PasswordDeriveBytes(_key, ASCIIEncoding.ASCII.GetBytes(salt));
            return key.GetBytes(_key.Length);
        }
        /// <summary>
        /// Encripta o dado solicitado.
        /// </summary>
        /// <param name="plainText">Texto a ser criptografado.</param>
        /// <returns>Texto criptografado.</returns>
        public virtual string Encrypt(string texto)
        {
            byte[] plainByte = Encoding.UTF8.GetBytes(texto);
            byte[] keyByte = GetKey();
            // Seta a chave privada
            _algorithm.Key = keyByte;
            SetIV();
            // Interface de criptografia / Cria objeto de criptografia
            ICryptoTransform cryptoTransform = _algorithm.CreateEncryptor();
            MemoryStream _memoryStream = new MemoryStream();
            CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Write);
            // Grava os dados criptografados no MemoryStream
            _cryptoStream.Write(plainByte, 0, plainByte.Length);
            _cryptoStream.FlushFinalBlock();
            // Busca o tamanho dos bytes encriptados
            byte[] cryptoByte = _memoryStream.ToArray();
            // Converte para a base 64 string para uso posterior em um xml
            return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0));
        }
        /// <summary>
        /// Desencripta o dado solicitado.
        /// </summary>
        /// <param name="cryptoText">Texto a ser descriptografado.</param>
        /// <returns>Texto descriptografado.</returns>
        public virtual string Decrypt(string textoCriptografado)
        {
            // Converte a base 64 string em num array de bytes
            byte[] cryptoByte = Convert.FromBase64String(textoCriptografado);
            byte[] keyByte = GetKey();
            // Seta a chave privada
            _algorithm.Key = keyByte;
            SetIV();
            // Interface de criptografia / Cria objeto de descriptografia
            ICryptoTransform cryptoTransform = _algorithm.CreateDecryptor();
            try
            {
                MemoryStream _memoryStream = new MemoryStream(cryptoByte, 0, cryptoByte.Length);
                CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Read);
                // Busca resultado do CryptoStream
                StreamReader _streamReader = new StreamReader(_cryptoStream);
                return _streamReader.ReadToEnd();
            }
            catch
            {
                return null;
            }
        }
        #endregion

    }

    private void btnCriptografar_Click(object sender, EventArgs e)
    {
        
        string texto = txtNormal.Text;
        string key = "Criptografia"; //Está chave você mesmo é quem escolhe.
        Criptografia crip = new Criptografia(CryptProvider.DES);

              crip.Key = key;

        txtCriptografado.Text = crip.Encrypt(texto);
        txtNormal.Text = string.Empty;

    }

    private void btnDecriptar_Click(object sender, EventArgs e)
    {
        
      string texto = txtCriptografado.Text;

      //Está chave tem que ser a mesma que a do texto Encriptado.

      string key = "Criptografia"; 
      Criptografia crip = new Criptografia(CryptProvider.DES);
      crip.Key = key;
      txtNormal.Text = crip.Decrypt(texto);
    
    }


   }
} 

E no PHP como faz? Bem vamos a algumas soluções que seu amigo aqui chegou, primeiramente procurando pela internet chegamos aos mais óbvios por exemplo:

<?php

// Test code

    $objEncManager = new DataEncryptor();

    $sensitiveData = "7890";
    echo "Raw Data: _" . $sensitiveData . "_<br><br>";

    $encryptedData = $objEncManager->mcryptEncryptString( $sensitiveData );
    echo "Enc Data: _" . $encryptedData . "_<br><br>";
    echo "Enc Data length: " . strlen( $encryptedData) . "<br><br>";

    $decryptedData = $objEncManager->mcryptDecryptString( $encryptedData, $objEncManager->lastIv );
    echo "D-enc Data: _" . $decryptedData . "_<br><br>";

    echo "IV: _" . $objEncManager->lastIv . "_<br><br>";


/*
 * Note: These functions do not accurately handle cases where the data 
 * being encrypted have trailing whitespace so the data
 *       encrypted by them must not have any. Leading whitespace is okay.
 *  
 * Note: If your data needs to be passed through a non-binary safe medium you should
 * base64_encode it but this makes the data about 33% larger.
 * 
 * Note: The decryption IV must be the same as the encryption IV so the encryption
 * IV must be stored or transmitted with the encrypted data.
 * From (http://php.net/manual/en/function.mcrypt-create-iv.php)... 
 * "The IV is only meant to give an alternative seed to the encryption routines. 
 * This IV does not need to be secret at all, though it can be desirable. 
 * You even can send it along with your ciphertext without losing security."
 * 
 * Note: These methods don't do any error checking on the success of the various mcrypt functions
 */
class DataEncryptor
{
    const MY_MCRYPT_CIPHER        = MCRYPT_RIJNDAEL_256;
    const MY_MCRYPT_MODE          = MCRYPT_MODE_CBC;
    const MY_MCRYPT_KEY_STRING    = "1234567890-abcDEFGHUzyxwvutsrqpo"; // This should be a random string, recommended 32 bytes

    public  $lastIv               = '';


    public function __construct()
    {
        // do nothing
    }


    /**
     * Accepts a plaintext string and returns the encrypted version
     */
    public function mcryptEncryptString( $stringToEncrypt, $base64encoded = true )
    {
        // Set the initialization vector
            $iv_size      = mcrypt_get_iv_size( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_MODE );
            $iv           = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
            $this->lastIv = $iv;

        // Encrypt the data
            $encryptedData = mcrypt_encrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToEncrypt , self::MY_MCRYPT_MODE , $iv );

        // Data may need to be passed through a non-binary safe medium so base64_encode it if necessary. (makes data about 33% larger)
            if ( $base64encoded ) {
                $encryptedData = base64_encode( $encryptedData );
                $this->lastIv  = base64_encode( $iv );
            } else {
                $this->lastIv = $iv;
            }

        // Return the encrypted data
            return $encryptedData;
    }


    /**
     * Accepts a plaintext string and returns the encrypted version
     */
    public function mcryptDecryptString( $stringToDecrypt, $iv, $base64encoded = true )
    {
        // Note: the decryption IV must be the same as the encryption IV so the encryption IV must be stored during encryption

        // The data may have been base64_encoded so decode it if necessary (must come before the decrypt)
            if ( $base64encoded ) {
                $stringToDecrypt = base64_decode( $stringToDecrypt );
                $iv              = base64_decode( $iv );
            }

        // Decrypt the data
            $decryptedData = mcrypt_decrypt( self::MY_MCRYPT_CIPHER, self::MY_MCRYPT_KEY_STRING, $stringToDecrypt, self::MY_MCRYPT_MODE, $iv );

        // Return the decrypted data
            return rtrim( $decryptedData ); // the rtrim is needed to remove padding added during encryption
    }


}
?>

Porém vocês podem percebe nesse código que o IV é gerado randomicamente, fazendo com que os dados encriptados não sejam lidos pelo C# com o IV único.

 

Então comecei uma conversão de C# via PHP.

<?php
function getKey($chave, $salt)
{
	$keySize 	= strlen($chave) * 8;
	$minSize 	= 128;
	$maxSize 	= 256;
	$skipSize 	= 256;	
	
	if ($skipSize > $maxSize)
	{
		// Busca o valor máximo da chave
		$chave = sub_str($chave, 0, $maxSize / 8);
	}
	else if ($keySize < $maxSize)
	{
		// Seta um tamanho válido
		$validSize = ($keySize <= $minSize) ? $minSize : ($keySize - $keySize % $skipSize) + $skipSize;

		if ($keySize < $validSize)
		{
			// Preenche a chave com arterisco para corrigir o tamanho
			$chave = str_pad($chave, $validSize / 8, '*', STR_PAD_RIGHT);
		}
	}	
	$keyNova = PBKDF1($chave, '', 100, 32);	
	return getBytes($keyNova, strlen($chave));
}

function getBytes($chave, $tamanho)
{
	$bytes = array();
	for($i = 0; $i < $tamanho; $i++){
		 $bytes[] = ord($chave[$i]);
	}
	return ($bytes);
}
//Função equivale:  PasswordDeriveBytes key = new PasswordDeriveBytes(_key, ASCIIEncoding.ASCII.GetBytes(salt));
function PBKDF1($pass, $salt, $count, $dklen)
{
    $t = $pass.$salt;
    $t = sha1($t, true);
    for($i=2; $i <= $count; $i++)
    {
        $t = sha1($t, true);
    }
    $t = substr($t,0,$dklen-1);
    return $t;
}

Pois bem alguns problemas e nada de solução, no PHP.net também encontrei alguns problemas como:

 

 

Caution, MCRYPT_RIJNDAEL_256 is not equivalent to AES_256.

The way to make RIJNDAEL be decrypted from AES with openssl is to use MCRYPT_RIJNDAEL_128 and padd the string to encrypt before encrypting with the follwing function:

<?php
function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return
$text . str_repeat(chr($pad), $pad);
}

?>

On the decryption, the choosing of AES_256 or AES_128, etc. is based on the keysize used in the crypting. In my case it was a 128bit key so I used AES_128.

 

E eu digo para vocês não é por falta de tentar uma solução que eu estou aqui, por isso venho por meio desse descobri se alguém já passou por tal problema.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem função nativa pra isso no php

 

Por exemplo:

 

<?php
$td
= mcrypt_module_open(MCRYPT_DES, '',
MCRYPT_MODE_ECB, '/usr/lib/mcrypt-modes');

$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
?>

 

Mais em: http://www.php.net/manual/pt_BR/function.mcrypt-module-open.php

 

Lei ainda:

 

http://stackoverflow.com/questions/3431950/rijndael-256-encrypt-decrypt-between-c-sharp-and-php

 

http://stackoverflow.com/questions/6400203/using-php-mcrypt-with-rijndael-aes

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem função nativa pra isso no php

 

Por exemplo:

 

<?php

$td

= mcrypt_module_open(MCRYPT_DES, '',

MCRYPT_MODE_ECB, '/usr/lib/mcrypt-modes');

 

$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');

?>

 

Mais em: http://www.php.net/manual/pt_BR/function.mcrypt-module-open.php

 

Lei ainda:

 

http://stackoverflow.com/questions/3431950/rijndael-256-encrypt-decrypt-between-c-sharp-and-php

 

http://stackoverflow.com/questions/6400203/using-php-mcrypt-with-rijndael-aes

 

Obrigado pela sua resposta, porém todos esses casos já foram testado, até os código aqui em meu poste.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.