【问题标题】:PHP mcrypt & Perl Crypt::CBC generating different ciphertextPHP mcrypt & Perl Crypt::CBC 生成不同的密文
【发布时间】:2013-12-01 19:36:49
【问题描述】:

我正在尝试学习各种加密方法,但在尝试使用 Perl 与 PHP 生成密文时遇到了问题。

如果我用 PHP 加密一个秘密,我可以在 PHP 和 Perl 中解密生成的密文,但如果我在 Perl 中加密,密文是“错误的”,并且秘密会被 PHP 和 Perl 弄乱......

加密.php:

#!/usr/bin/env php
<?php

# Set up vars   
$iv = 'length16length16';
$key = 'length32length32length32length32';
$cleartext = 'password';

if( count( $argv ) > 1 )
{
    $cleartext = $argv[1];
}
# --- ENCRYPTION ---
# Set up cipher
$cipher = mcrypt_module_open( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init( $cipher, $key, $iv );

# Do the encryption
$ciphertext = mcrypt_generic( $cipher, $cleartext );

# Convert to HEX for print/storage
$cipher_block = implode( unpack( 'H*', $iv . $ciphertext ) );

print( "IV " . implode( unpack( 'H*', $iv ) ) );
print( "CIPH " . implode( unpack( 'H*', $ciphertext ) ) );
print( $cipher_block );

# Clean up
mcrypt_generic_deinit( $cipher );
mcrypt_module_close( $cipher );

?>

解密.php:

#!/usr/bin/env php
<?php

# Set up vars
$key = 'length32length32length32length32';

if( count( $argv ) > 1 )
{
    # --- DECRYPTION ---
    # Grab the hex-encoded cipherblock & convert it to binary
    $cipher_block = unpack( 'a16iv/a*ciphertext', pack( 'H*', $argv[1] ) );

    # Set up cipher
    $cipher = mcrypt_module_open( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

    mcrypt_generic_init( $cipher, $key, $cipher_block['iv'] );

    # Do the decryption
    $cleartext = mdecrypt_generic( $cipher, $cipher_block['ciphertext'] );

    print( $cleartext );

    # Clean up
    mcrypt_generic_deinit( $cipher );
    mcrypt_module_close( $cipher );
}

?>

加密.pl:

#!/usr/bin/env perl

use strict;
use warnings;

use Crypt::CBC;

# Set up vars
my $iv = 'length16length16';
my $key = 'length32length32length32length32';
my $cleartext = shift;

# --- ENCRYPTION ---
# Set up cipher
my $cipher = Crypt::CBC->new(
    -literal_key    => 1,
    -key            => $key,
    -header         => 'none',
    -iv             => $iv,
    -cipher         => 'Crypt::OpenSSL::AES');

# Do the encryption
my $ciphertext = $cipher->encrypt( $cleartext );

# Convert to HEX for print/storage
my $cipher_block = unpack( 'H*', $iv . $ciphertext );

print( "IV " . unpack( 'H*', $iv ) . "\n" );
print( "CIPH " . unpack( 'H*', $ciphertext ) . "\n" );
print( $cipher_block );

解密.pl:

#!/usr/bin/env perl

use strict;
use warnings;

use Crypt::CBC;

# Set up vars
my $key = 'length32length32length32length32';

my $cipher_block = shift;

if( $cipher_block )
{
    # --- DECRYPTION ---
    # Grab the hex-encoded cipherblock & convert it to binary
    my ($iv, $ciphertext) = unpack( 'a16a*', pack( 'H*', $cipher_block ) );

    # Set up cipher
    my $cipher = Crypt::CBC->new(
        -literal_key    => 1,
        -key            => $key,
        -header         => 'none',
        -iv             => $iv,
        -cipher         => 'Crypt::OpenSSL::AES');

    my $cleartext = $cipher->decrypt( $ciphertext );

    print( $cleartext );
}

这是我得到的输出:

$ ./encrypt.php "Secret Text"
IV 6c656e67746831366c656e6774683136
CIPH 32a47901313f47ed2ca657d3bd0c2e80
6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80

$ ./decrypt.php 6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80
Secret Text

$ ./decrypt.pl 6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80
Secret Text

$ ./encrypt.pl "Secret Text"
IV 6c656e67746831366c656e6774683136
CIPH f3ae0d5f236cea77fa9ac5540d733aef
6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80

$ ./decrypt.php 6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80
sesswrtext

$ ./decrypt.pl 6c656e67746831366c656e677468313632a47901313f47ed2ca657d3bd0c2e80
sesswrtext

如您所见,即使使用相同的密钥、密钥和 IV,Perl 脚本也会生成不同的密文,PHP 和 Perl 脚本都解密为相同的密文,但不是原始密钥...

提前致谢。

【问题讨论】:

  • 尝试不打印输出,而是将其转换为十六进制然后输出。我觉得这里有尾随字符(填充)。根据使用的模式,它们将是空字节或低字节(不可打印)......
  • 在这两个加密脚本中,出于这个原因,我在输出之前将密文打包成一个十六进制字符串:D
  • 我不担心密文,我担心解密后的纯文本(没有正确剥离填充)...

标签: php perl encryption aes


【解决方案1】:

问题在于填充。试试:

my $cipher = Crypt::CBC->new(
    -literal_key    => 1,
    -key            => $key,
    -header         => 'none',
    -iv             => $iv,
    -padding        => 'null', #!!!!!!!!!!!!!
    -cipher         => 'Crypt::OpenSSL::AES');

你应该得到:

CIPH 32a47901313f47ed2ca657d3bd0c2e80

无论如何-padding=&gt;'null' 可能不是一个好主意。尝试找出 PHP 的 mcrypt 是否支持最常用的 PKCS#5/7 填充,对应于 Crypt::CBC 的 -padding=&gt;'standard'

【讨论】:

  • PHP 的 mcrypt 不支持该填充。你必须自己做这件事; mcrypt_encrypt 的 cmets 中有一些实现。还要注意Crypt::OpenSSL::AES:“加密数据。$data 的大小必须完全是块大小的长度(16 字节),否则这个函数会发牢骚。”我试过了,但我的电脑不会响。文档也必须是 0.1 版左右。
猜你喜欢
  • 2023-03-03
  • 2013-06-08
  • 2016-07-25
  • 2011-08-31
  • 2014-02-06
  • 2019-10-03
  • 1970-01-01
  • 2013-06-06
  • 2010-10-13
相关资源
最近更新 更多