Exemple #1
0
def read_certificate(crt):
    """
    Read details about a certificate.

    crt:
        Path to PEM-encoded certificate file or PEM-encoded string.


    CLI Example:

    .. code-block:: bash

        salt '*' pki.read_certificate /etc/ssl/certs/example.crt
    """

    if os.path.exists(crt):
        with _fopen(crt, "rb") as f:
            crt = x509.load_pem_x509_certificate(f.read(), _default_backend())
    else:
        crt = x509.load_pem_x509_certificate(crt.encode(), _default_backend())

    ret = {
        "extensions": _read_extensions(crt.extensions),
        "issuer": _read_name(crt.issuer),
        "not_valid_after": str(crt.not_valid_after),
        "not_valid_before": str(crt.not_valid_before),
        "public_key": _read_public_key(crt.public_key(), text=True),
        "serial": crt.serial_number,
        "subject": _read_name(crt.subject),
    }

    return ret
Exemple #2
0
def create_private_key(path, type="ec", size=4096, curve="secp256r1"):
    """
    Create an RSA or elliptic curve private key in PEM format.

    path:
        The file path to write the private key to. File are written with ``600``
        as file mode.

    type:
        Key type to generate, either ``ec`` (default) or ``rsa``.

    size:
        Key length of an RSA key in bits. Defaults to ``4096``.

    curve:
        Curve to use for an EC key. Defaults to ``secp256r1``.

    CLI example:

    .. code-block:: bash

        salt '*' pki.create_private_key /etc/ssl/private/example.key
        salt '*' pki.create_private_key /etc/ssl/private/rsa.key type=rsa
    """
    # pylint: disable=redefined-builtin

    ret = {"type": type}

    if type == "rsa":
        key = rsa.generate_private_key(public_exponent=65537,
                                       key_size=size,
                                       backend=_default_backend())

        ret["size"] = size

    elif type == "ec":
        key = ec.generate_private_key(
            # pylint: disable=protected-access
            curve=ec._CURVE_TYPES[curve.lower()],
            backend=_default_backend(),
        )

        ret["curve"] = curve

    else:
        raise SaltInvocationError("Unsupported key type: {}".format(type))

    out = key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )

    with _fpopen(path, "wb", mode=0o600) as f:
        f.write(out)

    return ret
Exemple #3
0
def generate_kcv(key: bytes, length: int = 2) -> bytes:
    r"""Generate DES key checksum value (KCV).

    Parameters
    ----------
    key : bytes
        Binary key to provide check digits for. Has to be a valid DES key.
    length : int, optional
        Number of KCV bytes returned (default 2).

    Returns
    -------
    kcv : bytes
        Binary KCV (`length` bytes)

    Examples
    --------
    >>> import psec
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> psec.des.generate_kcv(key).hex().upper()
    '08D7'
    """
    cipher = _Cipher(_algorithms.TripleDES(key),
                     _modes.ECB(),
                     backend=_default_backend())
    encryptor = cipher.encryptor()
    return encryptor.update(b"\x00\x00\x00\x00\x00\x00\x00\x00")[:length]
Exemple #4
0
def key_check_digits(key: bytes, length: int = 2) -> bytes:
    r"""Calculate Triple DES key check digits.

    Parameters
    ----------
    key : bytes
        Binary key to provide check digits for. Has to be a valid DES key.
    length : int, optional
        Number of key check digits bytes provided in the response (default 2).

    Returns
    -------
    check_digits : bytes
        Binary check digits (`length` bytes)

    Examples
    --------
    >>> from pyemv import tools
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> tools.key_check_digits(key).hex().upper()
    '08D7'
    """
    cipher = _Cipher(_algorithms.TripleDES(key),
                     _modes.ECB(),
                     backend=_default_backend())
    encryptor = cipher.encryptor()
    return encryptor.update(b"\x00\x00\x00\x00\x00\x00\x00\x00")[:length]
Exemple #5
0
def encrypt_tdes_cbc(key: bytes, iv: bytes, data: bytes) -> bytes:
    r"""Encrypt data using Triple DES CBC algorithm.

    Parameters
    ----------
    key : bytes
        Binary Triple DES key. Has to be a valid DES key.
    iv : bytes
        Binary initial initialization vector for CBC.
    data : bytes
        Binary data to be encrypted.

    Returns
    -------
    encrypted_data : bytes
        Binary encrypted data.

    Examples
    --------
    >>> from pyemv.tools import encrypt_tdes_cbc
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> iv = bytes.fromhex("0000000000000000")
    >>> encrypt_tdes_cbc(key, iv, b"12345678").hex().upper()
    '41D2FFBA3CDC15FE'
    """
    cipher = _Cipher(
        _algorithms.TripleDES(key),
        _modes.CBC(iv),
        backend=_default_backend(),
    )
    return cipher.encryptor().update(data)
Exemple #6
0
def read_private_key(path):
    """
    Read details about a private key.

    path:
        Path to a private key in PEM format.

    CLI Example:

    .. code-block:: bash

        salt '*' pki.read_private_key /etc/ssl/private/example.key
    """

    ret = {}

    with _fopen(path, "rb") as f:
        key = serialization.load_pem_private_key(f.read(),
                                                 password=None,
                                                 backend=_default_backend())

    if isinstance(key, ec.EllipticCurvePrivateKey):
        ret["type"] = "ec"
        ret["curve"] = key.curve.name

    elif isinstance(key, rsa.RSAPrivateKey):
        ret["type"] = "rsa"
        ret["size"] = key.key_size

    else:
        raise SaltInvocationError("Unsupported private key object: {0}".format(
            type(key)))

    return ret
Exemple #7
0
def encrypt_tdes_ecb(key: bytes, data: bytes) -> bytes:
    r"""Encrypt data using Triple DES ECB algorithm.

    Parameters
    ----------
    key : bytes
        Binary Triple DES key. Has to be a valid DES key.
    data : bytes
        Binary data to be encrypted.

    Returns
    -------
    encrypted_data : bytes
        Binary encrypted data.

    Examples
    --------
    >>> from pyemv.tools import encrypt_tdes_ecb
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> encrypt_tdes_ecb(key, b"12345678").hex().upper()
    '41D2FFBA3CDC15FE'
    """
    cipher = _Cipher(_algorithms.TripleDES(key),
                     _modes.ECB(),
                     backend=_default_backend())
    return cipher.encryptor().update(data)
Exemple #8
0
 def _aes256cbc_encrypt(key, iv, plaintext):
     algo = _AES(key)
     mode = _CBC(iv)
     backend = _default_backend()
     cipher = _Cipher(algo, mode, backend)
     encryptor = cipher.encryptor()
     ciphertext = encryptor.update(plaintext)
     ciphertext += encryptor.finalize()
     return ciphertext
def _load_trusted_ca_certs(cafile):
    """Parse the tlsCAFile into a list of certificates."""
    with open(cafile, "rb") as f:
        data = f.read()

    # Load all the certs in the file.
    trusted_ca_certs = []
    backend = _default_backend()
    for cert_data in _re.findall(_CERT_REGEX, data):
        trusted_ca_certs.append(_load_pem_x509_certificate(cert_data, backend))
    return trusted_ca_certs
Exemple #10
0
def read_csr(csr):
    """
    Read details about a certificate signing request.

    csr:
        Path to a certificate signing request file or a PEM-encoded string.
    """

    if not isinstance(csr, x509.CertificateSigningRequest):
        if os.path.isfile(csr):
            with _fopen(csr, "rb") as f:
                csr = x509.load_pem_x509_csr(f.read(), _default_backend())
        else:
            csr = x509.load_pem_x509_csr(csr.encode(), _default_backend())

    return {
        "extensions": _read_extensions(csr.extensions),
        "public_key": _read_public_key(csr.public_key(), text=True),
        "subject": _read_name(csr.subject),
    }
Exemple #11
0
def __encryption():
    __salt = b'salt_'
    __user_input = bytes(input("[Input]: "), "utf8")
    _KDF = _PBKDF2HMAC(algorithm=_hashes.SHA256(),
                       length=32,
                       salt=__salt,
                       iteration=100000,
                       backend=_default_backend())
    __encrypted_String = __user_input.encode()  # Convert to `bytes[]`
    __key = base64.urlsafe_b64decode(_KDF.derive(__encrypted_String))
    __file = open("exported_keys/_crypto_key.key", "wb")
    __file.write("Key:", __key, "\n", "User Input: ", __user_input, "\n",
                 "Encrypted: ", __encrypted_String)
    __file.close()
Exemple #12
0
 def _private_key(self):
     path = os.path.join(self.base, "private.key")
     if not os.path.exists(path):
         logging.info("ACME: creating new private account key...")
         with _fpopen(path, "wb", mode=0o600) as f:
             f.write(
                 rsa.generate_private_key(
                     public_exponent=65537, key_size=4096, backend=_default_backend()
                 ).private_bytes(
                     encoding=serialization.Encoding.PEM,
                     format=serialization.PrivateFormat.PKCS8,
                     encryption_algorithm=serialization.NoEncryption(),
                 )
             )
     with _fopen(path, "rb") as f:
         return josepy.JWK.load(f.read())
def _public_key_hash(cert):
    public_key = cert.public_key()
    # https://tools.ietf.org/html/rfc2560#section-4.2.1
    # "KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
    # (excluding the tag and length fields)"
    # https://stackoverflow.com/a/46309453/600498
    if isinstance(public_key, _RSAPublicKey):
        pbytes = public_key.public_bytes(_Encoding.DER, _PublicFormat.PKCS1)
    elif isinstance(public_key, _EllipticCurvePublicKey):
        pbytes = public_key.public_bytes(_Encoding.X962,
                                         _PublicFormat.UncompressedPoint)
    else:
        pbytes = public_key.public_bytes(_Encoding.DER,
                                         _PublicFormat.SubjectPublicKeyInfo)
    digest = _Hash(_SHA1(), backend=_default_backend())
    digest.update(pbytes)
    return digest.finalize()
Exemple #14
0
def decrypt_tdes_cbc(key: bytes, iv: bytes, data: bytes) -> bytes:
    r"""Decrypt data using Triple DES CBC algorithm.

    Parameters
    ----------
    key : bytes
        Binary Triple DES key. Has to be a valid DES key.
    iv : bytes
        Binary initial initialization vector for CBC.
        Has to be 8 bytes long.
    data : bytes
        Binary data to be decrypted.
        Has to be multiple of 8 bytes.

    Returns
    -------
    decrypted_data : bytes
        Binary decrypted data.

    Raises
    ------
    ValueError
        Data length must be multiple of DES block size 8.

    Examples
    --------
    >>> import psec
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> iv = bytes.fromhex("0000000000000000")
    >>> psec.des.decrypt_tdes_cbc(key, iv, bytes.fromhex("41D2FFBA3CDC15FE"))
    b'12345678'
    """
    if len(data) < 8 or len(data) % 8 != 0:
        raise ValueError(
            f"Data length ({str(len(data))}) must be multiple of DES block size 8."
        )

    cipher = _Cipher(
        _algorithms.TripleDES(key),
        _modes.CBC(iv),
        backend=_default_backend(),
    )
    return cipher.decryptor().update(data)
Exemple #15
0
def renewal_needed(path, days_remaining=28):
    """
    Check if a certificate expires within the specified days.

    path:
        Path to PEM encoded certificate file.

    days_remaining:
        The minimum number of days remaining when the certificate should be
        renewed. Defaults to 28 days.
    """

    with _fopen(path, "rb") as f:
        crt = x509.load_pem_x509_certificate(f.read(),
                                             backend=_default_backend())

    remaining_days = (crt.not_valid_after - datetime.datetime.now()).days

    return remaining_days < days_remaining
Exemple #16
0
def encrypt_tdes_ecb(key: bytes, data: bytes) -> bytes:
    r"""Encrypt data using Triple DES ECB algorithm.

    Parameters
    ----------
    key : bytes
        Binary Triple DES key. Has to be a valid DES key.
    data : bytes
        Binary data to be encrypted.
        Has to be multiple of 8 bytes.

    Returns
    -------
    encrypted_data : bytes
        Binary encrypted data.

    Raises
    ------
    ValueError
        Data length must be multiple of DES block size 8.

    Examples
    --------
    >>> import psec
    >>> key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> psec.des.encrypt_tdes_ecb(key, b"12345678").hex().upper()
    '41D2FFBA3CDC15FE'
    """
    if len(data) < 8 or len(data) % 8 != 0:
        raise ValueError(
            f"Data length ({str(len(data))}) must be multiple of DES block size 8."
        )

    cipher = _Cipher(_algorithms.TripleDES(key),
                     _modes.ECB(),
                     backend=_default_backend())
    return cipher.encryptor().update(data)
Exemple #17
0
def generate_retail_mac(
    key1: bytes,
    key2: bytes,
    data: bytes,
    padding: int,
    length: _typing.Optional[int] = None,
) -> bytes:
    r"""ISO/IEC 9797-1 MAC algorithm 3 aka retail MAC.
    Requires two independent keys.
    All blocks until the last are processed using single DES using key1.
    The last data block is processed using TDES using key2 and key1.
    The resulting block is the MAC.

    Parameters
    ----------
    key1 : bytes
        Binary MAC key used in initial transformation.
        Has to be a valid DES key.
    key2 : bytes
        Binary MAC key used  in output transformation.
        Has to be a valid DES key.
    data : bytes
        Data to be MAC'd.
    padding : int
        Padding method of `data`.

            - 1 = ISO/IEC 9797-1 method 1.
            - 2 = ISO/IEC 9797-1 method 2.
            - 3 = ISO/IEC 9797-1 method 3.

    length : int, optional
        Desired MAC length [4 <= N <= 8] (default 8 bytes).

    Returns
    -------
    mac : bytes
        Returns a binary MAC of requested length

    Raises
    ------
    ValueError
        Invalid padding method specified

    Notes
    -----
    See https://en.wikipedia.org/wiki/ISO/IEC_9797-1 for the
    algorithm reference.

    See Also
    --------
    psec.mac.pad_iso_1 : ISO/IEC 9791-1 padding method 1
    psec.mac.pad_iso_2 : ISO/IEC 9791-1 padding method 2
    psec.mac.pad_iso_3 : ISO/IEC 9791-1 padding method 3

    Examples
    --------
    >>> import psec
    >>> key1 = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> key2 = bytes.fromhex("FEDCBA98765432100123456789ABCDEF")
    >>> data = bytes.fromhex("1234567890ABCDEF")
    >>> psec.mac.generate_retail_mac(key1, key2, data, padding=2).hex().upper()
    '644AA5C915DBDAF8'
    """
    if length is None:
        length = 8

    try:
        data = _pad_dispatch[padding](data, 8)
    except KeyError:
        raise ValueError("Specify valid padding method: 1, 2 or 3.")

    # Encrypt first block with key1 then
    # encrypt the rest of the data in CBC mode
    cipher1 = _Cipher(
        _algorithms.TripleDES(key1),
        _modes.CBC(b"\x00\x00\x00\x00\x00\x00\x00\x00"),
        backend=_default_backend(),
    )
    encryptor1 = cipher1.encryptor()
    data = encryptor1.update(data)[-8:]

    # Decrypt the last block with key2 and then encrypt it with key1
    cipher2 = _Cipher(
        _algorithms.TripleDES(key2), _modes.CBC(data), backend=_default_backend()
    )
    decryptor2 = cipher2.decryptor()
    return encryptor1.update(decryptor2.update(data))[:length]
Exemple #18
0
def create_csr(path=None, text=False, algorithm="sha384", **kwargs):
    """
    Create a certificate signing request (CSR).

    path:
        Path to write the CSR to.

    text:
        Directly return PEM-encoded CSR instead of dictionary with details.
        Either ``path`` or ``text`` must be specified.

    key:
        Path to the private key used to sign the CSR.

    subject:
        Dictionary with subject name pairs.

        .. code-block::
            {"commonName": "localhost"}

    domains:
        Alias method to quickly create a CSR with DNS names. All given domains
        will be added as DNS names to the subject alternative name extension and
        the first domain will additionally used for the subjects common name.

    extensions:
        Dictionary with x509v3 extensions.

        Supported extensions are:

        subjectAltName:
            x509v3 Subject Alternative Name

            .. code-block::

                {"subjectAltName": "DNS:www.example.org,IP:192.0.2.1"}
    """

    key = kwargs.get("key", None)
    subject = kwargs.get("subject", {})
    extensions = kwargs.get("extensions", {})

    if not path and not text:
        raise SaltInvocationError("Either path or text must be specified")

    if not key:
        raise SaltInvocationError("Key required")

    if "domains" in kwargs:
        domains = kwargs["domains"]
        if isinstance(domains, str):
            domains = [d.strip() for d in domains.split(",")]
        if isinstance(domains, list):
            if not subject:
                subject = {"commonName": domains[0]}
            if "subjectAltName" not in extensions:
                extensions["subjectAltName"] = [f"DNS:{n}" for n in domains]

    if not subject:
        raise SaltInvocationError("Subject required")

    if algorithm not in _HASHES:
        raise SaltInvocationError(f"Algorithm not supported: {algorithm}")

    subject = _create_name(subject)
    extensions = _create_extensions(extensions)

    with _fopen(key, "rb") as f:
        key = serialization.load_pem_private_key(f.read(),
                                                 password=None,
                                                 backend=_default_backend())

    csr = x509.CertificateSigningRequestBuilder(subject, extensions).sign(
        key, _HASHES[algorithm], backend=_default_backend())

    out = csr.public_bytes(serialization.Encoding.PEM)

    if path:
        with _fopen(path, "wb") as f:
            f.write(out)

    if text:
        return out.decode()

    return read_csr(csr)
Exemple #19
0
import os
import pathlib
import socket

from cryptography.hazmat.backends import default_backend as _default_backend
from cryptography.hazmat.primitives.asymmetric import rsa

from . import aesm_pb2

fspath = getattr(os, 'fspath', str)  # pylint: disable=invalid-name

ATTRIBUTES = bytes.fromhex('0600000000000000 1f00000000000000')  # flags, xfrm
AESMD_SOCKET = pathlib.Path('/var/run/aesmd/aesm.socket')

_backend = _default_backend()  # pylint: disable=invalid-name


def get_token():
    mrenclave = os.urandom(32)
    key = rsa.generate_private_key(public_exponent=3,
                                   key_size=3072,
                                   backend=_backend)
    modulus = key.public_key().public_numbers().n.to_bytes(384, 'little')

    req = aesm_pb2.GetTokenReq(req=aesm_pb2.GetTokenReqRaw(
        signature=mrenclave, key=modulus, attributes=ATTRIBUTES,
        timeout=10000)).SerializeToString()

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.connect(fspath(AESMD_SOCKET))
Exemple #20
0
def mac_iso9797_3(key1: bytes,
                  key2: bytes,
                  data: bytes,
                  padding: int,
                  length: Optional[int] = None) -> bytes:
    r"""ISO/IEC 9797-1 MAC algorithm 3. Requires two independent keys.
    Only the last data block is processed using TDES,
    all previous blocks are processed using single DES.

    Parameters
    ----------
    key1 : bytes
        Binary MAC key used in initial transformation.
        Has to be a valid DES key.
    key2 : bytes
        Binary MAC key used  in output transformation.
        Has to be a valid DES key.
    data : bytes
        Data to be MAC'd.
    padding : int
        Padding method of `data`.

            - 1 = ISO/IEC 9797-1 method 1.
            - 2 = ISO/IEC 9797-1 method 2.
            - 3 = ISO/IEC 9797-1 method 3.

    length : int, optional
        Desired length of AC [4 <= N <= 8] (default 8 bytes).

    Returns
    -------
    mac : bytes
        Returns a binary MAC of requested length

    Raises
    ------
    ValueError
        Invalid padding method specified

    Notes
    -----
    See https://en.wikipedia.org/wiki/ISO/IEC_9797-1 for the
    algorithm reference.

    See Also
    --------
    pyemv.mac.pad_iso9797_1 : ISO/IEC 9791-1 padding method 1
    pyemv.mac.pad_iso9797_2 : ISO/IEC 9791-1 padding method 2
    pyemv.mac.pad_iso9797_3 : ISO/IEC 9791-1 padding method 3

    Examples
    --------
    >>> from pyemv.mac import mac_iso9797_3
    >>> key1 = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    >>> key2 = bytes.fromhex("FEDCBA98765432100123456789ABCDEF")
    >>> data = bytes.fromhex("1234567890ABCDEF")
    >>> mac_iso9797_3(key1, key2, data, padding=2).hex().upper()
    '644AA5C915DBDAF8'
    """
    if length is None:
        length = 8

    if padding == 1:
        data = pad_iso9797_1(data, 8)
    elif padding == 2:
        data = pad_iso9797_2(data, 8)
    elif padding == 3:
        data = pad_iso9797_3(data, 8)
    else:
        raise ValueError("Specify valid padding method: 1, 2 or 3.")

    # Encrypt first block with key1 then
    # encrypt the rest of the data in CBC mode
    cipher1 = _Cipher(
        _algorithms.TripleDES(key1),
        _modes.CBC(b"\x00\x00\x00\x00\x00\x00\x00\x00"),
        backend=_default_backend(),
    )
    encryptor1 = cipher1.encryptor()
    data = encryptor1.update(data)[-8:]

    # Decrypt the last block with key2 and then encrypt it with key1
    cipher2 = _Cipher(_algorithms.TripleDES(key2),
                      _modes.CBC(data),
                      backend=_default_backend())
    decryptor2 = cipher2.decryptor()
    return encryptor1.update(decryptor2.update(data))[:length]
Exemple #21
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- author: [email protected] -*-

import os
import base64
from binascii import Error

from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend as _default_backend
from cryptography.exceptions import InvalidSignature, InvalidTag

default_backend = _default_backend()


def encode(*bytes):
    return base64.b64encode(bytes[0]).decode('ascii') if len(bytes) == 1 else [
        encode(i) for i in bytes
    ]


def decode(*str):
    return base64.b64decode(
        str[0], validate=True) if len(str) == 1 else [decode(i) for i in str]


def encrypt_password(password, key=None):
    if key:
        key = decode(key)
        verify = True