Example #1
0
def get_cert(pccert, native = False):
	from asn1crypto.x509 import Certificate
	cctx = pccert.contents
	cert_data = ctypes.string_at(cctx.pbCertEncoded, cctx.cbCertEncoded)
	if native is False:
		return Certificate.load(cert_data)
	return Certificate.load(cert_data).native
Example #2
0
    def test_self_sign_certificate(self):
        # Warning: proof of concept code only!
        pub, priv = self.session.generate_keypair(KeyType.RSA, 1024)

        tbs = TbsCertificate({
            'version':
            'v1',
            'serial_number':
            1,
            'issuer':
            Name.build({
                'common_name': 'Test Certificate',
            }),
            'subject':
            Name.build({
                'common_name': 'Test Certificate',
            }),
            'signature': {
                'algorithm': 'sha1_rsa',
                'parameters': None,
            },
            'validity': {
                'not_before':
                Time({
                    'utc_time': datetime.datetime(2017, 1, 1, 0, 0),
                }),
                'not_after':
                Time({
                    'utc_time': datetime.datetime(2038, 12, 31, 23, 59),
                }),
            },
            'subject_public_key_info': {
                'algorithm': {
                    'algorithm': 'rsa',
                    'parameters': None,
                },
                'public_key': RSAPublicKey.load(encode_rsa_public_key(pub)),
            }
        })

        # Sign the TBS Certificate
        value = priv.sign(tbs.dump(), mechanism=Mechanism.SHA1_RSA_PKCS)

        cert = Certificate({
            'tbs_certificate': tbs,
            'signature_algorithm': {
                'algorithm': 'sha1_rsa',
                'parameters': None,
            },
            'signature_value': value,
        })

        # Pipe our certificate to OpenSSL to verify it
        with subprocess.Popen((OPENSSL, 'verify'),
                              stdin=subprocess.PIPE,
                              stdout=subprocess.DEVNULL) as proc:

            proc.stdin.write(pem.armor('CERTIFICATE', cert.dump()))
            proc.stdin.close()
            self.assertEqual(proc.wait(), 0)
Example #3
0
def check_ess_certid(cert: x509.Certificate, certid: Union[tsp.ESSCertID,
                                                           tsp.ESSCertIDv2]):
    """
    Match an ``ESSCertID`` value against a certificate.

    :param cert:
        The certificate to match against.
    :param certid:
        The ``ESSCertID`` value.
    :return:
        ``True`` if the ``ESSCertID`` matches the certificate,
        ``False`` otherwise.
    """
    if isinstance(certid, tsp.ESSCertID):
        hash_algo = 'sha1'
    else:
        hash_algo = certid['hash_algorithm']['algorithm'].native

    hash_spec = get_pyca_cryptography_hash(hash_algo)
    md = hashes.Hash(hash_spec)
    md.update(cert.dump())
    digest_value = md.finalize()
    expected_digest_value = certid['cert_hash'].native
    if digest_value != expected_digest_value:
        return False
    expected_issuer_serial: tsp.IssuerSerial = certid['issuer_serial']
    return (not expected_issuer_serial
            or match_issuer_serial(expected_issuer_serial, cert))
Example #4
0
    def test_verify_certificate_ecdsa(self):
        # Warning: proof of concept code only!
        CERT = base64.b64decode("""
        MIIDGjCCAsKgAwIBAgIJAL+PbwiJUZB1MAkGByqGSM49BAEwRTELMAkGA1UEBhMC
        QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp
        dHMgUHR5IEx0ZDAeFw0xNzA3MDMxMTUxMTBaFw0xOTA3MDMxMTUxMTBaMEUxCzAJ
        BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
        dCBXaWRnaXRzIFB0eSBMdGQwggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjO
        PQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAA
        AAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQaw
        zFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i8
        5uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2
        QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAE
        royPJHkCQMq55egxmQxkFWqiz+yJx0MZP98is99SrkiK5UadFim3r3ZSt5kfh/cc
        Ccmy94BZCmihhGJ0F4eB2qOBpzCBpDAdBgNVHQ4EFgQURNXKlYGsAMItf4Ad8fkg
        Rg9ATqEwdQYDVR0jBG4wbIAURNXKlYGsAMItf4Ad8fkgRg9ATqGhSaRHMEUxCzAJ
        BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
        dCBXaWRnaXRzIFB0eSBMdGSCCQC/j28IiVGQdTAMBgNVHRMEBTADAQH/MAkGByqG
        SM49BAEDRwAwRAIgAdJp/S9vSjS6EvRy/9zl5k2DBKGI52A3Ygsp1a96UicCIDul
        m/eL2OcGdNbzqzsC11alhemJX7Qt9GOcVqQwROIm
        """)

        x509 = Certificate.load(CERT)
        key = self.session.create_object(decode_x509_public_key(CERT))
        self.assertIsInstance(key, pkcs11.PublicKey)

        value = x509['tbs_certificate'].dump()

        assert x509.signature_algo == 'ecdsa'
        assert x509.hash_algo == 'sha1'

        signature = decode_ecdsa_signature(x509.signature)

        self.assertTrue(
            key.verify(value, signature, mechanism=Mechanism.ECDSA_SHA1))
    def fetch_certificate(session):
        """
            Return smart card certificate

            Params:
                session: smart card session
        """

        log.info("fetching certificate")
        try:
            certificates = session.findObjects([(LowLevel.CKA_CLASS,
                                                 LowLevel.CKO_CERTIFICATE)])
        except:
            raise SmartCardConnectionError("Certificate not found")

        certificate = None
        log.info("picking non_repudiation certificate")
        for cert in certificates:
            cert_value = SignatureUtils.get_certificate_value(session, cert)
            x509 = Certificate.load(cert_value)
            try:
                key_usage = x509.key_usage_value
                if 'non_repudiation' in key_usage.native:
                    certificate = cert
                    break
            except:
                log.info("key usage not found, skip this certificate")
                continue

        if certificate is None:
            log.info("non_repudiation certificate not found, pick the latest")
            last_certificate_index = len(certificates) - 1
            certificate = certificates[last_certificate_index]

        return certificate
Example #6
0
def exploit_certificate(
    certificate: x509.Certificate,
) -> Tuple[ecdsa.keys.SigningKey, keys.ECPrivateKey]:
    curve_name = certificate.public_key["algorithm"][
        "parameters"].chosen.native
    ec_private_key = generate_ec_private_key(curve_name)

    k = ec_private_key["private_key"].native
    parameters = ec_private_key["parameters"].chosen

    nist_curve = curve_from_ec_parameters(parameters)
    Qx, Qy = certificate.public_key["public_key"].to_coords()
    Gx, Gy = get_exploit_generator(k, Qx, Qy, nist_curve)
    parameters["base"] = keys.ECPoint.from_coords(Gx, Gy)

    ec_private_key["parameters"] = parameters
    ec_private_key["public_key"] = certificate.public_key["public_key"]

    certificate.public_key["algorithm"]["parameters"] = parameters

    exploit_curve = curve_from_ec_parameters(parameters)
    signing_key = ecdsa.keys.SigningKey.from_secret_exponent(
        k, curve=exploit_curve)

    signed_digest_algorithm = x509.SignedDigestAlgorithm(
        {"algorithm": "sha256_ecdsa"})
    certificate["tbs_certificate"]["signature"] = signed_digest_algorithm
    certificate["signature_algorithm"] = signed_digest_algorithm

    sign_certificate(signing_key, certificate)

    return (signing_key, ec_private_key)
Example #7
0
def sync():
    data = request.data
    session = json.loads(data)
    c = dbcon.cursor()
    c.execute("SELECT * FROM Users WHERE uid = ?", (session['mid'], ))
    master = c.fetchone()
    cert = Certificate.load(binascii.unhexlify(master[5]))
    n = cert.public_key.native['public_key']['modulus']
    e = cert.public_key.native['public_key']['public_exponent']

    hash_package = ''.join(sorted([attn['cid'] for attn in session['attns']]))
    digest = SHA256.new()
    digest.update(hash_package)

    public_key = RSA.construct((n, e))

    verifier = PKCS1_v1_5.new(public_key)
    verified = verifier.verify(digest, binascii.unhexlify(session['sig']))

    if verified:
        dbcon.execute(
            "INSERT INTO Sessions (sid, sig, master) VALUES (?, ?, ?)",
            (session['sid'], session['sig'], session['master']))
        for attn in session['attns']:
            db_tuple = (attn['sig'], attn['mid'], attn['uid'], attn['lat'],
                        attn['lon'], attn['ts'], attn['sig'], attn['aid'],
                        attn['confsig'], attn['cid'])
            dbcon.execute(
                "INSERT INTO Attendances (sid, mid, uid, lat, lon, ts, sig, aid, confsig, cid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                db_tuple)
        dbcon.commit()
        return "", 201  # Created
    else:
        return "", 401  # Unauthorized
    def get_cert_info(self, hostname, port=443, cert_file_name='cert.der'):

        f = open(str(cert_file_name), 'wb')
        certificate = ssl.get_server_certificate((hostname, port))
        f.write(ssl.PEM_cert_to_DER_cert(certificate))

        with open(str(cert_file_name), "rb") as f:
            certificate = Certificate.load(f.read())

        modulus = certificate.public_key.native["public_key"]["modulus"]
        pub_exp = certificate.public_key.native["public_key"][
            "public_exponent"]
        sig_algorithm = certificate.signature_algo
        try:
            cert_issuer_country = certificate.issuer.native["country_name"]
        except:
            cert_issuer_country = "Empty"
        try:
            cert_issuer_name = certificate.issuer.native["organization_name"]
        except:
            cert_issuer_name = "Empty"
        try:
            cert_issuer_common_name = certificate.issuer.native["common_name"]
        except:
            cert_issuer_common_name = "Empty"
        self_signed = certificate.self_signed
        hash_algo = certificate.hash_algo
        domains = certificate.valid_domains

        return ('{0:02x}'.format(modulus), pub_exp, sig_algorithm,
                cert_issuer_country, cert_issuer_name, cert_issuer_common_name,
                self_signed, hash_algo, domains)
Example #9
0
def apply_sig(filename: str, detach_path: str):
    """
    Attach the signature for the bundle of the same name at the detach_path
    """
    bundle, filepath = get_bundle_exec(filename)
    detach_bundle = os.path.join(detach_path, os.path.basename(bundle))

    bin_code_signers: Dict[str, CodeSigner] = {}

    for file_path in glob.iglob(os.path.join(detach_bundle, "**"),
                                recursive=True):
        if os.path.isdir(file_path):
            continue
        bundle_relpath = os.path.relpath(file_path, detach_bundle)
        bundle_path = os.path.join(bundle, bundle_relpath)

        if os.path.basename(os.path.dirname(file_path)) == "MacOS":
            # Signature files are only in the MacOS dir
            if file_path.endswith("sign"):
                bin_name, ext = os.path.splitext(file_path)

                bundle_relpath = os.path.relpath(bin_name, detach_bundle)
                bundle_path = os.path.join(bundle, bundle_relpath)

                if bin_name not in bin_code_signers:
                    bin_code_signers[bin_name] = CodeSigner(
                        bundle_path, Certificate(), PrivateKeyInfo())
                bcs = bin_code_signers[bin_name]

                # Figure out which index this sig is for
                idx = 0
                macho = bcs.macho
                if hasattr(bcs.macho, "Fhdr"):
                    if ext == ".sign":
                        raise Exception(
                            "Cannot attach single architecture signature to universal binary"
                        )
                    arch_type = CPU_NAME_TO_TYPE[ext[1:-4]]
                    for i, h in enumerate(bcs.macho.fh):
                        if h.cputype == arch_type:
                            idx = i
                            macho = bcs.macho.arch[i]
                            break

                # Create a CodeSignatureAttacher
                csa = CodeSignatureAttacher(bundle_path, idx, macho, file_path)

                # Add it to the CodeSigner
                bcs.code_signers.append(csa)

                continue

        # Non-signature files are just copied over
        os.makedirs(os.path.dirname(bundle_path), exist_ok=True)
        shutil.copyfile(file_path, bundle_path)

    # Apply the signature for all CodeSigners
    for _, cs in bin_code_signers.items():
        cs.apply_signature()
def extract_from_system(cert_callback=None):
    """
    Extracts trusted CA certs from the system CA cert bundle

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.

    :return:
        A list of 3-element tuples:
         - 0: a byte string of a DER-encoded certificate
         - 1: a set of unicode strings that are OIDs of purposes to trust the
              certificate for
         - 2: a set of unicode strings that are OIDs of purposes to reject the
              certificate for
    """

    all_purposes = '2.5.29.37.0'
    ca_path = system_path()

    output = []
    with open(ca_path, 'rb') as f:
        for armor_type, _, cert_bytes in unarmor(f.read(), multiple=True):
            # Without more info, a certificate is trusted for all purposes
            if armor_type == 'CERTIFICATE':
                if cert_callback:
                    cert_callback(Certificate.load(cert_bytes), None)
                output.append((cert_bytes, set(), set()))

            # The OpenSSL TRUSTED CERTIFICATE construct adds OIDs for trusted
            # and rejected purposes, so we extract that info.
            elif armor_type == 'TRUSTED CERTIFICATE':
                cert, aux = TrustedCertificate.load(cert_bytes)
                reject_all = False
                trust_oids = set()
                reject_oids = set()
                for purpose in aux['trust']:
                    if purpose.dotted == all_purposes:
                        trust_oids = set([purpose.dotted])
                        break
                    trust_oids.add(purpose.dotted)
                for purpose in aux['reject']:
                    if purpose.dotted == all_purposes:
                        reject_all = True
                        break
                    reject_oids.add(purpose.dotted)
                if reject_all:
                    if cert_callback:
                        cert_callback(cert, 'explicitly distrusted')
                    continue
                if cert_callback:
                    cert_callback(cert, None)
                output.append((cert.dump(), trust_oids, reject_oids))

    return output
Example #11
0
def extract_from_system(cert_callback=None):
    """
    Extracts trusted CA certs from the system CA cert bundle

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.

    :return:
        A list of 3-element tuples:
         - 0: a byte string of a DER-encoded certificate
         - 1: a set of unicode strings that are OIDs of purposes to trust the
              certificate for
         - 2: a set of unicode strings that are OIDs of purposes to reject the
              certificate for
    """

    all_purposes = '2.5.29.37.0'
    ca_path = system_path()

    output = []
    with open(ca_path, 'rb') as f:
        for armor_type, _, cert_bytes in unarmor(f.read(), multiple=True):
            # Without more info, a certificate is trusted for all purposes
            if armor_type == 'CERTIFICATE':
                if cert_callback:
                    cert_callback(Certificate.load(cert_bytes), None)
                output.append((cert_bytes, set(), set()))

            # The OpenSSL TRUSTED CERTIFICATE construct adds OIDs for trusted
            # and rejected purposes, so we extract that info.
            elif armor_type == 'TRUSTED CERTIFICATE':
                cert, aux = TrustedCertificate.load(cert_bytes)
                reject_all = False
                trust_oids = set()
                reject_oids = set()
                for purpose in aux['trust']:
                    if purpose.dotted == all_purposes:
                        trust_oids = set([purpose.dotted])
                        break
                    trust_oids.add(purpose.dotted)
                for purpose in aux['reject']:
                    if purpose.dotted == all_purposes:
                        reject_all = True
                        break
                    reject_oids.add(purpose.dotted)
                if reject_all:
                    if cert_callback:
                        cert_callback(cert, 'explicitly distrusted')
                    continue
                if cert_callback:
                    cert_callback(cert, None)
                output.append((cert.dump(), trust_oids, reject_oids))

    return output
Example #12
0
def decode_x509_certificate(der, extended_set=False):
    """
    Decode a DER-encoded X.509 certificate into a dictionary of
    attributes able to be passed to :meth:`pkcs11.Session.create_object`.

    Optionally pass `extended_set` to include additional attributes:
    start date, end date and key identifiers.

    For PEM-encoded certificates, use :func:`asn1crypto.pem.unarmor`.

    .. warning::

        Does not verify certificate.

    :param bytes der: DER-encoded certificate
    :param extended_set: decodes more metadata about the certificate
    :rtype: dict(Attribute,*)
    """
    x509 = Certificate.load(der)
    subject = x509.subject
    issuer = x509.issuer
    serial = x509['tbs_certificate']['serial_number']

    template = {
        Attribute.CLASS: ObjectClass.CERTIFICATE,
        Attribute.CERTIFICATE_TYPE: CertificateType.X_509,
        Attribute.SUBJECT: subject.dump(),
        Attribute.ISSUER: issuer.dump(),
        Attribute.SERIAL_NUMBER: serial.dump(),
        Attribute.VALUE: x509.dump(),
    }

    if extended_set:
        start_date = \
            x509['tbs_certificate']['validity']['not_before'].native.date()
        end_date = \
            x509['tbs_certificate']['validity']['not_after'].native.date()

        template.update({
            Attribute.START_DATE: start_date,
            Attribute.END_DATE: end_date,
        })

        # FIXME: is this correct?
        try:
            template[Attribute.HASH_OF_SUBJECT_PUBLIC_KEY] = \
                x509.key_identifier
        except KeyError:
            pass

        try:
            template[Attribute.HASH_OF_ISSUER_PUBLIC_KEY] = \
                x509.authority_key_identifier
        except KeyError:
            pass

    return template
Example #13
0
    def read_dss(cls, handler: PdfHandler) -> 'DocumentSecurityStore':
        """
        Read a DSS record from a file and add the data to a validation context.

        :param handler:
            PDF handler from which to read the DSS.
        :return:
            A DocumentSecurityStore object describing the current state of the
            DSS.
        """
        try:
            dss_dict = handler.root['/DSS']
        except KeyError as e:
            raise NoDSSFoundError() from e

        cert_refs = {}
        cert_ref_list = get_and_apply(dss_dict, '/Certs', list, default=())
        for cert_ref in cert_ref_list:
            cert_stream: generic.StreamObject = cert_ref.get_object()
            cert: Certificate = Certificate.load(cert_stream.data)
            cert_refs[cert.issuer_serial] = cert_ref

        ocsp_refs = get_and_apply(dss_dict, '/OCSPs', list, default=())
        ocsps = []
        for ocsp_ref in ocsp_refs:
            ocsp_stream: generic.StreamObject = ocsp_ref.get_object()
            resp = asn1_ocsp.OCSPResponse.load(ocsp_stream.data)
            ocsps.append(resp)

        crl_refs = get_and_apply(dss_dict, '/CRLs', list, default=())
        crls = []
        for crl_ref in crl_refs:
            crl_stream: generic.StreamObject = crl_ref.get_object()
            crl = asn1_crl.CertificateList.load(crl_stream.data)
            crls.append(crl)

        # shallow-copy the VRI dictionary
        try:
            vri_entries = dict(dss_dict['/VRI'])
        except KeyError:
            vri_entries = None

        # if the handler is a writer, the DSS will support updates
        if isinstance(handler, BasePdfFileWriter):
            writer = handler
        else:
            writer = None

        # the DSS returned will be backed by the original DSS object, so CRLs
        # are automagically preserved if they happened to be included in
        # the original file
        dss = cls(
            writer=writer, certs=cert_refs, ocsps=ocsp_refs,
            vri_entries=vri_entries, crls=crl_refs, backing_pdf_object=dss_dict
        )
        return dss
Example #14
0
 def verify_signature(self, challenge, application):
     challenge_data = sha256(challenge.encode('utf-8')).digest()
     application_data = sha256(application.encode('utf-8')).digest()
     message = b'\0' + application_data + challenge_data + \
         self.key_handle + self.public_key
     certificate = Certificate.load(self.attestation_certificate)
     # the public key is represented in uncompressed x,y notation,
     # with 0x04 as byte 0 and x and y each 32 bytes, 65 bytes in total
     public_key = bytes(certificate.public_key['public_key'])
     u2f_verify_signature(self.signature, message, public_key)
Example #15
0
    def UnpackX509Certificate(self) -> Certificate:
        b = self.UnpackBytes()
        if len(b) == 0:
            return None

        cert = Certificate.load(b)
        if cert is None:
            self.Add(Exception("Error Parsing X509 Cert"))
            return None

        return cert
Example #16
0
    def load_certs(self):
        """
        Return a generator that parses and yields all certificates in the DSS.

        :return:
            A generator yielding :class:`.Certificate` objects.
        """
        for cert_ref in self.certs.values():
            cert_stream: generic.StreamObject = cert_ref.get_object()
            cert = Certificate.load(cert_stream.data)
            yield cert
Example #17
0
def extarct_components(cert):
    # extract (n, e) components from der cert
    from asn1crypto.x509 import Certificate
    with open(cert, "rb") as f:
        cert = Certificate.load(f.read())

    n = cert.public_key.native["public_key"]["modulus"]
    e = cert.public_key.native["public_key"]["public_exponent"]

    print("{:#x}".format(n))  # prints the modulus (hexadecimal)
    print("{:#x}".format(e))  # same, for the public exponent
    def sign(self, datau, session, cert, cert_value, algomd, sig_attributes, timestamp):
        log.info('get certificate in format x509 to build signer attributes')
        x509 = Certificate.load(cert_value)

        sign_name = sig_attributes['position']['signature_name']
        if sign_name == "":
            sign_name = MyConfigLoader().get_pdf_config()['position']['signatureName']

        dct = {
            b'sigflags': 3,
            b'name': b'%b' % x509.subject.native['common_name'].encode(),
            b'signingdate': b'%b' % timestamp.encode(),
            b'sign_name': sign_name.encode()
        }

        # Variabile segnaposto per i bytes che conterranno il file firmato riferimenti della firma
        zeros = self.aligned(b'\0')

        log.info('start building the new pdf')
        try:
            pdfdata2 = self.makepdf(datau, dct, zeros, sig_attributes)
            log.info('pdf generated correctly')
        except PDFLinearizedError as e:
            raise PDFLinearizedError(e)
        except Exception:
            raise PDFCreationError('Exception on creating pdf')

        log.info('preparing data to be signed')
        startxref = len(datau)
        pdfbr1 = pdfdata2.find(zeros)
        pdfbr2 = pdfbr1 + len(zeros)
        br = [0, startxref + pdfbr1 - 1, startxref + pdfbr2 + 1, len(pdfdata2) - pdfbr2 - 1]
        brfrom = b'[0000000000 0000000000 0000000000 0000000000]'
        brto = b'[%010d %010d %010d %010d]' % tuple(br)
        pdfdata2 = pdfdata2.replace(brfrom, brto, 1)

        b1 = pdfdata2[:br[1] - startxref]
        b2 = pdfdata2[br[2] - startxref:]
        md = session.digestSession(Mechanism(LowLevel.CKM_SHA256))
        md.update(datau)
        md.update(b1)
        md.update(b2)
        md = bytes(md.final())
        log.info('start pdf signing')
        try:
            contents = pdf_signer.sign(None, session, cert, cert_value, algomd, True, md)
            contents = self.aligned(contents)
            pdfdata2 = pdfdata2.replace(zeros, contents, 1)
            log.info('pdf signed')
        except Exception:
            raise PDFSigningError('error in the sign procedure')

        return pdfdata2
 def read_cert_bundle(self, ca_bundle_file, storage=None):
     """Reads a certificate file including certificates in PEM format."""
     if storage is None:
         storage = SnowflakeOCSP.ROOT_CERTIFICATES_DICT
     logger.debug('reading certificate bundle: %s', ca_bundle_file)
     with open(ca_bundle_file, 'rb') as all_certs:
         # don't lock storage
         from asn1crypto import pem
         pem_certs = pem.unarmor(all_certs.read(), multiple=True)
         for type_name, _, der_bytes in pem_certs:
             if type_name == 'CERTIFICATE':
                 crt = Certificate.load(der_bytes)
                 storage[crt.subject.sha256] = crt
Example #20
0
def decode_x509_public_key(der):
    """
    Decode a DER-encoded X.509 certificate's public key into a set of
    attributes able to be passed to :meth:`pkcs11.Session.create_object`.

    For PEM-encoded certificates, use :func:`asn1crypto.pem.unarmor`.

    .. warning::

        Does not verify certificate.

    :param bytes der: DER-encoded certificate
    :rtype: dict(Attribute,*)
    """
    x509 = Certificate.load(der)
    key_info = x509.public_key
    key = bytes(key_info['public_key'])

    key_type = {
        'rsa': KeyType.RSA,
        'dsa': KeyType.DSA,
        'ec': KeyType.EC,
    }[key_info.algorithm]

    attrs = {
        Attribute.CLASS: ObjectClass.PUBLIC_KEY,
        Attribute.KEY_TYPE: key_type,
    }

    if key_type is KeyType.RSA:
        from .rsa import decode_rsa_public_key
        attrs.update(decode_rsa_public_key(key))
    elif key_type is KeyType.DSA:
        from .dsa import decode_dsa_domain_parameters, decode_dsa_public_key
        params = key_info['algorithm']['parameters'].dump()

        attrs.update(decode_dsa_domain_parameters(params))
        attrs.update({
            Attribute.VALUE: decode_dsa_public_key(key),
        })
    elif key_type is KeyType.EC:
        params = key_info['algorithm']['parameters'].dump()

        attrs.update({
            Attribute.EC_PARAMS: params,
            Attribute.EC_POINT: key,
        })
    else:
        raise AssertionError("Should not be reached")

    return attrs
Example #21
0
    def test_verify_certificate_rsa(self):
        # Warning: proof of concept code only!
        x509 = Certificate.load(CERT)
        key = self.session.create_object(decode_x509_public_key(CERT))
        self.assertIsInstance(key, pkcs11.PublicKey)

        value = x509['tbs_certificate'].dump()
        signature = x509.signature

        assert x509.signature_algo == 'rsassa_pkcs1v15'
        assert x509.hash_algo == 'sha1'

        self.assertTrue(
            key.verify(value, signature, mechanism=Mechanism.SHA1_RSA_PKCS))
Example #22
0
    def from_ldap(entry, location):
        adi = MSADCA()
        adi.location = location
        adi.sn = entry['attributes'].get('sn')
        adi.cn = entry['attributes'].get('cn')
        adi.distinguishedName = entry['attributes'].get('distinguishedName')
        adi.whenChanged = entry['attributes'].get('whenChanged')
        adi.whenCreated = entry['attributes'].get('whenCreated')
        adi.cACertificate = entry['attributes'].get('cACertificate')
        if adi.cACertificate is not None:
            adi.cACertificate = Certificate.load(adi.cACertificate)
        adi.name = entry['attributes'].get('name')

        return adi
Example #23
0
    def __init__(self,
                 end_entity_cert,
                 intermediate_certs=None,
                 validation_context=None):
        """
        :param end_entity_cert:
            An asn1crypto.x509.Certificate object or a byte string of the DER or
            PEM-encoded X.509 end-entity certificate to validate

        :param intermediate_certs:
            None or a list of asn1crypto.x509.Certificate objects or a byte
            string of a DER or PEM-encoded X.509 certificate. Used in
            constructing certificate paths for validation.

        :param validation_context:
            A certvalidator.context.ValidationContext() object that controls
            validation options
        """

        if not isinstance(end_entity_cert, Certificate):
            if not isinstance(end_entity_cert, byte_cls):
                raise TypeError(
                    pretty_message(
                        '''
                    end_entity_cert must be a byte string or an instance of
                    asn1crypto.x509.Certificate, not %s
                    ''', type_name(end_entity_cert)))
            if pem.detect(end_entity_cert):
                _, _, end_entity_cert = pem.unarmor(end_entity_cert)
            end_entity_cert = Certificate.load(end_entity_cert)

        if validation_context is None:
            validation_context = ValidationContext()

        if not isinstance(validation_context, ValidationContext):
            raise TypeError(
                pretty_message(
                    '''
                validation_context must be an instance of
                certvalidator.context.ValidationContext, not %s
                ''', type_name(validation_context)))

        if intermediate_certs is not None:
            certificate_registry = validation_context.certificate_registry
            for intermediate_cert in intermediate_certs:
                certificate_registry.add_other_cert(intermediate_cert)

        self._context = validation_context
        self._certificate = end_entity_cert
Example #24
0
    def extract_certificate_chain(self, connection):
        """Gets certificate chain and extract the key info from OpenSSL connection."""
        from OpenSSL.crypto import dump_certificate, FILETYPE_ASN1
        cert_map = OrderedDict()
        logger.debug("# of certificates: %s",
                     len(connection.get_peer_cert_chain()))

        for cert_openssl in connection.get_peer_cert_chain():
            cert_der = dump_certificate(FILETYPE_ASN1, cert_openssl)
            cert = Certificate.load(cert_der)
            logger.debug('subject: %s, issuer: %s', cert.subject.native,
                         cert.issuer.native)
            cert_map[cert.subject.sha256] = cert

        return self.create_pair_issuer_subject(cert_map)
def _fetch_certs(hostname_file):
    with open(hostname_file) as f:
        hostnames = f.read().split("\n")

    map_serial_to_name = {}
    for h in hostnames:
        if not h:
            continue
        connection = _openssl_connect(h, 443)
        for cert_openssl in connection.get_peer_cert_chain():
            cert_der = dump_certificate(FILETYPE_ASN1, cert_openssl)
            cert = Certificate.load(cert_der)
            map_serial_to_name[cert.serial_number] = cert.subject.native

    return map_serial_to_name
Example #26
0
    def __init__(self, end_entity_cert, intermediate_certs=None, validation_context=None):
        """
        :param end_entity_cert:
            An asn1crypto.x509.Certificate object or a byte string of the DER or
            PEM-encoded X.509 end-entity certificate to validate

        :param intermediate_certs:
            None or a list of asn1crypto.x509.Certificate objects or a byte
            string of a DER or PEM-encoded X.509 certificate. Used in
            constructing certificate paths for validation.

        :param validation_context:
            A certvalidator.context.ValidationContext() object that controls
            validation options
        """

        if not isinstance(end_entity_cert, Certificate):
            if not isinstance(end_entity_cert, byte_cls):
                raise TypeError(pretty_message(
                    '''
                    end_entity_cert must be a byte string or an instance of
                    asn1crypto.x509.Certificate, not %s
                    ''',
                    type_name(end_entity_cert)
                ))
            if pem.detect(end_entity_cert):
                _, _, end_entity_cert = pem.unarmor(end_entity_cert)
            end_entity_cert = Certificate.load(end_entity_cert)

        if validation_context is None:
            validation_context = ValidationContext()

        if not isinstance(validation_context, ValidationContext):
            raise TypeError(pretty_message(
                '''
                validation_context must be an instance of
                certvalidator.context.ValidationContext, not %s
                ''',
                type_name(validation_context)
            ))

        if intermediate_certs is not None:
            certificate_registry = validation_context.certificate_registry
            for intermediate_cert in intermediate_certs:
                certificate_registry.add_other_cert(intermediate_cert)

        self._context = validation_context
        self._certificate = end_entity_cert
def _check_certificate(cert_abs_path):
    """
    Check the cerficate. Will raise
    :class:`systemlink.messagebus.exceptions.SystemLinkException` with details if
    the certicate check fails.

    :param cert_abs_path: Absolute path to the cerficate file.
    :type cert_abs_path: str
    :raises systemlink.messagebus.exceptions.SystemLinkException: If the certificate
        check fails.
    """
    if not HAS_ASN1CRYPTO and not HAS_PYCRYPTO:
        error_info = 'Cannot import asn1crypto nor PyCrypto[dome] for certificate check.'
        raise SystemLinkException.from_name('Skyline.InternalServiceError',
                                            info=error_info)

    with open(cert_abs_path) as fp_:
        pem = fp_.read()
    lines = pem.replace(' ', '').split()
    # Skip over begin and end lines
    der = a2b_base64(''.join(lines[1:-1]))
    # Extract validity field from X.509 certificate (see RFC3280)
    if HAS_ASN1CRYPTO:
        cert = Certificate.load(der)
        tbs_certificate = cert['tbs_certificate']
        validity = tbs_certificate['validity']
        not_before = validity['not_before'].native
        not_before = not_before.replace(tzinfo=None)
        not_after = validity['not_after'].native
        not_after = not_after.replace(tzinfo=None)
    else:
        cert = DerSequence()
        cert.decode(der)
        tbs_certificate = DerSequence()
        tbs_certificate.decode(cert[0])
        validity = DerSequence()
        validity.decode(tbs_certificate[4])
        not_before = _der_to_datetime(validity[0])
        not_after = _der_to_datetime(validity[1])
    now = datetime.datetime.utcnow()
    if now < not_before or now > not_after:
        error_info = (
            'Certificate check failed. Certificate is valid from {0} to {1} (UTC). '
            'Current system UTC time is {2}, which is outside the valid range. '
            'Please ensure that the current system time is properly set.'
            ''.format(not_before, not_after, now))
        raise SystemLinkException.from_name(
            'Skyline.AMQPErrorCertificateExpired', info=error_info)
Example #28
0
def parse_der(data):
    cert = Certificate.load(data)
    d = cert.native
    res = {}
    if d.has_key('tbs_certificate'):
        tbs = d['tbs_certificate']
        res['tls_version'] = tbs['version']
        res['tls_serial_number'] = hex(tbs['serial_number'])[2:].rstrip('L')
        res['sig_algo'] = tbs['signature']['algorithm']
        res['tls_issuer'] = dict2str(tbs['issuer'])
        res['not_valid_before'] = str(tbs['validity']['not_before'])
        res['not_valid_after'] = str(tbs['validity']['not_after'])
        pubkey = tbs['subject_public_key_info']
        res['pubkey_algo'] = pubkey['algorithm']['algorithm']
        res['pubkey_param'] = dict2str(pubkey['public_key'])
    return res
Example #29
0
def check_ess_certid(cert: x509.Certificate, certid: Union[tsp.ESSCertID,
                                                           tsp.ESSCertIDv2]):
    if isinstance(certid, tsp.ESSCertID):
        hash_algo = 'sha1'
    else:
        hash_algo = certid['hash_algorithm']['algorithm'].native

    hash_spec = get_pyca_cryptography_hash(hash_algo)
    md = hashes.Hash(hash_spec)
    md.update(cert.dump())
    digest_value = md.finalize()
    expected_digest_value = certid['cert_hash'].native
    if digest_value != expected_digest_value:
        return False
    expected_issuer_serial: tsp.IssuerSerial = certid['issuer_serial']
    return (not expected_issuer_serial
            or match_issuer_serial(expected_issuer_serial, cert))
Example #30
0
 def from_ldap(entry):
     adi = MSADEnrollmentService()
     adi.sn = entry['attributes'].get('sn')
     adi.cn = entry['attributes'].get('cn')
     adi.distinguishedName = entry['attributes'].get('distinguishedName')
     adi.cACertificate = entry['attributes'].get('cACertificate')
     if adi.cACertificate is not None:
         adi.cACertificate = Certificate.load(adi.cACertificate)
     adi.name = entry['attributes'].get('name')
     adi.displayName = entry['attributes'].get('displayName')
     adi.dNSHostName = entry['attributes'].get('dNSHostName')
     adi.cACertificateDN = entry['attributes'].get('cACertificateDN')
     adi.certificateTemplates = entry['attributes'].get(
         'certificateTemplates')
     for serverdef in entry['attributes'].get('msPKI-Enrollment-Servers',
                                              []):
         adi.enrollmentServers.append(serverdef.split('\n')[3])
     return adi
Example #31
0
def extract_chain(server_handshake_bytes):
    """
    Extracts the X.509 certificates from the server handshake bytes for use
    when debugging

    :param server_handshake_bytes:
        A byte string of the handshake data received from the server

    :return:
        A list of asn1crypto.x509.Certificate objects
    """

    output = []

    found = False
    message_bytes = None

    pointer = 0
    while pointer < len(server_handshake_bytes):
        record_header = server_handshake_bytes[pointer:pointer + 5]
        record_type = record_header[0:1]
        record_length = int_from_bytes(record_header[3:])
        sub_type = server_handshake_bytes[pointer + 5:pointer + 6]
        if record_type == b'\x16' and sub_type == b'\x0b':
            found = True
            message_bytes = server_handshake_bytes[pointer + 5:pointer + 5 +
                                                   record_length]
            break
        pointer += 5 + record_length

    if found:
        # The first 7 bytes are the handshake type (1 byte) and total message
        # length (3 bytes) and cert chain length (3 bytes)
        pointer = 7
        while pointer < len(message_bytes):
            cert_length = int_from_bytes(message_bytes[pointer:pointer + 3])
            cert_start = pointer + 3
            cert_end = cert_start + cert_length
            pointer = cert_end
            cert_bytes = message_bytes[cert_start:cert_end]
            output.append(Certificate.load(cert_bytes))

    return output
Example #32
0
def extract_chain(server_handshake_bytes):
    """
    Extracts the X.509 certificates from the server handshake bytes for use
    when debugging

    :param server_handshake_bytes:
        A byte string of the handshake data received from the server

    :return:
        A list of asn1crypto.x509.Certificate objects
    """

    output = []

    found = False
    message_bytes = None

    pointer = 0
    while pointer < len(server_handshake_bytes):
        record_header = server_handshake_bytes[pointer : pointer + 5]
        record_type = record_header[0:1]
        record_length = int_from_bytes(record_header[3:])
        sub_type = server_handshake_bytes[pointer + 5 : pointer + 6]
        if record_type == b"\x16" and sub_type == b"\x0b":
            found = True
            message_bytes = server_handshake_bytes[pointer + 5 : pointer + 5 + record_length]
            break
        pointer += 5 + record_length

    if found:
        # The first 7 bytes are the handshake type (1 byte) and total message
        # length (3 bytes) and cert chain length (3 bytes)
        pointer = 7
        while pointer < len(message_bytes):
            cert_length = int_from_bytes(message_bytes[pointer : pointer + 3])
            cert_start = pointer + 3
            cert_end = cert_start + cert_length
            pointer = cert_end
            cert_bytes = message_bytes[cert_start:cert_end]
            output.append(Certificate.load(cert_bytes))

    return output
Example #33
0
def _read_cert_bundle(ca_bundle_file):
    """
    Reads a certificate file including certificates in PEM format
    """
    from asn1crypto.x509 import Certificate
    from asn1crypto import pem
    certs = []

    logger = getLogger(__name__)
    logger.debug('reading certificate bundle: %s', ca_bundle_file)
    all_certs = open(ca_bundle_file, 'rb').read()

    pem_certs = pem.unarmor(all_certs, multiple=True)
    for type_name, _, der_bytes in pem_certs:
        if type_name == 'CERTIFICATE':
            crt = Certificate.load(der_bytes)
            logger.debug("Found part of the chain..")
            certs.append(crt)

    return certs
Example #34
0
def extract_chain(server_handshake_bytes):
    """
    Extracts the X.509 certificates from the server handshake bytes for use
    when debugging

    :param server_handshake_bytes:
        A byte string of the handshake data received from the server

    :return:
        A list of asn1crypto.x509.Certificate objects
    """

    output = []

    chain_bytes = None

    for record_type, _, record_data in parse_tls_records(server_handshake_bytes):
        if record_type != b'\x16':
            continue
        for message_type, message_data in parse_handshake_messages(record_data):
            if message_type == b'\x0b':
                chain_bytes = message_data
                break
        if chain_bytes:
            break

    if chain_bytes:
        # The first 3 bytes are the cert chain length
        pointer = 3
        while pointer < len(chain_bytes):
            cert_length = int_from_bytes(chain_bytes[pointer:pointer + 3])
            cert_start = pointer + 3
            cert_end = cert_start + cert_length
            pointer = cert_end
            cert_bytes = chain_bytes[cert_start:cert_end]
            output.append(Certificate.load(cert_bytes))

    return output
Example #35
0
def get_list(cache_length=24):
    """
    Retrieves (and caches in memory) the list of CA certs from the OS

    :param cache_length:
        The number of hours to cache the CA certs in memory before they are
        refreshed

    :raises:
        oscrypto.errors.CACertsError - when an error occurs exporting/locating certs

    :return:
        A (copied) list of asn1crypto.x509.Certificate objects of the CA certs
        from the OS
    """

    if not _in_memory_up_to_date(cache_length):
        with memory_lock:
            if not _in_memory_up_to_date(cache_length):
                _module_values['certs'] = [Certificate.load(cert) for cert in extract_from_system()]
                _module_values['last_update'] = time.time()

    return list(_module_values['certs'])
Example #36
0
def extract_from_system(cert_callback=None, callback_only_on_failure=False):
    """
    Extracts trusted CA certificates from the Windows certificate store

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.

    :param callback_only_on_failure:
        A boolean - if the callback should only be called when a certificate is
        not exported.

    :raises:
        OSError - when an error is returned by the OS crypto library

    :return:
        A list of 3-element tuples:
         - 0: a byte string of a DER-encoded certificate
         - 1: a set of unicode strings that are OIDs of purposes to trust the
              certificate for
         - 2: a set of unicode strings that are OIDs of purposes to reject the
              certificate for
    """

    certificates = {}
    processed = {}

    now = datetime.datetime.utcnow()

    for store in ["ROOT", "CA"]:
        store_handle = crypt32.CertOpenSystemStoreW(null(), store)
        handle_error(store_handle)

        context_pointer = null()
        while True:
            context_pointer = crypt32.CertEnumCertificatesInStore(store_handle, context_pointer)
            if is_null(context_pointer):
                break
            context = unwrap(context_pointer)

            trust_all = False
            data = None
            digest = None

            if context.dwCertEncodingType != Crypt32Const.X509_ASN_ENCODING:
                continue

            data = bytes_from_buffer(context.pbCertEncoded, int(context.cbCertEncoded))
            digest = hashlib.sha1(data).digest()
            if digest in processed:
                continue

            processed[digest] = True
            cert_info = unwrap(context.pCertInfo)

            not_before_seconds = _convert_filetime_to_timestamp(cert_info.NotBefore)
            try:
                not_before = datetime.datetime.fromtimestamp(not_before_seconds)
                if not_before > now:
                    if cert_callback:
                        cert_callback(Certificate.load(data), 'not yet valid')
                    continue
            except (ValueError, OSError):
                # If there is an error converting the not before timestamp,
                # it is almost certainly because it is from too long ago,
                # which means the cert is definitely valid by now.
                pass

            not_after_seconds = _convert_filetime_to_timestamp(cert_info.NotAfter)
            try:
                not_after = datetime.datetime.fromtimestamp(not_after_seconds)
                if not_after < now:
                    if cert_callback:
                        cert_callback(Certificate.load(data), 'no longer valid')
                    continue
            except (ValueError, OSError) as e:
                # The only reason we would get an exception here is if the
                # expiration time is so far in the future that it can't be
                # used as a timestamp, or it is before 0. If it is very far
                # in the future, the cert is still valid, so we only raise
                # an exception if the timestamp is less than zero.
                if not_after_seconds < 0:
                    message = e.args[0] + ' - ' + str_cls(not_after_seconds)
                    e.args = (message,) + e.args[1:]
                    raise e

            trust_oids = set()
            reject_oids = set()

            # Here we grab the extended key usage properties that Windows
            # layers on top of the extended key usage extension that is
            # part of the certificate itself. For highest security, users
            # should only use certificates for the intersection of the two
            # lists of purposes. However, many seen to treat the OS trust
            # list as an override.
            to_read = new(crypt32, 'DWORD *', 0)
            res = crypt32.CertGetEnhancedKeyUsage(
                context_pointer,
                Crypt32Const.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
                null(),
                to_read
            )

            # Per the Microsoft documentation, if CRYPT_E_NOT_FOUND is returned
            # from get_error(), it means the certificate is valid for all purposes
            error_code, _ = get_error()
            if not res and error_code != Crypt32Const.CRYPT_E_NOT_FOUND:
                handle_error(res)

            if error_code == Crypt32Const.CRYPT_E_NOT_FOUND:
                trust_all = True
            else:
                usage_buffer = buffer_from_bytes(deref(to_read))
                res = crypt32.CertGetEnhancedKeyUsage(
                    context_pointer,
                    Crypt32Const.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
                    cast(crypt32, 'CERT_ENHKEY_USAGE *', usage_buffer),
                    to_read
                )
                handle_error(res)

                key_usage_pointer = struct_from_buffer(crypt32, 'CERT_ENHKEY_USAGE', usage_buffer)
                key_usage = unwrap(key_usage_pointer)

                # Having no enhanced usage properties means a cert is distrusted
                if key_usage.cUsageIdentifier == 0:
                    if cert_callback:
                        cert_callback(Certificate.load(data), 'explicitly distrusted')
                    continue

                oids = array_from_pointer(
                    crypt32,
                    'LPCSTR',
                    key_usage.rgpszUsageIdentifier,
                    key_usage.cUsageIdentifier
                )
                for oid in oids:
                    trust_oids.add(oid.decode('ascii'))

            cert = None

            # If the certificate is not under blanket trust, we have to
            # determine what purposes it is rejected for by diffing the
            # set of OIDs from the certificate with the OIDs that are
            # trusted.
            if not trust_all:
                cert = Certificate.load(data)
                if cert.extended_key_usage_value:
                    for cert_oid in cert.extended_key_usage_value:
                        oid = cert_oid.dotted
                        if oid not in trust_oids:
                            reject_oids.add(oid)

            if cert_callback and not callback_only_on_failure:
                if cert is None:
                    cert = Certificate.load(data)
                cert_callback(cert, None)

            certificates[digest] = (data, trust_oids, reject_oids)

        result = crypt32.CertCloseStore(store_handle, 0)
        handle_error(result)
        store_handle = None

    return certificates.values()
Example #37
0
def get_path(temp_dir=None, cache_length=24, map_vendor_oids=True, cert_callback=None):
    """
    Get the filesystem path to a file that contains OpenSSL-compatible CA certs.

    On OS X and Windows, there are extracted from the system certificate store
    and cached in a file on the filesystem. This path should not be writable
    by other users, otherwise they could inject CA certs into the trust list.

    :param temp_dir:
        The temporary directory to cache the CA certs in on OS X and Windows.
        Needs to have secure permissions so other users can not modify the
        contents.

    :param cache_length:
        The number of hours to cache the CA certs on OS X and Windows

    :param map_vendor_oids:
        A bool indicating if the following mapping of OIDs should happen for
        trust information from the OS trust list:
         - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.1 (server_auth)
         - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.2 (client_auth)
         - 1.2.840.113635.100.1.8 (apple_smime) -> 1.3.6.1.5.5.7.3.4 (email_protection)
         - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.13 (eap_over_ppp)
         - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.14 (eap_over_lan)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.5 (ipsec_end_system)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.6 (ipsec_tunnel)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.7 (ipsec_user)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.17 (ipsec_ike)
         - 1.2.840.113635.100.1.16 (apple_code_signing) -> 1.3.6.1.5.5.7.3.3 (code_signing)
         - 1.2.840.113635.100.1.20 (apple_time_stamping) -> 1.3.6.1.5.5.7.3.8 (time_stamping)
         - 1.3.6.1.4.1.311.10.3.2 (microsoft_time_stamp_signing) -> 1.3.6.1.5.5.7.3.8 (time_stamping)

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.
        This is only called on Windows and OS X when passed to this function.

    :raises:
        oscrypto.errors.CACertsError - when an error occurs exporting/locating certs

    :return:
        The full filesystem path to a CA certs file
    """

    ca_path, temp = _ca_path(temp_dir)

    # Windows and OS X
    if temp and _cached_path_needs_update(ca_path, cache_length):
        empty_set = set()

        with path_lock:
            if _cached_path_needs_update(ca_path, cache_length):
                with open(ca_path, 'wb') as f:
                    for cert, trust_oids, reject_oids in extract_from_system(cert_callback):
                        if trust_oids == empty_set and reject_oids == empty_set:
                            f.write(armor('CERTIFICATE', cert))
                        else:
                            if map_vendor_oids:
                                trust_oids = _map_oids(trust_oids)
                                reject_oids = _map_oids(reject_oids)
                            f.write(armor(
                                'TRUSTED CERTIFICATE',
                                TrustedCertificate([
                                    Certificate.load(cert),
                                    CertificateAux({
                                        'trust': trust_oids,
                                        'reject': reject_oids,
                                    })
                                ]).dump()
                            ))

    if not ca_path:
        raise CACertsError('No CA certs found')

    return ca_path
Example #38
0
def get_path(temp_dir=None, cache_length=24, cert_callback=None):
    """
    Get the filesystem path to a file that contains OpenSSL-compatible CA certs.

    On OS X and Windows, there are extracted from the system certificate store
    and cached in a file on the filesystem. This path should not be writable
    by other users, otherwise they could inject CA certs into the trust list.

    :param temp_dir:
        The temporary directory to cache the CA certs in on OS X and Windows.
        Needs to have secure permissions so other users can not modify the
        contents.

    :param cache_length:
        The number of hours to cache the CA certs on OS X and Windows

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.
        This is only called on Windows and OS X when passed to this function.

    :raises:
        oscrypto.errors.CACertsError - when an error occurs exporting/locating certs

    :return:
        The full filesystem path to a CA certs file
    """

    ca_path, temp = _ca_path(temp_dir)

    # Windows and OS X
    if temp and _cached_path_needs_update(ca_path, cache_length):
        empty_set = set()

        any_purpose = '2.5.29.37.0'
        apple_ssl = '1.2.840.113635.100.1.3'
        win_server_auth = '1.3.6.1.5.5.7.3.1'

        with path_lock:
            if _cached_path_needs_update(ca_path, cache_length):
                with open(ca_path, 'wb') as f:
                    for cert, trust_oids, reject_oids in extract_from_system(cert_callback, True):
                        if sys.platform == 'darwin':
                            if trust_oids != empty_set and any_purpose not in trust_oids \
                                    and apple_ssl not in trust_oids:
                                if cert_callback:
                                    cert_callback(Certificate.load(cert), 'implicitly distrusted for TLS')
                                continue
                            if reject_oids != empty_set and (apple_ssl in reject_oids
                                                             or any_purpose in reject_oids):
                                if cert_callback:
                                    cert_callback(Certificate.load(cert), 'explicitly distrusted for TLS')
                                continue
                        elif sys.platform == 'win32':
                            if trust_oids != empty_set and any_purpose not in trust_oids \
                                    and win_server_auth not in trust_oids:
                                if cert_callback:
                                    cert_callback(Certificate.load(cert), 'implicitly distrusted for TLS')
                                continue
                            if reject_oids != empty_set and (win_server_auth in reject_oids
                                                             or any_purpose in reject_oids):
                                if cert_callback:
                                    cert_callback(Certificate.load(cert), 'explicitly distrusted for TLS')
                                continue
                        if cert_callback:
                            cert_callback(Certificate.load(cert), None)
                        f.write(armor('CERTIFICATE', cert))

    if not ca_path:
        raise CACertsError('No CA certs found')

    return ca_path
Example #39
0
def get_list(cache_length=24, map_vendor_oids=True, cert_callback=None):
    """
    Retrieves (and caches in memory) the list of CA certs from the OS. Includes
    trust information from the OS - purposes the certificate should be trusted
    or rejected for.

    Trust information is encoded via object identifiers (OIDs) that are sourced
    from various RFCs and vendors (Apple and Microsoft). This trust information
    augments what is in the certificate itself. Any OID that is in the set of
    trusted purposes indicates the certificate has been explicitly trusted for
    a purpose beyond the extended key purpose extension. Any OID in the reject
    set is a purpose that the certificate should not be trusted for, even if
    present in the extended key purpose extension.

    *A list of common trust OIDs can be found as part of the `KeyPurposeId()`
    class in the `asn1crypto.x509` module of the `asn1crypto` package.*

    :param cache_length:
        The number of hours to cache the CA certs in memory before they are
        refreshed

    :param map_vendor_oids:
        A bool indicating if the following mapping of OIDs should happen for
        trust information from the OS trust list:
         - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.1 (server_auth)
         - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.2 (client_auth)
         - 1.2.840.113635.100.1.8 (apple_smime) -> 1.3.6.1.5.5.7.3.4 (email_protection)
         - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.13 (eap_over_ppp)
         - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.14 (eap_over_lan)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.5 (ipsec_end_system)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.6 (ipsec_tunnel)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.7 (ipsec_user)
         - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.17 (ipsec_ike)
         - 1.2.840.113635.100.1.16 (apple_code_signing) -> 1.3.6.1.5.5.7.3.3 (code_signing)
         - 1.2.840.113635.100.1.20 (apple_time_stamping) -> 1.3.6.1.5.5.7.3.8 (time_stamping)
         - 1.3.6.1.4.1.311.10.3.2 (microsoft_time_stamp_signing) -> 1.3.6.1.5.5.7.3.8 (time_stamping)

    :param cert_callback:
        A callback that is called once for each certificate in the trust store.
        It should accept two parameters: an asn1crypto.x509.Certificate object,
        and a reason. The reason will be None if the certificate is being
        exported, otherwise it will be a unicode string of the reason it won't.

    :raises:
        oscrypto.errors.CACertsError - when an error occurs exporting/locating certs

    :return:
        A (copied) list of 3-element tuples containing CA certs from the OS
        trust ilst:
         - 0: an asn1crypto.x509.Certificate object
         - 1: a set of unicode strings of OIDs of trusted purposes
         - 2: a set of unicode strings of OIDs of rejected purposes
    """

    if not _in_memory_up_to_date(cache_length):
        with memory_lock:
            if not _in_memory_up_to_date(cache_length):
                certs = []
                for cert_bytes, trust_oids, reject_oids in extract_from_system(cert_callback):
                    if map_vendor_oids:
                        trust_oids = _map_oids(trust_oids)
                        reject_oids = _map_oids(reject_oids)
                    certs.append((Certificate.load(cert_bytes), trust_oids, reject_oids))
                _module_values['certs'] = certs
                _module_values['last_update'] = time.time()

    return list(_module_values['certs'])