{"id":8959,"library":"dkimpy","title":"DKIMpy: DKIM, ARC, and TLSRPT email signing and verification","description":"DKIMpy is a Python library for creating and verifying DKIM (DomainKeys Identified Mail), ARC (Authenticated Receive Chain), and TLSRPT (TLS Report) signatures on email messages. It provides a robust implementation for email authentication, relying on cryptographic operations and DNS lookups. The current version is 1.1.8, and it maintains a stable release cadence with updates addressing security and compatibility.","status":"active","version":"1.1.8","language":"en","source_language":"en","source_url":"https://github.com/sdgathman/dkimpy","tags":["email","dkim","arc","tlsrpt","security","cryptography","authentication"],"install":[{"cmd":"pip install dkimpy","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core cryptographic operations for signing and verification.","package":"cryptography","optional":false},{"reason":"Required for performing DNS lookups to retrieve public keys during verification.","package":"dnspython","optional":false},{"reason":"Provides additional cryptographic primitives.","package":"pycryptodomex","optional":false},{"reason":"Often used alongside DKIM for generating Authentication-Results headers.","package":"authres","optional":true}],"imports":[{"note":"The primary `dkim` module is nested inside the `dkimpy` package.","wrong":"from dkim import DKIM","symbol":"DKIM","correct":"from dkimpy.dkim import DKIM"},{"note":"`verify` is a function directly within the `dkimpy.dkim` module, not a method on a `DKIM` object.","wrong":"import dkim; dkim.verify(...)","symbol":"verify","correct":"from dkimpy.dkim import verify"},{"symbol":"DKIMException","correct":"from dkimpy.dkim import DKIMException"}],"quickstart":{"code":"import os\nfrom dkimpy.dkim import DKIM, verify, DKIMException\n\n# In a real application, load your actual private key and use your domain/selector.\n# Example key generation (using openssl):\n#   openssl genrsa -out dkim.private 1024\n#   openssl rsa -in dkim.private -pubout -out dkim.public\n# Then load: `with open('dkim.private', 'rb') as f: private_key = f.read()`\nprivate_key = os.environ.get('DKIM_PRIVATE_KEY', b\"\") # Should be bytes\ndomain = os.environ.get('DKIM_DOMAIN', 'example.com')\nselector = os.environ.get('DKIM_SELECTOR', 's1')\n\n# Sample email content (bytes) - DKIMpy works with byte strings\nemail_message_bytes = b\"\"\"From: sender@example.com\\r\\nTo: recipient@example.com\\r\\nSubject: Test DKIM Signature\\r\\n\\r\\nThis is the body of the email.\\r\\n\"\"\"\n\n# --- 1. Sign an email ---\nprint(\"--- Signing an Email ---\")\nif not private_key:\n    print(\"Warning: DKIM_PRIVATE_KEY environment variable not set. Signing will likely fail.\")\n    print(\"Please provide a valid private key for real signing.\")\n\ntry:\n    # Initialize the DKIM signer\n    signer = DKIM(\n        message=email_message_bytes,\n        selector=selector.encode(), # Selector must be bytes\n        domain=domain.encode(),     # Domain must be bytes\n        privkey=private_key\n    )\n    \n    # Sign the message\n    signed_email_bytes = signer.sign()\n    print(\"Email signed successfully. First 500 bytes of signed email:\")\n    print(signed_email_bytes[:500].decode(errors='ignore'))\n    print(\"...\")\n\nexcept DKIMException as e:\n    print(f\"Error signing email: {e}\")\nexcept Exception as e:\n    print(f\"An unexpected error occurred during signing: {e}\")\n\n\n# --- 2. Verify a received email ---\n# For actual verification, the signed email needs to be received, and\n# dkimpy will perform DNS lookups for the public key (TXT record).\nprint(\"\\n--- Verification Example (requires real signed email and DNS) ---\")\nreceived_signed_email_bytes = signed_email_bytes # Use the just-signed email for demonstration\n\ntry:\n    # The `verify` function is a module-level function\n    # It returns a list of (dkim_domain, dkim_selector, ...) tuples for each valid signature.\n    result = verify(received_signed_email_bytes)\n    \n    if result:\n        print(f\"Verification successful. Found {len(result)} valid DKIM signatures.\")\n        # print(f\"Result details: {result}\") # Uncomment for verbose output\n    else:\n        print(\"Verification failed or no valid DKIM-Signature found.\")\n\nexcept DKIMException as e:\n    print(f\"Verification encountered an error: {e}\")\nexcept Exception as e:\n    print(f\"An unexpected error occurred during verification: {e}\")","lang":"python","description":"This quickstart demonstrates how to sign an email using `dkimpy.dkim.DKIM` and how to initiate verification using `dkimpy.dkim.verify`. For actual signing, a private key (bytes) and a domain/selector (bytes) are required. Full verification relies on accurate DNS TXT records for the public key, which `dkimpy` will query automatically."},"warnings":[{"fix":"Migrate your code to Python 3 and update call signatures and data types (e.g., using byte strings for email content and keys instead of unicode).","message":"Version 1.0.0 introduced a major rewrite, making it incompatible with Python 2. Code written for `dkimpy` prior to 1.0.0 will likely break when upgrading to Python 3 with `dkimpy>=1.0.0`.","severity":"breaking","affected_versions":"<1.0.0"},{"fix":"Ensure all string-like inputs are explicitly encoded to bytes using `.encode('utf-8')` or similar, e.g., `b'example.com'`, `b's1'`.","message":"All email content, keys, domain, and selector parameters must be byte strings (`bytes`), not unicode strings (`str`). Passing `str` will lead to `TypeError` or unexpected encoding issues.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure your environment has working DNS resolution. For verification failures, check the domain's DKIM DNS TXT records (`selector._domainkey.example.com`) and confirm they contain a valid public key (p= tag).","message":"DKIM verification heavily relies on successful DNS lookups to retrieve the public key. Network issues, misconfigured DNS records (TXT records), or DNSSEC failures can cause verification to fail.","severity":"gotcha","affected_versions":"All"},{"fix":"Ensure your private key is in PEM format. You can often convert keys using `openssl` if needed.","message":"The private key must be in PEM format. Other formats (e.g., DER) are not directly supported and will result in `cryptography.exceptions.InvalidKey` or other decryption errors.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Change the import statement to `from dkimpy.dkim import DKIM`.","cause":"Attempting to import from the wrong module path. The `dkim` module is nested within the `dkimpy` package.","error":"ImportError: cannot import name 'DKIM' from 'dkim'"},{"fix":"Provide a valid private key (as bytes, in PEM format) to the `privkey` argument when initializing `DKIM` for signing.","cause":"The `privkey` parameter in the `DKIM` constructor was missing, `None`, or an empty byte string.","error":"dkimpy.exceptions.DKIMException: No private key for signing"},{"fix":"Ensure you are passing a complete email message (including headers) that has been previously signed with DKIM.","cause":"The email message passed to `dkimpy.dkim.verify` does not contain a 'DKIM-Signature' header, or the header is malformed.","error":"dkimpy.exceptions.DKIMException: No DKIM-Signature header found"},{"fix":"Encode the string to bytes, e.g., `my_string.encode('utf-8')` or use byte literals `b'my_string'`.","cause":"Attempting to pass a unicode string (`str`) to a function or method expecting bytes (`bytes`), such as email content, domain, selector, or private key.","error":"TypeError: argument 'data' must be bytes, not str"}]}