Encoding / encryption error when porting Python to Perl script
I have a Python script that works great. It is able to decode / decrypt the provided pwd and encode / encrypt it like below:
#!/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
... this gives me the following output:
Encoded Encrypted Password : 4TOHTKsvihUXuUd9M3TpoA==
Decoded (Decrypted ( PWD ) ) : MYweakPW
New Encoded (Encrypted ( MYweakPW ) ) : 4TOHTKsvihUXuUd9M3TpoA==
Now I need to port this script to Perl, so I did:
#!/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";
... but this comes back to me:
Encoded Encrypted Password : 4TOHTKsvihUXuUd9M3TpoA==
Decoded (Decrypted ( PWD ) ) : MYweakPW
New Encoded (Encrypted (MYweakPW) ) : 4TOHTKsvihU=
Question: Why when I encrypt / encode a password in Perl, it returns an abbreviated string? What's missing to match?
RH relationships
EDIT
Since I am changing the accepted answer, let me clarify some aspects of using this code to justify some options. Of course, this is not the whole script. I removed as much personal information as possible, as well as other parts of the script that were already working, isolating the piece of code that needs attention. The general purpose of the script is to manage passwords used in some other scripts / applications when the password is changed on remote servers.
This particular piece of code that handles the password stored by Remmina in the saved sessions. Unfortunately Remmina did not provide a centralized method for replacing saved passwords, so in my case, every time I change my password on a Windows domain, all my saved Remmina sessions are outdated (and I have dozens of them!)
Here's how Remmina stores passwords:
-
at
$HOME/.remmina/remmina.pref
is a string containingsecret=*
, encoded, with DES3key
andiv
-
each named session file
$HOME/.remmina/*.remmina
has a linepassword=*
with your password, encoded and encrypted
However, it doesn't matter if the original encrypted / encoded password is correctly or incorrectly generated ... This is how Remmina does it and I have to deal with it: - /
Depending on the options provided on the command line, the script should be able to retrieve the saved password from files, *.remmina
or get a new one and replace it with files *.remmina
, so the point raised by @ jm666 on his EDIT2 is very pertinent, since when I get a new password from the command lines, it will not be pre-completed in any way.
For my specific scenario, I know that passwords will never be shorter than 8 bytes, but may be longer than a multiple of 8, so I tested this with new different passwords and realized that to encrypt passwords for Remmin, the corresponding padding = 'null'
- A special case is that the password has exactly 8 bytes (or multiples of it). In this case, I had to "manually" add one
null
char to the end of the provided string to force the padding of additionalnull
characters
source to share
You requested perl using padding=>'null'
. Just change:
$cipher = Crypt::CBC->new(
-cipher => 'DES_EDE3',
-key => $key,
-iv => $iv,
-header => 'none',
-padding => 'null',
-literal_key => 1
);
before -padding => 'none',
and will get the same result as python .
NOTE. The above gives the desired output, but this is WRONG's answer as well as @harmic's answer too. See Board EDIT2.
Note that if you haven't entered any paddig (since you haven't used padding in your python code), for example
$cipher = Crypt::CBC->new(
-cipher => 'DES_EDE3',
-key => $key,
-iv => $iv,
-header => 'none',
#-padding => 'null', # <- commented out
-literal_key => 1
);
perl will use indentation standard
defined in PKCS#5
eg. padds with a number equal to the missing padding bytes. for example, if 2 bytes are missing, padding will be 0x02 0x02
, if 3 bytes are missing, padding will be 0x03 0x03 0x03
. (so a different python output with no specific padding will be issued)
EDIT
from the 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.
The gasket has been none
supported for almost two years. :)
EDIT2
using none
or space
is completely wrong because:
You can never save the original 4TOHTKsvihUXuUd9M3TpoA==
string correctly ...
Try to encrypt and encode the password entered directly MYweakPW
. Regardless of the shim used, you never get the original String 4TOHTKsvihUXuUd9M3TpoA==
.
You will receive the following (depending on the add-on used):
Padding: standard 4TOHTKsvihW3UDR8tqmBIg==
Padding: space 4TOHTKsvihU=
Padding: oneandzeroes 4TOHTKsvihUndO+JKCfmog==
Padding: rijndael_compat 4TOHTKsvihU=
Padding: null 4TOHTKsvihU=
Padding: none 4TOHTKsvihU=
MYweakPW
Hexdump plaintext password:
4d597765616b5057
You, after receiving decode -> decrypt
(depending on the fill method used :)
4d597765616b50570000000000000000
#or
4d597765616b5057
and when trying to encrypt it again, the encryption algorithm adds a new padding (or not - depending on the method used) to the string again.
All of this can be seen in the following script (basically yours, just using hex output):
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", @_; }
conclusion, compare yourself what hexdump you got after decryption / encryption depending on the addon used. ("Oroginal" means the strings you get from the original base64 encoding, and "String" means the value you get from the string entered directly 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=
Result:
- from a string string
MYweakPW
(for example, without 8 zeros at the end) you can never get the desired output regardless of padding (see results) - so the original string
4TOHTKsvihUXuUd9M3TpoA==
was created INCORRECTLY (or deliberately added eight null characters toMYweakPW
what is already added in 8 bytes). - you have to double-check how the original string was created (with which padding method), especially with a password that is not 8 bytes long . For example. try building a base64 encoded string with a password
weak
and you will see how it is padded.
source to share