Python DES Encryption Code Example (Online Runner)

Python DES examples with mode, padding, and encoding controls to mirror the DES tool.

Online calculator: use the site DES tool.

Note: This snippet requires locally installed dependencies and will not run in the online runner.

Calculation method

The DES tool matches the AES controls: ECB/CBC/CFB/OFB/CTR modes, PKCS7 or zero padding for block modes, and hex or base64 ciphertext encodings. This helper mirrors those settings.

Install the dependency first: pip install pycryptodome.

Implementation notes

  • Package: pycryptodome provides Crypto.Cipher.DES and Crypto.Util.Counter.
  • Implementation: keys and IVs are normalized to 8 bytes, and CTR uses the IV bytes to seed the counter.
  • Notes: DES is considered insecure for modern use (56-bit key). Keep it for legacy compatibility only and prefer AES for new designs.
python
from __future__ import annotations

import base64
from typing import Literal

from Crypto.Cipher import DES
from Crypto.Util import Counter

Mode = Literal["ECB", "CBC", "CFB", "OFB", "CTR"]
Padding = Literal["PKCS7", "Zero"]
KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]

BLOCK_SIZE = 8


def _decode_value(value: str, encoding: KeyEncoding) -> bytes:
    return value.encode("utf-8") if encoding == "utf8" else bytes.fromhex(value)


def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
    return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")


def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
    return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)


def _normalize_key(key: bytes) -> bytes:
    return key[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")


def _normalize_iv(iv: bytes | None) -> bytes:
    return (iv or b"")[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")


def _pkcs7_pad(data: bytes) -> bytes:
    pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + bytes([pad_len] * pad_len)


def _pkcs7_unpad(data: bytes) -> bytes:
    if not data:
        return data
    pad_len = data[-1]
    if pad_len < 1 or pad_len > BLOCK_SIZE:
        raise ValueError("Invalid PKCS7 padding")
    if data[-pad_len:] != bytes([pad_len] * pad_len):
        raise ValueError("Invalid PKCS7 padding")
    return data[:-pad_len]


def _zero_pad(data: bytes) -> bytes:
    if len(data) % BLOCK_SIZE == 0:
        return data
    pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + b"\x00" * pad_len


def _zero_unpad(data: bytes) -> bytes:
    return data.rstrip(b"\x00")


def _build_cipher(key: bytes, mode: Mode, iv: bytes | None) -> DES:
    if mode == "ECB":
        return DES.new(key, DES.MODE_ECB)
    if mode == "CBC":
        return DES.new(key, DES.MODE_CBC, iv=_normalize_iv(iv))
    if mode == "CFB":
        return DES.new(key, DES.MODE_CFB, iv=_normalize_iv(iv), segment_size=64)
    if mode == "OFB":
        return DES.new(key, DES.MODE_OFB, iv=_normalize_iv(iv))
    counter = Counter.new(64, initial_value=int.from_bytes(_normalize_iv(iv), "big"))
    return DES.new(key, DES.MODE_CTR, counter=counter)


def des_encrypt(
    plaintext: str,
    key: str,
    *,
    mode: Mode = "CBC",
    padding: Padding = "PKCS7",
    key_encoding: KeyEncoding = "hex",
    iv: str | None = None,
    iv_encoding: KeyEncoding = "hex",
    output_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding))
    iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
    data = plaintext.encode("utf-8")
    if mode in {"ECB", "CBC"}:
        data = _pkcs7_pad(data) if padding == "PKCS7" else _zero_pad(data)
    cipher = _build_cipher(key_bytes, mode, iv_bytes)
    encrypted = cipher.encrypt(data)
    return _encode_ciphertext(encrypted, output_encoding)


def des_decrypt(
    ciphertext: str,
    key: str,
    *,
    mode: Mode = "CBC",
    padding: Padding = "PKCS7",
    key_encoding: KeyEncoding = "hex",
    iv: str | None = None,
    iv_encoding: KeyEncoding = "hex",
    input_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding))
    iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
    data = _decode_ciphertext(ciphertext, input_encoding)
    cipher = _build_cipher(key_bytes, mode, iv_bytes)
    decrypted = cipher.decrypt(data)
    if mode in {"ECB", "CBC"}:
        decrypted = _pkcs7_unpad(decrypted) if padding == "PKCS7" else _zero_unpad(decrypted)
    return decrypted.decode("utf-8", errors="replace")

# Example usage
key_hex = "0123456789abcdef"
iv_hex = "a1b2c3d4e5f60708"

cipher = des_encrypt(
    "hello",
    key_hex,
    mode="CBC",
    padding="PKCS7",
    key_encoding="hex",
    iv=iv_hex,
    iv_encoding="hex",
    output_encoding="hex",
)
print(cipher)

plain = des_decrypt(
    cipher,
    key_hex,
    mode="CBC",
    padding="PKCS7",
    key_encoding="hex",
    iv=iv_hex,
    iv_encoding="hex",
    input_encoding="hex",
)
print(plain)

Complete script (implementation + tests)

python
from __future__ import annotations

import base64
from typing import Literal

from Crypto.Cipher import DES
from Crypto.Util import Counter

Mode = Literal["ECB", "CBC", "CFB", "OFB", "CTR"]
Padding = Literal["PKCS7", "Zero"]
KeyEncoding = Literal["utf8", "hex"]
CipherEncoding = Literal["hex", "base64"]

BLOCK_SIZE = 8


def _decode_value(value: str, encoding: KeyEncoding) -> bytes:
    return value.encode("utf-8") if encoding == "utf8" else bytes.fromhex(value)


def _encode_ciphertext(data: bytes, encoding: CipherEncoding) -> str:
    return data.hex() if encoding == "hex" else base64.b64encode(data).decode("ascii")


def _decode_ciphertext(value: str, encoding: CipherEncoding) -> bytes:
    return bytes.fromhex(value) if encoding == "hex" else base64.b64decode(value)


def _normalize_key(key: bytes) -> bytes:
    return key[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")


def _normalize_iv(iv: bytes | None) -> bytes:
    return (iv or b"")[:BLOCK_SIZE].ljust(BLOCK_SIZE, b"\x00")


def _pkcs7_pad(data: bytes) -> bytes:
    pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + bytes([pad_len] * pad_len)


def _pkcs7_unpad(data: bytes) -> bytes:
    if not data:
        return data
    pad_len = data[-1]
    if pad_len < 1 or pad_len > BLOCK_SIZE:
        raise ValueError("Invalid PKCS7 padding")
    if data[-pad_len:] != bytes([pad_len] * pad_len):
        raise ValueError("Invalid PKCS7 padding")
    return data[:-pad_len]


def _zero_pad(data: bytes) -> bytes:
    if len(data) % BLOCK_SIZE == 0:
        return data
    pad_len = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
    return data + b"\x00" * pad_len


def _zero_unpad(data: bytes) -> bytes:
    return data.rstrip(b"\x00")


def _build_cipher(key: bytes, mode: Mode, iv: bytes | None) -> DES:
    if mode == "ECB":
        return DES.new(key, DES.MODE_ECB)
    if mode == "CBC":
        return DES.new(key, DES.MODE_CBC, iv=_normalize_iv(iv))
    if mode == "CFB":
        return DES.new(key, DES.MODE_CFB, iv=_normalize_iv(iv), segment_size=64)
    if mode == "OFB":
        return DES.new(key, DES.MODE_OFB, iv=_normalize_iv(iv))
    counter = Counter.new(64, initial_value=int.from_bytes(_normalize_iv(iv), "big"))
    return DES.new(key, DES.MODE_CTR, counter=counter)


def des_encrypt(
    plaintext: str,
    key: str,
    *,
    mode: Mode = "CBC",
    padding: Padding = "PKCS7",
    key_encoding: KeyEncoding = "hex",
    iv: str | None = None,
    iv_encoding: KeyEncoding = "hex",
    output_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding))
    iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
    data = plaintext.encode("utf-8")
    if mode in {"ECB", "CBC"}:
        data = _pkcs7_pad(data) if padding == "PKCS7" else _zero_pad(data)
    cipher = _build_cipher(key_bytes, mode, iv_bytes)
    encrypted = cipher.encrypt(data)
    return _encode_ciphertext(encrypted, output_encoding)


def des_decrypt(
    ciphertext: str,
    key: str,
    *,
    mode: Mode = "CBC",
    padding: Padding = "PKCS7",
    key_encoding: KeyEncoding = "hex",
    iv: str | None = None,
    iv_encoding: KeyEncoding = "hex",
    input_encoding: CipherEncoding = "base64",
) -> str:
    key_bytes = _normalize_key(_decode_value(key, key_encoding))
    iv_bytes = _decode_value(iv, iv_encoding) if iv is not None else None
    data = _decode_ciphertext(ciphertext, input_encoding)
    cipher = _build_cipher(key_bytes, mode, iv_bytes)
    decrypted = cipher.decrypt(data)
    if mode in {"ECB", "CBC"}:
        decrypted = _pkcs7_unpad(decrypted) if padding == "PKCS7" else _zero_unpad(decrypted)
    return decrypted.decode("utf-8", errors="replace")


def run_tests() -> None:
    key = "0123456789abcdef"
    iv = "a1b2c3d4e5f60708"
    cipher = des_encrypt("hello", key, mode="CBC", padding="PKCS7", key_encoding="hex", iv=iv, iv_encoding="hex", output_encoding="hex")
    recovered = des_decrypt(cipher, key, mode="CBC", padding="PKCS7", key_encoding="hex", iv=iv, iv_encoding="hex", input_encoding="hex")
    assert recovered == "hello"
    print("DES tests passed")


if __name__ == "__main__":
    run_tests()