将Python移植到Perl脚本时编码/加密问题

时间:2014-10-03 22:39:07

标签: python perl encryption decode encode

我有一个Python脚本可以正常工作。它能够解码/解密提供的pwd,并对其进行编码/加密,如下所示:

#!/usr/bin/python

from Crypto.Cipher import DES3
import base64

secret = base64.decodestring('tcxpLw1PsMR0CtXt/HfbIZomvJtDyE6h1Gl4vblX2W4=')
key = secret[:24]
iv = secret[24:]

# Encoded Encrypted password
EEpwd = '4TOHTKsvihUXuUd9M3TpoA=='
print "Encoded Encrypted Password : ",EEpwd

# Decoded Encrypted password
DEpwd = base64.decodestring(EEpwd)

# Decoded Decrypted password
DDpwd = DES3.new(key, DES3.MODE_CBC, iv).decrypt(DEpwd)
print "Decoded (Decrypted ( PWD ) ) : ",DDpwd

# New Decoded Encrypted password
NewDEpwd = DES3.new(key, DES3.MODE_CBC, iv).encrypt(DDpwd)

# New Encoded Encrypted password
NewEEpwd = base64.b64encode(NewDEpwd)
print "New Encoded (Encrypted (",DDpwd,") ) : ",NewEEpwd

...这给了我以下输出:

Encoded Encrypted Password :  4TOHTKsvihUXuUd9M3TpoA==
Decoded (Decrypted ( PWD ) ) :  MYweakPW
New Encoded (Encrypted ( MYweakPW ) ) :  4TOHTKsvihUXuUd9M3TpoA==

现在我必须将此脚本迁移到Perl,所以我做了:

#!/usr/bin/perl
use MIME::Base64;
use Crypt::CBC;

$secret = decode_base64('tcxpLw1PsMR0CtXt/HfbIZomvJtDyE6h1Gl4vblX2W4=');
$key = substr($secret,0,24);
$iv = substr($secret,24);

$cipher = Crypt::CBC->new(
                -cipher => 'DES_EDE3',
                -key    => $key,
                -iv     => $iv,
                -header => 'none',
                -padding => 'null',
                -literal_key => 1
                );

# Encoded Encrypted password
$EEpwd = '4TOHTKsvihUXuUd9M3TpoA==';
print "Encoded Encrypted Password : ". $EEpwd ."\n";

# Decoded Encrypted password
$DEpwd = decode_base64($EEpwd);

# Decoded Decrypted password
$DDpwd = $cipher->decrypt($DEpwd);
print "Decoded (Decrypted ( PWD ) ) : $DDpwd \n";

# New Decoded Encrypted password
$NewDEpwd = $cipher->encrypt($DDpwd);

# New Encoded Encrypted password
$NewEEpwd = encode_base64($NewDEpwd);
print "New Encoded (Encrypted ($DDpwd) ) : $NewEEpwd \n";

...但这回到我身边:

Encoded Encrypted Password : 4TOHTKsvihUXuUd9M3TpoA==
Decoded (Decrypted ( PWD ) ) : MYweakPW 
New Encoded (Encrypted (MYweakPW) ) : 4TOHTKsvihU=

问题:为什么当我在Perl中加密/编码密码时,它返回一个缩短的字符串?那匹配缺少什么?

此致 RZ


修改

由于我正在更改已接受的答案,请让我澄清此代码用法的某些方面,以证明某些选择的合理性。 当然这不是整个脚本。我已经删除了尽可能多的私人信息,以及已经工作的其他脚本,隔离了需要注意的代码段。 当在远程服务器上更改密码时,脚本的整体意图是管理在其他一些脚本/应用程序中使用的密码。

这段特殊的代码用于处理Remmina在保存的会话中存储的密码。不幸的是,Remmina没有提供一个集中的方法来替换保存的密码,所以在我的情况下,每次我在Windows域更改密码时,我所有的Remmina保存的会话都会过时(我有几十个!)

Remmina存储密码的方式是:

  • $HOME/.remmina/remmina.pref,有一行包含secret=*,已编码,包含DES3 keyiv

  • 在每个会话文件中,名为$HOME/.remmina/*.remmina,其中包含password=*一行,其中包含您的密码,已编码和加密

那就是说,如果原始的加密/编码密码是正确或错误生成的话是无关紧要的......这就是Remmina的做法,我必须处理它: - /

根据命令行中提供的参数,脚本应该能够从*.remmina文件中检索存储的密码,或者获取一个新的密码并将其替换为*.remmina个文件,所以这一点@ jm666在他的EDIT2上提出的非常相关,因为当我从命令行获得一个新密码时,它不会以任何方式填充。

对于我的特定场景,我知道密码永远不会短于8个字节,但可以更长,而不是8的倍数,所以我用新的不同密码测试了这个,并意识到要加密密码Remmina,恰当的是padding = 'null'

  • 特殊情况似乎是密码长度恰好为8个字节(或其倍数)。在这种情况下,我必须在提供的字符串的末尾“手动”添加一个null字符,以强制填充添加额外的null字符

2 个答案:

答案 0 :(得分:3)

您选择的填充选项似乎与Python加密库使用的填充选项不同。

我发现通过将-padding选项更改为'space',重新加密的密码与原始密码相同。

答案 1 :(得分:2)

您使用padding=>'null'询问了perl。只需更改:

$cipher = Crypt::CBC->new(
                -cipher => 'DES_EDE3',
                -key    => $key,
                -iv     => $iv,
                -header => 'none',
                -padding => 'null',
                -literal_key => 1
                );

-padding => 'none',并获得与python相同的结果

注意:上面给出了想要的输出,但它是错误答案,以及@ harmic的答案。见下面的EDIT2。

注意,如果你没有输入任何paddig,(因为你没有在你的python代码中使用任何填充),例如。

$cipher = Crypt::CBC->new(
                -cipher => 'DES_EDE3',
                -key    => $key,
                -iv     => $iv,
                -header => 'none',
                #-padding => 'null',   # <- commented out
                -literal_key => 1
                );

perl将使用standard中定义的PKCS#5填充,例如padds的数字等于缺少填充字节。例如如果缺少2个字节,填充将为0x02 0x02如果缺少3个字节,则填充将为0x03 0x03 0x03。 (因此将产生不同的结果,如没有定义填充的python)

修改

来自Crypt :: CBC的Changelog的

2.31    Tue Oct 30 07:03:40 EDT 2012
    - Fixes to regular expressions to avoid rare failures to
      correctly strip padding in decoded messages.
    - Add padding type = "none".
    - Both fixes contributed by Bas van Sisseren.

填充none支持近两年。 :)

EDIT2

使用nonespace完全不正确,因为:

您永远无法正确获取原始4TOHTKsvihUXuUd9M3TpoA==编码字符串

尝试直接输入密码MYweakPW进行加密和编码。 无论使用哪种填充,您永远不会获得原始填充 4TOHTKsvihUXuUd9M3TpoA==字符串。

您将获得下一个(取决于使用的填充):

Padding: standard               4TOHTKsvihW3UDR8tqmBIg==
Padding: space                  4TOHTKsvihU=
Padding: oneandzeroes           4TOHTKsvihUndO+JKCfmog==
Padding: rijndael_compat        4TOHTKsvihU=
Padding: null                   4TOHTKsvihU=
Padding: none                   4TOHTKsvihU=

明文密码MYweakPW hexdump:

4d597765616b5057

decode -> decrypt获得后(取决于使用的填充方法:)

4d597765616b50570000000000000000
#or
4d597765616b5057

当尝试再次加密时,加密算法再次添加新的填充(或不 - 取决于使用的方法)到字符串。

整个可以在以下脚本中看到(主要是你的,只使用十六进制输出):

use strict;
use warnings;
use MIME::Base64;
use Crypt::CBC;

my $secret = decode_base64('tcxpLw1PsMR0CtXt/HfbIZomvJtDyE6h1Gl4vblX2W4=');
my $iv = substr($secret,24);
my $key = substr($secret,0,24);

for my $padd ( qw(standard space  oneandzeroes  rijndael_compat  null  none)) {
    my $c = Crypt::CBC->new( -cipher=>'DES_EDE3', -key=>$key, -iv=>$iv,
                -header=>'none', -literal_key=>1,
                -padding=>$padd
            );
    print "== Padding: $padd=\n";
    display($c);
}

sub display {
    my $cipher = shift;

    my $EEpwd = '4TOHTKsvihUXuUd9M3TpoA==';
    p("Original (encrypted & encoded)", $EEpwd);

    my $DEpwd = decode_base64($EEpwd);
    ph("Original (decoded still encrypted)", $DEpwd);

    my $DDpwd = $cipher->decrypt($DEpwd);
    ph("Original plaintext", $DDpwd);

    my $NewDEpwd = $cipher->encrypt($DDpwd);
    ph("New from orig (encrypted)", $NewDEpwd);

    my $NewEEpwd = encode_base64($NewDEpwd);
    p( "New from orig (encrypted+encoded)", $NewEEpwd);

    my $asc = "MYweakPW";
    ph("String $asc", $asc);
    my $m1 = $cipher->encrypt($asc);
    ph("String (encrypted)", $m1);
    p("String (encrypted,encoded)", encode_base64($m1));
}

sub ph  { p($_[0] . " hex:", unpack('H*',$_[1]) ) }
sub p   { printf "%40.40s %s\n", @_; }

输出,根据使用的填充,比较你在解密/加密后得到的hexdump。 (“oroginal”表示从原始base64编码中获得的字符串,“String”表示从直接输入的字符串MYweakPW获得的值。

== Padding: standard=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b50570000000000000000
          New from orig (encrypted) hex: e133874cab2f8a1517b9477d3374e9a0a26625e0d2ebb3d4
       New from orig (encrypted+encoded) 4TOHTKsvihUXuUd9M3TpoKJmJeDS67PU

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a15b750347cb6a98122
              String (encrypted,encoded) 4TOHTKsvihW3UDR8tqmBIg==

== Padding: space=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b50570000000000000000
          New from orig (encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
       New from orig (encrypted+encoded) 4TOHTKsvihUXuUd9M3TpoA==

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a15
              String (encrypted,encoded) 4TOHTKsvihU=

== Padding: oneandzeroes=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b50570000000000000000
          New from orig (encrypted) hex: e133874cab2f8a1517b9477d3374e9a0854bea98199fa99e
       New from orig (encrypted+encoded) 4TOHTKsvihUXuUd9M3TpoIVL6pgZn6me

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a152774ef892827e6a2
              String (encrypted,encoded) 4TOHTKsvihUndO+JKCfmog==

== Padding: rijndael_compat=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b50570000000000000000
          New from orig (encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
       New from orig (encrypted+encoded) 4TOHTKsvihUXuUd9M3TpoA==

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a15
              String (encrypted,encoded) 4TOHTKsvihU=

== Padding: null=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b5057
          New from orig (encrypted) hex: e133874cab2f8a15
       New from orig (encrypted+encoded) 4TOHTKsvihU=

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a15
              String (encrypted,encoded) 4TOHTKsvihU=

== Padding: none=
          Original (encrypted & encoded) 4TOHTKsvihUXuUd9M3TpoA==
 Original (decoded still encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
                 Original plaintext hex: 4d597765616b50570000000000000000
          New from orig (encrypted) hex: e133874cab2f8a1517b9477d3374e9a0
       New from orig (encrypted+encoded) 4TOHTKsvihUXuUd9M3TpoA==

                    String MYweakPW hex: 4d597765616b5057
                 String (encrypted) hex: e133874cab2f8a15
              String (encrypted,encoded) 4TOHTKsvihU=

结果:

    来自文字字符串MYweakPW
  • (例如,在它结束时没有8个零),无论填充是什么,都无法获得所需的输出(请参阅结果)
  • 所以,你原来的字符串4TOHTKsvihUXuUd9M3TpoA==是不正确地创建的(或故意在MYweakPW中添加了8个空字符,这些字符已经是8字节填充的。)
  • 您应该重新检查原始字符串是如何创建的(使用什么填充方法),特别是密码不完全是8字节长。例如。尝试从密码weak创建base64编码的字符串,您将看到它是如何填充的。