{"id":885,"date":"2013-01-26T19:50:39","date_gmt":"2013-01-26T19:50:39","guid":{"rendered":"http:\/\/joelinoff.com\/blog\/?p=885"},"modified":"2017-03-13T11:31:15","modified_gmt":"2017-03-13T18:31:15","slug":"simple-python-functions-that-provide-openssl-aes-256-cbc-compatible-encryptdecrypt","status":"publish","type":"post","link":"https:\/\/joelinoff.com\/blog\/?p=885","title":{"rendered":"Simple python functions that provide openssl -aes-256-cbc compatible encrypt\/decrypt"},"content":{"rendered":"<p>The example here shows how to encrypt and decrypt data using python in a way that is fully compatible with openssl aes-256-cbc. It is based on the work that I did in C++ Cipher class that is published on this site. It works for both python-2.7 and python-3.x.<\/p>\n<p>The key idea is based on the way that openssl generates the key and iv data from password as well as the &#8220;Salted__&#8221; prefix that it uses.<\/p>\n<p>The complete routine can be downloaded here: <a href=\"http:\/\/downloads.joelinoff.com\/mycrypt.py\" title=\"mycrypt.py\">mycrypt.py<\/a>.<br \/>\n<!--more--><br \/>\nWhen you download the script from this section you will be able to perform operations like this.<\/p>\n<p>[crayon lang=&#8221;bash&#8221; toolbar=&#8221;always&#8221; title=&#8221;example&#8221;]<br \/>\n$ # Encrypt and decrypt using mycrypt.py.<br \/>\n$ echo &#8216;Lorem ipsum dolor sit amet&#8217; |\\<br \/>\n    .\/mycrypt.py -e -p secret |\\<br \/>\n    .\/mycrypt.py -d -p secret<br \/>\nLorem ipsum dolor sit amet<\/p>\n<p>$ # Encrypt using mycrypt.py and decrypt using openssl<br \/>\n$ # with the SHA512 message digest.<br \/>\n$ echo &#8216;Lorem ipsum dolor sit amet&#8217; |\\<br \/>\n    .\/mycrypt.py -e -m sha512 -p secret |\\<br \/>\n    openssl enc -d -aes-256-cbc -md sha512 -base64 -salt -pass pass:secret<br \/>\nLorem ipsum dolor sit amet<\/p>\n<p>$ # Encrypt using openssl and decrypt using mycrypt.py.<br \/>\n$ echo &#8216;Lorem ipsum dolor sit amet&#8217; |\\<br \/>\n    openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret |\\<br \/>\n    .\/mycrypt.py -d -p secret<br \/>\nLorem ipsum dolor sit amet<\/p>\n<p>$ # Encrypt and decrypt using openssl.<br \/>\n    openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret |\\<br \/>\n    openssl enc -d -aes-256-cbc -md md5 -base64 -salt -pass pass:secret<br \/>\nLorem ipsum dolor sit amet<br \/>\n[\/crayon]<\/p>\n<h1>Basic Routines<\/h1>\n<p>The code shown below implements the encrypt and decrypt routines.<\/p>\n<p>[crayon lang=&#8221;python&#8221; toolbar=&#8221;always&#8221; title=&#8221;encrypt\/decrypt&#8221;]<br \/>\n#!\/usr\/bin\/env python<br \/>\n&#8221;&#8217;<br \/>\nImplement openssl compatible AES-256 CBC mode encryption\/decryption.<\/p>\n<p>This module provides encrypt() and decrypt() functions that are compatible<br \/>\nwith the openssl algorithms.<\/p>\n<p>This is basically a python encoding of my C++ work on the Cipher class<br \/>\nusing the Crypto.Cipher.AES class.<\/p>\n<p>URL: http:\/\/projects.joelinoff.com\/cipher-1.1\/doxydocs\/html\/<br \/>\n&#8221;&#8217;<\/p>\n<p># LICENSE<br \/>\n#<br \/>\n# MIT Open Source<br \/>\n#<br \/>\n# Copyright (c) 2014 Joe Linoff<br \/>\n#<br \/>\n# Permission is hereby granted, free of charge, to any person<br \/>\n# obtaining a copy of this software and associated documentation files<br \/>\n# (the &#8220;Software&#8221;), to deal in the Software without restriction,<br \/>\n# including without limitation the rights to use, copy, modify, merge,<br \/>\n# publish, distribute, sublicense, and\/or sell copies of the Software,<br \/>\n# and to permit persons to whom the Software is furnished to do so,<br \/>\n# subject to the following conditions:<br \/>\n#<br \/>\n# The above copyright notice and this permission notice shall be<br \/>\n# included in all copies or substantial portions of the Software.<br \/>\n#<br \/>\n# THE SOFTWARE IS PROVIDED &#8220;AS IS&#8221;, WITHOUT WARRANTY OF ANY KIND,<br \/>\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF<br \/>\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND<br \/>\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS<br \/>\n# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN<br \/>\n# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN<br \/>\n# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br \/>\n# SOFTWARE.<\/p>\n<p>import argparse<br \/>\nimport base64<br \/>\nimport os<br \/>\nimport re<br \/>\nimport hashlib<br \/>\nimport inspect<br \/>\nimport sys<br \/>\nfrom getpass import getpass<br \/>\nfrom Crypto.Cipher import AES<\/p>\n<p>VERSION=&#8217;1.2&#8242;<\/p>\n<p># ================================================================<br \/>\n# debug<br \/>\n# ================================================================<br \/>\ndef _debug(msg, lev=1):<br \/>\n    &#8221;&#8217;<br \/>\n    Print a debug message with some context.<br \/>\n    &#8221;&#8217;<br \/>\n    sys.stderr.write(&#8216;DEBUG:{} ofp {}\\n&#8217;.format(inspect.stack()[lev][2], msg))<\/p>\n<p># ================================================================<br \/>\n# get_key_and_iv<br \/>\n# ================================================================<br \/>\ndef get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst=&#8217;md5&#8242;):<br \/>\n    &#8221;&#8217;<br \/>\n    Derive the key and the IV from the given password and salt.<\/p>\n<p>    This is a niftier implementation than my direct transliteration of<br \/>\n    the C++ code although I modified to support different digests.<\/p>\n<p>    CITATION: http:\/\/stackoverflow.com\/questions\/13907841\/implement-openssl-aes-encryption-in-python<\/p>\n<p>    @param password  The password to use as the seed.<br \/>\n    @param salt      The salt.<br \/>\n    @param klen      The key length.<br \/>\n    @param ilen      The initialization vector length.<br \/>\n    @param msgdgst   The message digest algorithm to use.<br \/>\n    &#8221;&#8217;<br \/>\n    # equivalent to:<br \/>\n    #   from hashlib import <mdi> as mdf<br \/>\n    #   from hashlib import md5 as mdf<br \/>\n    #   from hashlib import sha512 as mdf<br \/>\n    mdf = getattr(__import__(&#8216;hashlib&#8217;, fromlist=[msgdgst]), msgdgst)<br \/>\n    password = password.encode(&#8216;ascii&#8217;, &#8216;ignore&#8217;)  # convert to ASCII<\/p>\n<p>    try:<br \/>\n        maxlen = klen + ilen<br \/>\n        keyiv = mdf(password + salt).digest()<br \/>\n        tmp = [keyiv]<br \/>\n        while len(tmp) < maxlen:\n            tmp.append( mdf(tmp[-1] + password + salt).digest() )\n            keyiv += tmp[-1]  # append the last byte\n        key = keyiv[:klen]\n        iv = keyiv[klen:klen+ilen]\n        return key, iv\n    except UnicodeDecodeError:\n        return None, None\n\n\n# ================================================================\n# encrypt\n# ================================================================\ndef encrypt(password, plaintext, chunkit=True, msgdgst='md5'):\n    '''\n    Encrypt the plaintext using the password using an openssl\n    compatible encryption algorithm. It is the same as creating a file\n    with plaintext contents and running openssl like this:\n\n    $ cat plaintext\n    <plaintext>\n    $ openssl enc -e -aes-256-cbc -base64 -salt \\\\<br \/>\n        -pass pass:<password> -n plaintext<\/p>\n<p>    @param password  The password.<br \/>\n    @param plaintext The plaintext to encrypt.<br \/>\n    @param chunkit   Flag that tells encrypt to split the ciphertext<br \/>\n                     into 64 character (MIME encoded) lines.<br \/>\n                     This does not affect the decrypt operation.<br \/>\n    @param msgdgst   The message digest algorithm.<br \/>\n    &#8221;&#8217;<br \/>\n    salt = os.urandom(8)<br \/>\n    key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)<br \/>\n    if key is None:<br \/>\n        return None<\/p>\n<p>    # PKCS#7 padding<br \/>\n    padding_len = 16 &#8211; (len(plaintext) % 16)<br \/>\n    if isinstance(plaintext, str):<br \/>\n        padded_plaintext = plaintext + (chr(padding_len) * padding_len)<br \/>\n    else: # assume bytes<br \/>\n        padded_plaintext = plaintext + (bytearray([padding_len] * padding_len))<\/p>\n<p>    # Encrypt<br \/>\n    cipher = AES.new(key, AES.MODE_CBC, iv)<br \/>\n    ciphertext = cipher.encrypt(padded_plaintext)<\/p>\n<p>    # Make openssl compatible.<br \/>\n    # I first discovered this when I wrote the C++ Cipher class.<br \/>\n    # CITATION: http:\/\/projects.joelinoff.com\/cipher-1.1\/doxydocs\/html\/<br \/>\n    openssl_ciphertext = b&#8217;Salted__&#8217; + salt + ciphertext<br \/>\n    b64 = base64.b64encode(openssl_ciphertext)<br \/>\n    if not chunkit:<br \/>\n        return b64<\/p>\n<p>    LINELEN = 64<br \/>\n    chunk = lambda s: b&#8217;\\n&#8217;.join(s[i:min(i+LINELEN, len(s))]<br \/>\n                                for i in range(0, len(s), LINELEN))<br \/>\n    return chunk(b64)<\/p>\n<p># ================================================================<br \/>\n# decrypt<br \/>\n# ================================================================<br \/>\ndef decrypt(password, ciphertext, msgdgst=&#8217;md5&#8242;):<br \/>\n    &#8221;&#8217;<br \/>\n    Decrypt the ciphertext using the password using an openssl<br \/>\n    compatible decryption algorithm. It is the same as creating a file<br \/>\n    with ciphertext contents and running openssl like this:<\/p>\n<p>    $ cat ciphertext<br \/>\n    # ENCRYPTED<br \/>\n    <ciphertext><br \/>\n    $ egrep -v &#8216;^#|^$&#8217; | \\\\<br \/>\n        openssl enc -d -aes-256-cbc -base64 -salt -pass pass:<password> -in ciphertext<br \/>\n    @param password   The password.<br \/>\n    @param ciphertext The ciphertext to decrypt.<br \/>\n    @param msgdgst    The message digest algorithm.<br \/>\n    @returns the decrypted data.<br \/>\n    &#8221;&#8217;<\/p>\n<p>    # unfilter &#8212; ignore blank lines and comments<br \/>\n    if isinstance(ciphertext, str):<br \/>\n        filtered = &#8221;<br \/>\n        nl = &#8216;\\n&#8217;<br \/>\n        re1 = r&#8217;^\\s*$&#8217;<br \/>\n        re2 = r&#8217;^\\s*#&#8217;<br \/>\n    else:<br \/>\n        filtered = b&#8221;<br \/>\n        nl = b&#8217;\\n&#8217;<br \/>\n        re1 = b&#8217;^\\\\s*$&#8217;<br \/>\n        re2 = b&#8217;^\\\\s*#&#8217;<\/p>\n<p>    for line in ciphertext.split(nl):<br \/>\n        line = line.strip()<br \/>\n        if re.search(re1,line) or re.search(re2, line):<br \/>\n            continue<br \/>\n        filtered += line + nl<\/p>\n<p>    # Base64 decode<br \/>\n    raw = base64.b64decode(filtered)<br \/>\n    assert(raw[:8] == b&#8217;Salted__&#8217; )<br \/>\n    salt = raw[8:16]  # get the salt<\/p>\n<p>    # Now create the key and iv.<br \/>\n    key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)<br \/>\n    if key is None:<br \/>\n        return None<\/p>\n<p>    # The original ciphertext<br \/>\n    ciphertext = raw[16:]<\/p>\n<p>    # Decrypt<br \/>\n    cipher = AES.new(key, AES.MODE_CBC, iv)<br \/>\n    padded_plaintext = cipher.decrypt(ciphertext)<\/p>\n<p>    if isinstance(padded_plaintext, str):<br \/>\n        padding_len = ord(padded_plaintext[-1])<br \/>\n    else:<br \/>\n        padding_len = padded_plaintext[-1]<br \/>\n    plaintext = padded_plaintext[:-padding_len]<br \/>\n    return plaintext<br \/>\n[\/crayon]<\/p>\n<h1>The Rest of the Program<\/h1>\n<p>The following code shows how to use the previous code to create tool that will encrypt and decrypt in the command line.<\/p>\n<p>[crayon lang=&#8221;python&#8221; toolbar=&#8221;always&#8221; title=&#8221;encrypt\/decrypt&#8221;]<br \/>\n# include the code above &#8230;<br \/>\n# ================================================================<br \/>\n# _open_ios<br \/>\n# ================================================================<br \/>\ndef _open_ios(args):<br \/>\n    &#8221;&#8217;<br \/>\n    Open the IO files.<br \/>\n    &#8221;&#8217;<br \/>\n    ifp = sys.stdin<br \/>\n    ofp = sys.stdout<\/p>\n<p>    if args.input is not None:<br \/>\n        try:<br \/>\n            ifp = open(args.input, &#8216;rb&#8217;)<br \/>\n        except IOError:<br \/>\n            print(&#8216;ERROR: cannot read file: {}&#8217;.format(args.input))<br \/>\n            sys.exit(1)<\/p>\n<p>    if args.output is not None:<br \/>\n        try:<br \/>\n            ofp = open(args.output, &#8216;wb&#8217;)<br \/>\n        except IOError:<br \/>\n            print(&#8216;ERROR: cannot write file: {}&#8217;.format(args.output))<br \/>\n            sys.exit(1)<\/p>\n<p>    return ifp, ofp<\/p>\n<p># ================================================================<br \/>\n# _close_ios<br \/>\n# ================================================================<br \/>\ndef _close_ios(ifp, ofp):<br \/>\n    &#8221;&#8217;<br \/>\n    Close the IO files if necessary.<br \/>\n    &#8221;&#8217;<br \/>\n    if ifp != sys.stdin:<br \/>\n        ifp.close()<\/p>\n<p>    if ofp != sys.stdout:<br \/>\n        ofp.close()<\/p>\n<p># ================================================================<br \/>\n# _write<br \/>\n# ================================================================<br \/>\ndef _write(ofp, out, newline=False):<br \/>\n    &#8221;&#8217;<br \/>\n    Write out the data in the correct format.<br \/>\n    &#8221;&#8217;<br \/>\n    if ofp == sys.stdout and isinstance(out, bytes):<br \/>\n        out = out.decode(&#8216;utf-8&#8217;, &#8216;ignored&#8217;)<br \/>\n        ofp.write(out)<br \/>\n        if newline:<br \/>\n            ofp.write(&#8216;\\n&#8217;)<br \/>\n    elif isinstance(out, str):<br \/>\n        ofp.write(out)<br \/>\n        if newline:<br \/>\n            ofp.write(&#8216;\\n&#8217;)<br \/>\n    else:  # assume bytes<br \/>\n        ofp.write(out)<br \/>\n        if newline:<br \/>\n            ofp.write(b&#8217;\\n&#8217;)<\/p>\n<p># ================================================================<br \/>\n# _write<br \/>\n# ================================================================<br \/>\ndef _read(ifp):<br \/>\n    &#8221;&#8217;<br \/>\n    Read the data in the correct format.<br \/>\n    &#8221;&#8217;<br \/>\n    return ifp.read()<\/p>\n<p># ================================================================<br \/>\n# _runenc<br \/>\n# ================================================================<br \/>\ndef _runenc(args):<br \/>\n    &#8221;&#8217;<br \/>\n    Encrypt data.<br \/>\n    &#8221;&#8217;<br \/>\n    if args.passphrase is None:<br \/>\n        while True:<br \/>\n            passphrase = getpass(&#8216;Passphrase: &#8216;)<br \/>\n            tmp = getpass(&#8216;Re-enter passphrase: &#8216;)<br \/>\n            if passphrase == tmp:<br \/>\n                break<br \/>\n            print(&#8221;)<br \/>\n            print(&#8216;Passphrases do not match, please try again.&#8217;)<br \/>\n    else:<br \/>\n        passphrase = args.passphrase<\/p>\n<p>    ifp, ofp = _open_ios(args)<br \/>\n    text = _read(ifp)<br \/>\n    out = encrypt(passphrase, text, msgdgst=args.msgdgst)<br \/>\n    _write(ofp, out, True)<br \/>\n    _close_ios(ifp, ofp)<\/p>\n<p># ================================================================<br \/>\n# _rundec<br \/>\n# ================================================================<br \/>\ndef _rundec(args):<br \/>\n    &#8221;&#8217;<br \/>\n    Decrypt data.<br \/>\n    &#8221;&#8217;<br \/>\n    if args.passphrase is None:<br \/>\n        passphrase = getpass(&#8216;Passphrase: &#8216;)<br \/>\n    else:<br \/>\n        passphrase = args.passphrase<\/p>\n<p>    ifp, ofp = _open_ios(args)<br \/>\n    text = _read(ifp)<br \/>\n    out = decrypt(passphrase, text, msgdgst=args.msgdgst)<br \/>\n    _write(ofp, out, False)<br \/>\n    _close_ios(ifp, ofp)<\/p>\n<p># ================================================================<br \/>\n# _runtest<br \/>\n# ================================================================<br \/>\ndef _runtest(args):<br \/>\n    &#8221;&#8217;<br \/>\n    Run a series of iteration where each iteration generates a random<br \/>\n    password from 8-32 characters and random text from 20 to 256<br \/>\n    characters. The encrypts and decrypts the random data. It then<br \/>\n    compares the results to make sure that everything works correctly.<\/p>\n<p>    The test output looks like this:<\/p>\n<p>    $ crypt 2000<br \/>\n    2000 of 2000 100.00%  15 139 2000    0<br \/>\n    $ #     ^    ^        ^  ^   ^       ^<br \/>\n    $ #     |    |        |  |   |       +&#8211; num failed<br \/>\n    $ #     |    |        |  |   +&#8212;&#8212;&#8212;- num passed<br \/>\n    $ #     |    |        |  +&#8212;&#8212;&#8212;&#8212;&#8211; size of text for a test<br \/>\n    $ #     |    |        +&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; size of passphrase for a test<br \/>\n    $ #     |    +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; percent completed<br \/>\n    $ #     +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- total<br \/>\n    # #+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; current test<\/p>\n<p>    @param args  The args parse arguments.<br \/>\n    &#8221;&#8217;<br \/>\n    import string<br \/>\n    import random<br \/>\n    from random import randint<\/p>\n<p>    # Encrypt\/decrypt N random sets of plaintext and passwords.<br \/>\n    num = args.test<br \/>\n    ofp = sys.stdout<br \/>\n    if args.output is not None:<br \/>\n        try:<br \/>\n            ofp = open(args.output, &#8216;w&#8217;)<br \/>\n        except IOError:<br \/>\n            print(&#8216;ERROR: can open file for writing: {}&#8217;.format(args.output))<br \/>\n            sys.exit(1)<\/p>\n<p>    chset = string.printable<br \/>\n    passed = 0<br \/>\n    failed = []<br \/>\n    maxlen = len(str(num))<br \/>\n    for i in range(num):<br \/>\n        ran1 = randint(8,32)<br \/>\n        password = &#8221;.join(random.choice(chset) for x in range(ran1))<\/p>\n<p>        ran2 = randint(20, 256)<br \/>\n        plaintext = &#8221;.join(random.choice(chset) for x in range(ran2))<\/p>\n<p>        ciphertext = encrypt(password, plaintext, msgdgst=args.msgdgst)<br \/>\n        verification = decrypt(password, ciphertext, msgdgst=args.msgdgst)<\/p>\n<p>        if plaintext != verification:<br \/>\n            failed.append( [password, plaintext] )<br \/>\n        else:<br \/>\n            passed += 1<\/p>\n<p>        output = &#8216;%*d of %d %6.2f%% %3d %3d %*d %*d %s&#8217; % (maxlen,i+1,<br \/>\n                                                           num,<br \/>\n                                                           100*(i+1)\/num,<br \/>\n                                                           len(password),<br \/>\n                                                           len(plaintext),<br \/>\n                                                           maxlen, passed,<br \/>\n                                                           maxlen, len(failed),<br \/>\n                                                           args.msgdgst)<br \/>\n        if args.output is None:<br \/>\n            ofp.write(&#8216;\\b&#8217;*80)<br \/>\n            ofp.write(output)<br \/>\n            ofp.flush()<br \/>\n        else:<br \/>\n            ofp.write(output+&#8217;\\n&#8217;)<\/p>\n<p>    ofp.write(&#8216;\\n&#8217;)<\/p>\n<p>    if len(failed):<br \/>\n        for i in range(len(failed)):<br \/>\n            ofp.write(&#8216;%3d %2d %-34s %3d %s\\n&#8217; % (i,<br \/>\n                                                  len(failed[i][0]),<br \/>\n                                                  &#8216;&#8221;&#8216;+failed[i][0]+'&#8221;&#8216;,<br \/>\n                                                  len(failed[i][1]),<br \/>\n                                                  &#8216;&#8221;&#8216;+failed[i][1]+'&#8221;&#8216;))<br \/>\n        ofp.write(&#8216;\\n&#8217;)<\/p>\n<p>    if args.output is not None:<br \/>\n        ofp.close()<\/p>\n<p># ================================================================<br \/>\n# _cli_opts<br \/>\n# ================================================================<br \/>\ndef _cli_opts():<br \/>\n    &#8221;&#8217;<br \/>\n    Parse command line options.<br \/>\n    @returns the arguments<br \/>\n    &#8221;&#8217;<br \/>\n    mepath = os.path.abspath(sys.argv[0]).encode(&#8216;utf-8&#8242;)<br \/>\n    mebase = os.path.basename(mepath)<\/p>\n<p>    description = &#8221;&#8217;<br \/>\nImplements encryption\/decryption that is compatible with openssl<br \/>\nAES-256 CBC mode.<\/p>\n<p>You can use it as follows:<\/p>\n<p>    EXAMPLE 1: {0} -> {0} (MD5)<br \/>\n        $ # Encrypt and decrypt using {0}.<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            {0} -e -p secret | \\\\<br \/>\n            {0} -d -p secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 2: {0} -> openssl (MD5)<br \/>\n        $ # Encrypt using {0} and decrypt using openssl.<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            {0} -e -p secret | \\\\<br \/>\n            openssl enc -d -aes-256-cbc -md md5 -base64 -salt -pass pass:secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 3: openssl -> {0} (MD5)<br \/>\n        $ # Encrypt using openssl and decrypt using {0}<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret<br \/>\n            {0} -d -p secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 4: openssl -> openssl (MD5)<br \/>\n        $ # Encrypt and decrypt using openssl<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret<br \/>\n            openssl enc -d -aes-256-cbc -md md5 -base64 -salt -pass pass:secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 5: {0} -> {0} (SHA512)<br \/>\n        $ # Encrypt and decrypt using {0}.<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            {0} -e -m sha512 -p secret | \\\\<br \/>\n            {0} -d -m sha512 -p secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 6: {0} -> openssl (SHA512)<br \/>\n        $ # Encrypt using {0} and decrypt using openssl.<br \/>\n        $ echo &#8216;Lorem ipsum dolor sit amet&#8217; | \\\\<br \/>\n            {0} -e -m sha512 -p secret | \\\\<br \/>\n            openssl enc -d -aes-256-cbc -md sha1=512 -base64 -salt -pass pass:secret<br \/>\n        Lorem ipsum dolor sit amet<\/p>\n<p>    EXAMPLE 7:<br \/>\n        $ # Run internal tests.<br \/>\n        $ {0} -t 2000<br \/>\n        2000 of 2000 100.00%%  21 104 2000    0 md5<br \/>\n        $ #     ^    ^        ^  ^   ^       ^ ^<br \/>\n        $ #     |    |        |  |   |       | +- message digest<br \/>\n        $ #     |    |        |  |   |       +&#8212; num failed<br \/>\n        $ #     |    |        |  |   +&#8212;&#8212;&#8212;&#8211; num passed<br \/>\n        $ #     |    |        |  +&#8212;&#8212;&#8212;&#8212;&#8212; size of text for a test<br \/>\n        $ #     |    |        +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; size of passphrase for a test<br \/>\n        $ #     |    +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; percent completed<br \/>\n        $ #     +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; total<br \/>\n        # #+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- current test<br \/>\n&#8221;&#8217;.format(mebase.decode(&#8216;ascii&#8217;, &#8216;ignore&#8217;))<\/p>\n<p>    parser = argparse.ArgumentParser(prog=mebase,<br \/>\n                                     formatter_class=argparse.RawDescriptionHelpFormatter,<br \/>\n                                     description=description,<br \/>\n                                     )<\/p>\n<p>    group = parser.add_mutually_exclusive_group(required=True)<br \/>\n    group.add_argument(&#8216;-d&#8217;, &#8216;&#8211;decrypt&#8217;,<br \/>\n                       action=&#8217;store_true&#8217;,<br \/>\n                       help=&#8217;decryption mode&#8217;)<br \/>\n    group.add_argument(&#8216;-e&#8217;, &#8216;&#8211;encrypt&#8217;,<br \/>\n                       action=&#8217;store_true&#8217;,<br \/>\n                       help=&#8217;encryption mode&#8217;)<br \/>\n    parser.add_argument(&#8216;-i&#8217;, &#8216;&#8211;input&#8217;,<br \/>\n                        action=&#8217;store&#8217;,<br \/>\n                        help=&#8217;input file, default is stdin&#8217;)<br \/>\n    parser.add_argument(&#8216;-m&#8217;, &#8216;&#8211;msgdgst&#8217;,<br \/>\n                        action=&#8217;store&#8217;,<br \/>\n                        default=&#8217;md5&#8242;,<br \/>\n                        help=&#8217;message digest (md5, sha, sha1, sha256, sha512), default is md5&#8242;)<br \/>\n    parser.add_argument(&#8216;-o&#8217;, &#8216;&#8211;output&#8217;,<br \/>\n                        action=&#8217;store&#8217;,<br \/>\n                        help=&#8217;output file, default is stdout&#8217;)<br \/>\n    parser.add_argument(&#8216;-p&#8217;, &#8216;&#8211;passphrase&#8217;,<br \/>\n                        action=&#8217;store&#8217;,<br \/>\n                        help=&#8217;passphrase for encrypt\/decrypt operations&#8217;)<br \/>\n    group.add_argument(&#8216;-t&#8217;, &#8216;&#8211;test&#8217;,<br \/>\n                       action=&#8217;store&#8217;,<br \/>\n                       default=-1,<br \/>\n                       type=int,<br \/>\n                       help=&#8217;test mode (TEST is an integer)&#8217;)<br \/>\n    parser.add_argument(&#8216;-v&#8217;, &#8216;&#8211;verbose&#8217;,<br \/>\n                        action=&#8217;count&#8217;,<br \/>\n                        help=&#8217;the level of verbosity&#8217;)<br \/>\n    parser.add_argument(&#8216;-V&#8217;, &#8216;&#8211;version&#8217;,<br \/>\n                        action=&#8217;version&#8217;,<br \/>\n                        version=&#8217;%(prog)s &#8216;+VERSION)<\/p>\n<p>    args = parser.parse_args()<br \/>\n    return args<\/p>\n<p># ================================================================<br \/>\n# main<br \/>\n# ================================================================<br \/>\ndef main():<br \/>\n    args = _cli_opts()<br \/>\n    if args.test > 0:<br \/>\n        if args.input is not None:<br \/>\n            print(&#8216;WARNING: input argument will be ignored.&#8217;)<br \/>\n        if args.passphrase is not None:<br \/>\n            print(&#8216;WARNING: passphrase argument will be ignored.&#8217;)<br \/>\n        _runtest(args)<br \/>\n    elif args.encrypt:<br \/>\n        _runenc(args)<br \/>\n    elif args.decrypt:<br \/>\n        _rundec(args)<\/p>\n<p># ================================================================<br \/>\n# MAIN<br \/>\n# ================================================================<br \/>\nif __name__ == &#8220;__main__&#8221;:<br \/>\n    main()<br \/>\n[\/crayon]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The example here shows how to encrypt and decrypt data using python in a way that is fully compatible with openssl aes-256-cbc. It is based on the work that I did in C++ Cipher class that is published on this site. It works for both python-2.7 and python-3.x. The key idea is based on the &hellip; <a href=\"https:\/\/joelinoff.com\/blog\/?p=885\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Simple python functions that provide openssl -aes-256-cbc compatible encrypt\/decrypt<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[5,7,16],"tags":[],"_links":{"self":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/885"}],"collection":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=885"}],"version-history":[{"count":19,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/885\/revisions"}],"predecessor-version":[{"id":1752,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/885\/revisions\/1752"}],"wp:attachment":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=885"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=885"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=885"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}