Jump to content

Archived

This topic is now archived and is closed to further replies.

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.

Share this post


Link to post
Share on other 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

Share this post


Link to post
Share on other 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.

Share this post


Link to post
Share on other sites

×

Important Information

Ao usar o fórum, você concorda com nossos Terms of Use.