def verify(self, datas, datau):
        signed_data = cms.ContentInfo.load(datas)['content']
        # signed_data.debug()

        signature = signed_data['signer_infos'][0].native['signature']
        algo = signed_data['digest_algorithms'][0]['algorithm'].native
        attrs = signed_data['signer_infos'][0]['signed_attrs']
        mdData = getattr(hashlib, algo)(datau).digest()
        if attrs is not None and not isinstance(attrs, core.Void):
            mdSigned = None
            for attr in attrs:
                if attr['type'].native == 'message_digest':
                    mdSigned = attr['values'].native[0]
            signedData = attrs.dump()
            signedData = b'\x31' + signedData[1:]
        else:
            mdSigned = mdData
            signedData = datau
        hashok = mdData == mdSigned
        serial = signed_data['signer_infos'][0]['sid'].native['serial_number']
        public_key = None
        for cert in signed_data['certificates']:
            if serial == cert.native['tbs_certificate']['serial_number']:
                cert = cert.dump()
                cert = pem.armor(u'CERTIFICATE', cert)
                public_key = crypto.load_certificate(
                    crypto.FILETYPE_PEM,
                    cert).get_pubkey().to_cryptography_key()
                break

        try:
            public_key.verify(signature, signedData, padding.PKCS1v15(),
                              getattr(hashes, algo.upper())())
            signatureok = True
        except:
            signatureok = False

        # TODO verify certificates
        certok = True
        for cert in signed_data['certificates']:
            scert = pem.armor(u'CERTIFICATE', cert.dump()).decode()
            if not self.verify_cert(scert):
                print('*' * 10, 'failed certificate verification')
                print('cert.issuer:', cert.native['tbs_certificate']['issuer'])
                print('cert.subject:',
                      cert.native['tbs_certificate']['subject'])
                certok = False
        return {
            'hashok?': hashok,
            'signatureok?': signatureok,
            'certok?': certok
        }
예제 #2
0
    def _format_crl(self, name, content, fmt):
        # type: (str, bytes, str) -> Tuple[str, bytes]
        """
        Forces a CRL to DER or PEM.

        name: The original name of the downloaded CRL.
        content: The CRL contents.
        fmt: 'der' or 'pem'.

        Returns the updated name and content: (name, content),

        """
        base, ext = os.path.splitext(name)
        is_pem = pem.detect(content)

        if fmt == 'pem':
            ext = '.pem'
            if not is_pem:
                content = pem.armor('X509 CRL', content)
        elif fmt == 'der':
            ext = '.crl'
            if is_pem:
                _, _, content = pem.unarmor(content)

        return (base + ext, content)
예제 #3
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)
예제 #4
0
def dump_dh_parameters(dh_parameters, encoding='pem'):
    """
    Serializes an asn1crypto.algos.DHParameters object into a byte string

    :param dh_parameters:
        An asn1crypto.algos.DHParameters object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded DH parameters
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(
            pretty_message(
                '''
            encoding must be one of "pem", "der", not %s
            ''', repr(encoding)))

    if not isinstance(dh_parameters, algos.DHParameters):
        raise TypeError(
            pretty_message(
                '''
            dh_parameters must be an instance of asn1crypto.algos.DHParameters,
            not %s
            ''', type_name(dh_parameters)))

    output = dh_parameters.dump()
    if encoding == 'pem':
        output = pem.armor('DH PARAMETERS', output)
    return output
예제 #5
0
def summon(ctx, architecture, cert_label, output, no_pem, as_pfx, ignore_tty,
           pfx_pass):
    cfg: CertomancerConfig = next(ctx.obj['config'])
    pki_arch = cfg.get_pki_arch(architecture)
    if as_pfx and not pyca_cryptography_present():
        as_pfx = False
        logger.warning(
            "pyca/cryptography not installed, no PFX files will be created")

    output_is_binary = as_pfx or no_pem

    if not ignore_tty and output_is_binary and \
            output is None and sys.stdout.isatty():
        raise click.ClickException(
            "Refusing to write binary output to a TTY. Pass --ignore-tty if "
            "you really want to ignore this check.")

    if as_pfx:
        if pfx_pass is not None:
            pfx_pass = pfx_pass.encode('utf8')
        data = pki_arch.package_pkcs12(cert_label, password=pfx_pass)
    else:
        data = pki_arch.get_cert(CertLabel(cert_label)).dump()
        if not no_pem:
            data = pem.armor('certificate', data)

    if output is None:
        # we want to write bytes, not strings
        sys.stdout.buffer.write(data)
    else:
        with open(output, 'wb') as outf:
            outf.write(data)
예제 #6
0
파일: views.py 프로젝트: bbits/django-pki
    def form_valid(self, form):
        entity = self.object
        issuer = entity.get_issuer()
        cert = Certificate.objects.filter(entity=entity).order_by('-valid_at').first()

        days = form.cleaned_data['days']
        emails = cert.subject_alt_emails
        domains = cert.subject_alt_domains
        ips = cert.subject_alt_ips

        if issuer.is_private_key_encrypted():
            self.req = SigningRequest.objects.create(
                entity=entity,
                key_pem=pem.armor('PUBLIC KEY', cert.public_key_info().dump()),
                days=days, emails=emails, domains=domains, ips=ips
            )
            self.cert = None
        else:
            self.cert = issuer.issue_cert(
                entity, cert.public_key_info(),
                expires_at=timedelta(days=days),
                emails=emails, domains=domains, ips=ips
            )
            self.req = None

        return HttpResponseRedirect(self.get_success_url())
예제 #7
0
def dump_dh_parameters(dh_parameters, encoding='pem'):
    """
    Serializes an asn1crypto.algos.DHParameters object into a byte string

    :param dh_parameters:
        An asn1crypto.algos.DHParameters object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded DH parameters
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(pretty_message(
            '''
            encoding must be one of "pem", "der", not %s
            ''',
            repr(encoding)
        ))

    if not isinstance(dh_parameters, algos.DHParameters):
        raise TypeError(pretty_message(
            '''
            dh_parameters must be an instance of asn1crypto.algos.DHParameters,
            not %s
            ''',
            type_name(dh_parameters)
        ))

    output = dh_parameters.dump()
    if encoding == 'pem':
        output = pem.armor('DH PARAMETERS', output)
    return output
예제 #8
0
def pem_armor_csr(certification_request):
    """
    Encodes a CSR into PEM format

    :param certification_request:
        An asn1crypto.csr.CertificationRequest object of the CSR to armor.
        Typically this is obtained from CSRBuilder.build().

    :return:
        A byte string of the PEM-encoded CSR
    """

    if not isinstance(certification_request, csr.CertificationRequest):
        raise TypeError(_pretty_message(
            '''
            certification_request must be an instance of
            asn1crypto.csr.CertificationRequest, not %s
            ''',
            _type_name(certification_request)
        ))

    return pem.armor(
        'CERTIFICATE REQUEST',
        certification_request.dump()
    )
예제 #9
0
 def dump(self):
     return (
         self.fgprint,
         self.certobj.issuer.human_friendly,
         self.certobj.subject.human_friendly,
         pem.armor('CERTIFICATE', self.certder),
     )
예제 #10
0
    def armor(self, expected_bytes_filename, relative_path, type_name, headers):
        with open(os.path.join(fixtures_dir, relative_path), 'rb') as f:
            byte_string = f.read()

        encoded_bytes = pem.armor(type_name, byte_string, headers=headers)
        with open(os.path.join(fixtures_dir, expected_bytes_filename), 'rb') as f:
            expected_bytes = f.read()
            self.assertEqual(expected_bytes, encoded_bytes)
예제 #11
0
    def dump_to_pem(self):
        """
        Dump the certificate request to a PEM-encoded string

        :return: the certificate request PEM string
        :rtype: str
        """
        return pem.armor(u"CERTIFICATE REQUEST", self._req.dump())
예제 #12
0
    def to_pem(self):
        """Encode the TSSPrivKey as PEM encoded ASN.1.

        Returns:
            Returns the PEM encoding as bytes.
        """
        der = self.to_der()
        return pem.armor("TSS2 PRIVATE KEY", der)
예제 #13
0
    def serve_any_cert(self, _request: Request, *,
                       arch: str, label: str, use_pem):
        mime = 'application/x-pem-file' if use_pem else 'application/pkix-cert'
        pki_arch = self.architectures[ArchLabel(arch)]
        cert = pki_arch.get_cert(CertLabel(label))

        data = cert.dump()
        if use_pem:
            data = pem.armor('certificate', data)
        return Response(data, mimetype=mime)
예제 #14
0
파일: test_pem.py 프로젝트: zdw/asn1crypto
    def armor(self, expected_bytes_filename, relative_path, type_name,
              headers):
        with open(os.path.join(fixtures_dir, relative_path), 'rb') as f:
            byte_string = f.read()

        encoded_bytes = pem.armor(type_name, byte_string, headers=headers)
        with open(os.path.join(fixtures_dir, expected_bytes_filename),
                  'rb') as f:
            expected_bytes = f.read()
            self.assertEqual(expected_bytes, encoded_bytes)
예제 #15
0
    def armor(self, expected_bytes_filename, relative_path, type_name,
              headers):
        with open(os.path.join(fixtures_dir, relative_path), 'rb') as f:
            byte_string = f.read()

        encoded_bytes = pem.armor(type_name, byte_string, headers=headers)
        with open(os.path.join(fixtures_dir, expected_bytes_filename),
                  'rb') as f:
            expected_bytes = f.read()
            # In case a user on Windows has CRLF translation on in Git.
            # Ran into this with the GitHub Actions Windows environments.
            expected_bytes = expected_bytes.replace(b'\r\n', b'\n')
            self.assertEqual(expected_bytes, encoded_bytes)
예제 #16
0
def get_path(temp_dir=None, cache_length=24):
    """
    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

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

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

    ca_path = system_path()

    # Windows and OS X
    if ca_path is None:
        if temp_dir is None:
            temp_dir = tempfile.gettempdir()

        if not os.path.isdir(temp_dir):
            raise CACertsError(pretty_message(
                '''
                The temp dir specified, "%s", is not a directory
                ''',
                temp_dir
            ))

        ca_path = os.path.join(temp_dir, 'oscrypto-ca-bundle.crt')

        if _cached_path_needs_update(ca_path, cache_length):
            with path_lock:
                if _cached_path_needs_update(ca_path, cache_length):
                    with open(ca_path, 'wb') as f:
                        for cert in extract_from_system():
                            f.write(armor('CERTIFICATE', cert))

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

    return ca_path
예제 #17
0
def get_path(temp_dir=None, cache_length=24):
    """
    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

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

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

    ca_path = system_path()

    # Windows and OS X
    if ca_path is None:
        if temp_dir is None:
            temp_dir = tempfile.gettempdir()

        if not os.path.isdir(temp_dir):
            raise CACertsError(
                pretty_message(
                    '''
                The temp dir specified, "%s", is not a directory
                ''', temp_dir))

        ca_path = os.path.join(temp_dir, 'oscrypto-ca-bundle.crt')

        if _cached_path_needs_update(ca_path, cache_length):
            with path_lock:
                if _cached_path_needs_update(ca_path, cache_length):
                    with open(ca_path, 'wb') as f:
                        for cert in extract_from_system():
                            f.write(armor('CERTIFICATE', cert))

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

    return ca_path
def register_ca_certificate():

    if utility_verison() < '20.10':
        print('ERROR: update Azure Sphere SDK to the latest version')
        return

    response = iotclient.get_registration_code()
    regCode = response['registrationCode']

    if os.path.exists(CA_FILE_NAME):
        os.remove(CA_FILE_NAME)

    if os.path.exists(VER_FILE_NAME):
        os.remove(VER_FILE_NAME)

    if not utility_download_ca_certificate(CA_FILE_NAME):
        print('ERROR: failed to download ca certificate')
        return

    if not utility_download_validation_certificate(regCode, VER_FILE_NAME):
        print('ERROR: failed to download validation certificate')
        return

    with open(CA_FILE_NAME, 'rb') as f:
        ca_pem = pem.armor('CERTIFICATE', f.read())

    with open(VER_FILE_NAME, 'rb') as f:
        ver_pem = pem.armor('CERTIFICATE', f.read())

    try:
        response = iotclient.register_ca_certificate(
            caCertificate=ca_pem.decode('utf-8'),
            verificationCertificate=ver_pem.decode('utf-8'),
            setAsActive=True,
            allowAutoRegistration=True)
    except iotclient.exceptions.ResourceAlreadyExistsException:
        print("INFO: Already have this CA registered")
예제 #19
0
    def serve_crl(self, request: Request, *,
                  label: ServiceLabel, arch: str, crl_no, use_pem):
        pki_arch = self.architectures[ArchLabel(arch)]
        mime = 'application/x-pem-file' if use_pem else 'application/pkix-crl'
        if crl_no is not None:
            crl = pki_arch.service_registry.get_crl(label, number=crl_no)
        else:
            crl = pki_arch.service_registry.get_crl(
                label, self.at_time(request)
            )

        data = crl.dump()
        if use_pem:
            data = pem.armor('X509 CRL', data)
        return Response(data, mimetype=mime)
예제 #20
0
    def serve_cert(self, _request: Request, *, label: str, arch: str,
                   cert_label: Optional[str], use_pem):
        mime = 'application/x-pem-file' if use_pem else 'application/pkix-cert'
        pki_arch = self.architectures[ArchLabel(arch)]
        cert_label = CertLabel(cert_label) if cert_label is not None else None

        cert = pki_arch.service_registry.get_cert_from_repo(
            ServiceLabel(label), cert_label
        )
        if cert is None:
            raise NotFound()

        data = cert.dump()
        if use_pem:
            data = pem.armor('certificate', data)
        return Response(data, mimetype=mime)
예제 #21
0
 def main(self):
     cakeyID = bytes((0x1,))
     ca_cert_pem = asn1pem.armor('CERTIFICATE', self.cert_load(cakeyID))
     trusted_cert_pems = (ca_cert_pem,)
     for fname in (
         'pdf-signed-cms-hsm.pdf',
     ):
         print('*' * 20, fname)
         try:
             data = open(fname, 'rb').read()
         except:
             continue
         (hashok, signatureok, certok) = pdf.verify(data, trusted_cert_pems)
         print('signature ok?', signatureok)
         print('hash ok?', hashok)
         print('cert ok?', certok)
예제 #22
0
    def _read(self, to_pem=False):
        """
        This function returns an existing certificate from the OPTIGA(TM) Trust device

        :param to_pem:
            A boolean flag to indicate, whether you want return certificate PEM encoded

        :raises:
            - ValueError - when any of the parameters contain an invalid value
            - TypeError - when any of the parameters are of the wrong type
            - OSError - when an error is returned by the core initialisation library

        :returns:
            A byte string with a PEM certificate or DER encoded byte string
        """
        oid = self._optiga.object_id

        if self.id not in self._optiga.object_id_values:
            raise ValueError(
                'Certificate Slot is not correct. '
                'Supported values are {0} class you used {1}'.format(self._optiga.object_id_values, self.id)
            )
        _supported_objects = (oid.IFX_CERT.value, oid.USER_CERT_1.value, oid.USER_CERT_2.value, oid.USER_CERT_3.value,
                              oid.TRUST_ANCHOR_1.value, oid.TRUST_ANCHOR_2.value,
                              oid.DATA_SLOT_1500B_0, oid.DATA_SLOT_1500B_1)
        if self.id not in _supported_objects:
            raise ValueError(
                'Object ID is not one of supported {0}'.format(_supported_objects)
            )

        der_cert = self.read()

        # print(list(der_cert))

        if len(der_cert) == 0:
            raise ValueError(
                'Certificate Slot {0} is empty'.format(self.id)
            )

        # OPTIGA Trust Code to tag an X509 certificate
        if der_cert[0] == 0xC0:
            der_cert = der_cert[9:]

        if to_pem:
            return asn1_pem.armor('CERTIFICATE', der_cert)
        else:
            return bytes(der_cert)
예제 #23
0
def main():
    pdfname = 'pdf.pdf'
    if len(sys.argv) > 1:
        pdfname = sys.argv[1]
    config = open(pdfname + ".json", "rt").read()
    config = json.loads(config)

    tosign = base64.decodebytes(config['tosign'].encode('ascii'))

    clshsm = Signer(dllpath)
    keyid, cert = clshsm.certificate()
    signed_bytes = clshsm.sign(keyid, tosign, "sha256")

    config['signed_bytes'] = b"".join(
        base64.encodebytes(signed_bytes).split()).decode('ascii')
    config['certificate'] = asn1pem.armor("CERTIFICATE", cert).decode('ascii')
    json.dump(config, open(pdfname + ".json", "wt"), indent=4)
예제 #24
0
def pem_armor_csr(certification_request):
    """
	Encodes a CSR into PEM format
	:param certification_request:
		An asn1crypto.csr.CertificationRequest object of the CSR to armor.
		Typically this is obtained from Builder.build().
	:return:
		A byte string of the PEM-encoded CSR
	"""

    if not isinstance(certification_request, csr.CertificationRequest):
        raise TypeError(
            _pretty_message(
                '''
			certification_request must be an instance of
			asn1crypto.csr.CertificationRequest, not %s
			''', _type_name(certification_request)))

    return pem.armor('CERTIFICATE REQUEST', certification_request.dump())
예제 #25
0
def pem_armor_crl(certificate_list):
    """
    Encodes a CRL into PEM format

    :param certificate_list:
        An asn1crypto.crl.CertificateList object of the CRL to armor.
        Typically this is obtained from CertificateListBuilder.build().

    :return:
        A byte string of the PEM-encoded CRL
    """

    if not isinstance(certificate_list, crl.CertificateList):
        raise TypeError(
            _pretty_message(
                '''
            certificate_list must be an instance of
            asn1crypto.crl.CertificateList, not %s
            ''', _type_name(certificate_list)))

    return pem.armor('X509 CRL', certificate_list.dump())
예제 #26
0
    def encode(byte_string, is_der = None, alg = 'Curve25519' ):
        try:
            raw = OctetString(byte_string)
        except Exception as e:
            raise FailedToParseByteString
        
        privateKey = PrivateKey(raw.dump())
        
        privateKeyAlgorithmIdentifier = PrivateKeyAlgorithmIdentifier( {'algorithm': alg });
        
        oneAsymmetricKey = OneAsymmetricKey({
            'version': Integer(0), 
            'privateKeyAlgorithm': privateKeyAlgorithmIdentifier,
            'privateKey': privateKey})
            
        der = oneAsymmetricKey.dump()

        if is_der:
             return der 

        return pem.armor('PRIVATE KEY',der).decode('ASCII')
예제 #27
0
def pem_armor_crl(certificate_list):
    """
    Encodes a CRL into PEM format

    :param certificate_list:
        An asn1crypto.crl.CertificateList object of the CRL to armor.
        Typically this is obtained from CertificateListBuilder.build().

    :return:
        A byte string of the PEM-encoded CRL
    """

    if not isinstance(certificate_list, crl.CertificateList):
        raise TypeError(_pretty_message(
            '''
            certificate_list must be an instance of
            asn1crypto.crl.CertificateList, not %s
            ''',
            _type_name(certificate_list)
        ))

    return pem.armor('X509 CRL', certificate_list.dump())
예제 #28
0
def dump_public_key(public_key, encoding='pem'):
    """
    Serializes a public key object into a byte string

    :param public_key:
        An oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded public key
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(pretty_message(
            '''
            encoding must be one of "pem", "der", not %s
            ''',
            repr(encoding)
        ))

    is_oscrypto = isinstance(public_key, PublicKey)
    if not isinstance(public_key, keys.PublicKeyInfo) and not is_oscrypto:
        raise TypeError(pretty_message(
            '''
            public_key must be an instance of oscrypto.asymmetric.PublicKey or
            asn1crypto.keys.PublicKeyInfo, not %s
            ''',
            type_name(public_key)
        ))

    if is_oscrypto:
        public_key = public_key.asn1

    output = public_key.dump()
    if encoding == 'pem':
        output = pem.armor('PUBLIC KEY', output)
    return output
예제 #29
0
def dump_certificate(certificate, encoding='pem'):
    """
    Serializes a certificate object into a byte string

    :param certificate:
        An oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded certificate
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(pretty_message(
            '''
            encoding must be one of "pem", "der", not %s
            ''',
            repr(encoding)
        ))

    is_oscrypto = isinstance(certificate, Certificate)
    if not isinstance(certificate, x509.Certificate) and not is_oscrypto:
        raise TypeError(pretty_message(
            '''
            certificate must be an instance of oscrypto.asymmetric.Certificate
            or asn1crypto.x509.Certificate, not %s
            ''',
            type_name(certificate)
        ))

    if is_oscrypto:
        certificate = certificate.asn1

    output = certificate.dump()
    if encoding == 'pem':
        output = pem.armor('CERTIFICATE', output)
    return output
예제 #30
0
def dump_certificate(certificate, encoding='pem'):
    """
    Serializes a certificate object into a byte string

    :param certificate:
        An oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded certificate
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(
            pretty_message(
                '''
            encoding must be one of "pem", "der", not %s
            ''', repr(encoding)))

    is_oscrypto = isinstance(certificate, Certificate)
    if not isinstance(certificate, x509.Certificate) and not is_oscrypto:
        raise TypeError(
            pretty_message(
                '''
            certificate must be an instance of oscrypto.asymmetric.Certificate
            or asn1crypto.x509.Certificate, not %s
            ''', type_name(certificate)))

    if is_oscrypto:
        certificate = certificate.asn1

    output = certificate.dump()
    if encoding == 'pem':
        output = pem.armor('CERTIFICATE', output)
    return output
예제 #31
0
def dump_public_key(public_key, encoding='pem'):
    """
    Serializes a public key object into a byte string

    :param public_key:
        An oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo object

    :param encoding:
        A unicode string of "pem" or "der"

    :return:
        A byte string of the encoded public key
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(
            pretty_message(
                '''
            encoding must be one of "pem", "der", not %s
            ''', repr(encoding)))

    is_oscrypto = isinstance(public_key, PublicKey)
    if not isinstance(public_key, keys.PublicKeyInfo) and not is_oscrypto:
        raise TypeError(
            pretty_message(
                '''
            public_key must be an instance of oscrypto.asymmetric.PublicKey or
            asn1crypto.keys.PublicKeyInfo, not %s
            ''', type_name(public_key)))

    if is_oscrypto:
        public_key = public_key.asn1

    output = public_key.dump()
    if encoding == 'pem':
        output = pem.armor('PUBLIC KEY', output)
    return output
예제 #32
0
def necronomicon(ctx, architecture, crl_repo, output, no_pem, at_time,
                 ignore_tty):
    cfg: CertomancerConfig = next(ctx.obj['config'])
    pki_arch = cfg.get_pki_arch(architecture)
    if at_time is None:
        at_time = datetime.now(tz=tzlocal.get_localzone())
    else:
        at_time = parse_dt(at_time)
    crl = pki_arch.service_registry.get_crl(repo_label=crl_repo,
                                            at_time=at_time)

    if output is None and no_pem and not ignore_tty and sys.stdout.isatty():
        raise click.ClickException(
            "Refusing to write binary output to a TTY. Pass --ignore-tty if "
            "you really want to ignore this check.")
    data = crl.dump()
    if not no_pem:
        data = pem.armor('X509 CRL', data)

    if output is None:
        sys.stdout.buffer.write(data)
    else:
        with open(output, 'wb') as f:
            f.write(data)
예제 #33
0
파일: cert.py 프로젝트: jbg/python-smime
 def to_pem(self):
     """Get the PEM-encoding of the certificate."""
     return pem.armor(self.PEM_MARKERS[0], self.to_der())
예제 #34
0
def verify_bankid_response(bank_id_response,
                           ensure_certificates_still_valid=True,
                           BANK_ID_ROOT_CERT=None):

    if not isinstance(bank_id_response, dict):
        raise TypeError("Response not a dictionary")

    if 'completionData' not in bank_id_response:
        raise AttributeError('Completion data missing in dictionary')

    try:
        cdc = CompletionDataContainer(bank_id_response['completionData'])

        # First step is to hash the data and verify the digest matches

        _LOG.info("1. Message Digest Verification\n")
        bid_signed_data_raw_bytes = cdc.signature_container.bid_signed_data_raw.encode(
        )

        # TODO - Parse out of the XML which hashing algorithm should be sued

        bid_signed_data_hash = hashlib.sha256(
            bid_signed_data_raw_bytes).digest().hex()
        key_info_hash = hashlib.sha256(
            cdc.signature_container.key_info_raw.encode()).digest().hex()

        signed_data_hash_from_signature = base64.b64decode(
            cdc.signature_container.signed_data_digest.text).hex()
        key_info_hash_from_signature = base64.b64decode(
            cdc.signature_container.key_data_digest.text).hex()

        if bid_signed_data_hash != signed_data_hash_from_signature:
            raise AssertionError('Signed Data hash does not match!')

        if key_info_hash != key_info_hash_from_signature:
            raise AssertionError('Key Info hash does not match!')

        _LOG.info("\n2. Signature verification\n")

        # Helper function for the certificates
        user_certificate_string = make_cert(
            cdc.signature_container.certificates[0].text)

        # Making a certificate object out of it
        user_certificate = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            BytesIO(user_certificate_string.encode()).read())

        signature_bytes = base64.b64decode(
            cdc.signature_container.signature_value.text)
        signed_info = cdc.signature_container.signed_info.encode()

        try:
            _LOG.debug("Certificate:", user_certificate.get_subject())
            _LOG.debug("Signature Bytes:", signature_bytes)
            _LOG.debug("Signature Data Raw:", signed_info)

            OpenSSL.crypto.verify(user_certificate, signature_bytes,
                                  signed_info, 'sha256')
        except OpenSSL.crypto.Error as e:
            raise AssertionError('The BankID signature is not valid!')

        _LOG.info("\n3. OCSP Response Verification\n")

        ocsp = base64.b64decode(
            bank_id_response['completionData']['ocspResponse'])
        ocsp_response = asn1crypto.ocsp.OCSPResponse.load(ocsp)

        basic_ocsp_response = ocsp_response['response_bytes'][
            'response'].parsed

        # Some help by listing all the different parts of the OCSP response
        _LOG.debug("TBS Response Data",
                   basic_ocsp_response['tbs_response_data'])
        _LOG.debug("SignatureAlgorithm",
                   basic_ocsp_response['signature_algorithm'].signature_algo)
        _LOG.debug("SignatureAlgorithm Hash Function",
                   basic_ocsp_response['signature_algorithm'].hash_algo)
        _LOG.debug("Signature", basic_ocsp_response['signature'].__bytes__())
        _LOG.debug("Cert", basic_ocsp_response['certs'])

        # Response content
        _LOG.debug("version",
                   basic_ocsp_response['tbs_response_data']['version'])
        _LOG.debug("responderID", basic_ocsp_response['tbs_response_data']
                   ['responder_id'])  # has native
        _LOG.info("producedAt",
                  basic_ocsp_response['tbs_response_data']['produced_at'])

        cest = pytz.timezone('Europe/Stockholm')
        ocsp_produced_at = basic_ocsp_response['tbs_response_data'][
            'produced_at'].native

        if not isinstance(ocsp_produced_at, datetime.datetime):
            raise AssertionError('OCSP produced at is not a datetime!')

        ocsp_produced_at = ocsp_produced_at.astimezone(cest).strftime(
            '%Y-%m-%d %H:%M:%S')

        _LOG.debug("responses",
                   basic_ocsp_response['tbs_response_data']['responses'])
        _LOG.debug(
            "response Extentions",
            basic_ocsp_response['tbs_response_data']['response_extensions'])

        _LOG.debug("Extentions")
        extention = basic_ocsp_response['tbs_response_data'][
            'response_extensions'][0]

        _LOG.debug('extn_id', extention['extn_id'])
        _LOG.debug('critical', extention['critical'])

        # Cannot _LOG.debug the value without an exception being raised - need to parse that ourself later
        # print ('extn_value', extention['extn_value'])

        single_response = basic_ocsp_response['tbs_response_data'][
            'responses'][0]

        _LOG.debug("CertID", single_response['cert_id'])
        _LOG.debug("certStatus", single_response['cert_status'])
        _LOG.debug("thisUpdate", single_response['this_update'])
        _LOG.debug("nextUpdate", single_response['next_update'])
        _LOG.debug("singleExtensions", single_response['single_extensions'])

        _LOG.info("3.1. OCSP Response - Verify success ")

        if ocsp_response['response_status'].native != 'successful':
            raise AssertionError('OCSP response status was not successful')

        _LOG.info("3.2. OCSP Response - Verify signature ")

        # Transform the asn1 certificate to an openssl certificate
        der_bytes = basic_ocsp_response['certs'][0].dump()
        pem_bytes = pem.armor('CERTIFICATE', der_bytes)
        ocsp_certificate = crypto.load_certificate(crypto.FILETYPE_PEM,
                                                   pem_bytes)

        # Get the signature bytes
        signature = basic_ocsp_response['signature'].__bytes__()

        # Dump the TBS response data as DER bytes
        signature_data = basic_ocsp_response['tbs_response_data'].dump()

        # Define the hashing algorithm to be used
        digest_method = basic_ocsp_response['signature_algorithm'].hash_algo

        _LOG.debug("Certificate", ocsp_certificate.get_subject())
        _LOG.debug("Signature", signature)
        _LOG.debug("Signature data", signature_data)
        _LOG.debug("Digest Method", digest_method)

        try:
            OpenSSL.crypto.verify(ocsp_certificate, signature, signature_data,
                                  digest_method)
        except OpenSSL.crypto.Error as e:
            raise AssertionError('The OCSP signature is not valid!')

        _LOG.info("3.2. OCSP Response - Compare nonce")

        nonce_computed = hashlib.sha1(
            bank_id_response['completionData']['signature'].encode(
                'utf-8')).digest().hex()

        # A helper because the asn1 library seems to have a problem with the nonce parsing in some form or the other
        nonce_parser = NonceParse(extention.contents)

        # Verify that the computed nonce is part of the nonce value given in the oscp
        # Note that it only partially matches as we use sha-1 to compute the hash

        _LOG.debug("Nonce value computed ", nonce_computed)
        _LOG.debug("Nonce value presented", nonce_parser.value.hex())

        if not nonce_parser.value.hex().startswith(nonce_computed):
            raise AssertionError('Computed nonce not matching the OCSP nonce')

        _LOG.info(
            "\n4. Verify all the certificates by relying on the BankID root certificate as a trusted one \n"
        )

        user_cert = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            make_cert(cdc.signature_container.certificates[0].text).encode())

        bank_user_cert = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            make_cert(cdc.signature_container.certificates[1].text).encode())

        bank_bank_id_cert = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            make_cert(cdc.signature_container.certificates[2].text).encode())

        bank_id_root_cert = crypto.load_certificate(crypto.FILETYPE_PEM,
                                                    BANK_ID_ROOT_CERT.encode())

        # 3. verify the certificate chain of the tbs certificate

        # Make sure we respect or do not respect certificate expiration times
        if not ensure_certificates_still_valid:
            tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)

            bank_user_cert.set_notAfter(
                tomorrow.strftime('%Y%m%d%H%M%SZ').encode())
            bank_bank_id_cert.set_notAfter(
                tomorrow.strftime('%Y%m%d%H%M%SZ').encode())
            bank_id_root_cert.set_notAfter(
                tomorrow.strftime('%Y%m%d%H%M%SZ').encode())

            ocsp_certificate.set_notAfter(
                tomorrow.strftime('%Y%m%d%H%M%SZ').encode())
            user_cert.set_notAfter(tomorrow.strftime('%Y%m%d%H%M%SZ').encode())

        store = crypto.X509Store()
        store.add_cert(bank_user_cert)
        store.add_cert(bank_bank_id_cert)
        store.add_cert(bank_id_root_cert)

        try:
            # Verify the user certificate up to the root certificate
            store_ctx = crypto.X509StoreContext(store, user_cert)
            store_ctx.verify_certificate()
            _LOG.debug('User Certificate issued by the respective bank... OK')
        except X509StoreContextError:
            raise AssertionError(
                'BankID user certificate chain could not be verified.')

        try:
            # Verify the ocsp certificate up to the root certificate
            store_ctx = crypto.X509StoreContext(store, ocsp_certificate)
            store_ctx.verify_certificate()
            _LOG.debug('OCSP Certificate issued by the respective bank... OK')
        except X509StoreContextError:
            raise AssertionError(
                'OCSP certificate chain could not be verified.')

    except Exception as e:
        raise e

    return ocsp_produced_at
예제 #35
0
파일: tests.py 프로젝트: bbits/django-pki
 def private_key_pem(self, key):
     return pem.armor('RSA PRIVATE KEY', asymmetric.dump_private_key(key, None, 'der'))
예제 #36
0
    def verify(self, datas, datau):
        signed_data = cms.ContentInfo.load(datas)['content']
        # signed_data.debug()

        signature = signed_data['signer_infos'][0].native['signature']
        algo = signed_data['digest_algorithms'][0]['algorithm'].native
        attrs = signed_data['signer_infos'][0]['signed_attrs']
        mdData = getattr(hashlib, algo)(datau).digest()
        if attrs is not None and not isinstance(attrs, core.Void):
            mdSigned = None
            for attr in attrs:
                if attr['type'].native == 'message_digest':
                    mdSigned = attr['values'].native[0]
            signedData = attrs.dump()
            signedData = b'\x31' + signedData[1:]
        else:
            mdSigned = mdData
            signedData = datau
        hashok = mdData == mdSigned
        serial = signed_data['signer_infos'][0]['sid'].native['serial_number']
        public_key = None
        for cert in signed_data['certificates']:
            if serial == cert.native['tbs_certificate']['serial_number']:
                cert = cert.dump()
                cert = pem.armor(u'CERTIFICATE', cert)
                public_key = crypto.load_certificate(crypto.FILETYPE_PEM, cert).get_pubkey().to_cryptography_key()
                break

        sigalgo = signed_data['signer_infos'][0]['signature_algorithm']
        # sigalgo.debug()
        sigalgoname = sigalgo.signature_algo
        if sigalgoname == 'rsassa_pss':
            parameters = sigalgo['parameters']
            #parameters.debug()
            #print(parameters.native)
            salgo = parameters['hash_algorithm'].native['algorithm'].upper()
            mgf = getattr(padding, parameters['mask_gen_algorithm'].native['algorithm'].upper())(getattr(hashes, salgo)())
            salt_length = parameters['salt_length'].native
            try:
                public_key.verify(
                    signature,
                    signedData,
                    padding.PSS(mgf, salt_length),
                    getattr(hashes, salgo)()
                )
                signatureok = True
            except:
                signatureok = False
        elif sigalgoname == 'rsassa_pkcs1v15':
            try:
                public_key.verify(
                    signature,
                    signedData,
                    padding.PKCS1v15(),
                    getattr(hashes, algo.upper())()
                )
                signatureok = True
            except:
                signatureok = False
        else:
            raise ValueError('Unknown signature algorithm')
        # TODO verify certificates
        certok = True
        for cert in signed_data['certificates']:
            scert = pem.armor(u'CERTIFICATE', cert.dump()).decode()
            if not self.verify_cert(scert):
                print('*' * 10, 'failed certificate verification')
                print('cert.issuer:', cert.native['tbs_certificate']['issuer'])
                print('cert.subject:', cert.native['tbs_certificate']['subject'])
                certok = False
        return (hashok, signatureok, certok)
예제 #37
0
def dump_openssl_private_key(private_key, passphrase):
    """
    Serializes a private key object into a byte string of the PEM formats used
    by OpenSSL. The format chosen will depend on the type of private key - RSA,
    DSA or EC.

    Do not use this method unless you really must interact with a system that
    does not support PKCS#8 private keys. The encryption provided by PKCS#8 is
    far superior to the OpenSSL formats. This is due to the fact that the
    OpenSSL formats don't stretch the passphrase, making it very easy to
    brute-force.

    :param private_key:
        An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
        object

    :param passphrase:
        A unicode string of the passphrase to encrypt the private key with.
        A passphrase of None will result in no encryption. A blank string will
        result in a ValueError to help ensure that the lack of passphrase is
        intentional.

    :raises:
        ValueError - when a blank string is provided for the passphrase

    :return:
        A byte string of the encoded and encrypted public key
    """

    if passphrase is not None:
        if not isinstance(passphrase, str_cls):
            raise TypeError(pretty_message(
                '''
                passphrase must be a unicode string, not %s
                ''',
                type_name(passphrase)
            ))
        if passphrase == '':
            raise ValueError(pretty_message(
                '''
                passphrase may not be a blank string - pass None to disable
                encryption
                '''
            ))

    is_oscrypto = isinstance(private_key, PrivateKey)
    if not isinstance(private_key, keys.PrivateKeyInfo) and not is_oscrypto:
        raise TypeError(pretty_message(
            '''
            private_key must be an instance of oscrypto.asymmetric.PrivateKey or
            asn1crypto.keys.PrivateKeyInfo, not %s
            ''',
            type_name(private_key)
        ))

    if is_oscrypto:
        private_key = private_key.asn1

    output = private_key.unwrap().dump()

    headers = None
    if passphrase is not None:
        iv = rand_bytes(16)

        headers = OrderedDict()
        headers['Proc-Type'] = '4,ENCRYPTED'
        headers['DEK-Info'] = 'AES-128-CBC,%s' % binascii.hexlify(iv).decode('ascii')

        key_length = 16
        passphrase_bytes = passphrase.encode('utf-8')

        key = hashlib.md5(passphrase_bytes + iv[0:8]).digest()
        while key_length > len(key):
            key += hashlib.md5(key + passphrase_bytes + iv[0:8]).digest()
        key = key[0:key_length]

        iv, output = aes_cbc_pkcs7_encrypt(key, output, iv)

    if private_key.algorithm == 'ec':
        object_type = 'EC PRIVATE KEY'
    elif private_key.algorithm == 'rsa':
        object_type = 'RSA PRIVATE KEY'
    elif private_key.algorithm == 'dsa':
        object_type = 'DSA PRIVATE KEY'

    return pem.armor(object_type, output, headers=headers)
예제 #38
0
 def to_pem(self):
     """Get the PEM-encoding of the certificate."""
     return pem.armor(self.PEM_MARKERS[0], self.to_der())
예제 #39
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
예제 #40
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
예제 #41
0
def dump_private_key(private_key, passphrase, encoding='pem', target_ms=200):
    """
    Serializes a private key object into a byte string of the PKCS#8 format

    :param private_key:
        An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
        object

    :param passphrase:
        A unicode string of the passphrase to encrypt the private key with.
        A passphrase of None will result in no encryption. A blank string will
        result in a ValueError to help ensure that the lack of passphrase is
        intentional.

    :param encoding:
        A unicode string of "pem" or "der"

    :param target_ms:
        Use PBKDF2 with the number of iterations that takes about this many
        milliseconds on the current machine.

    :raises:
        ValueError - when a blank string is provided for the passphrase

    :return:
        A byte string of the encoded and encrypted public key
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(
            pretty_message(
                '''
            encoding must be one of "pem", "der", not %s
            ''', repr(encoding)))

    if passphrase is not None:
        if not isinstance(passphrase, str_cls):
            raise TypeError(
                pretty_message(
                    '''
                passphrase must be a unicode string, not %s
                ''', type_name(passphrase)))
        if passphrase == '':
            raise ValueError(
                pretty_message('''
                passphrase may not be a blank string - pass None to disable
                encryption
                '''))

    is_oscrypto = isinstance(private_key, PrivateKey)
    if not isinstance(private_key, keys.PrivateKeyInfo) and not is_oscrypto:
        raise TypeError(
            pretty_message(
                '''
            private_key must be an instance of oscrypto.asymmetric.PrivateKey
            or asn1crypto.keys.PrivateKeyInfo, not %s
            ''', type_name(private_key)))

    if is_oscrypto:
        private_key = private_key.asn1

    output = private_key.dump()

    if passphrase is not None:
        cipher = 'aes256_cbc'
        key_length = 32
        kdf_hmac = 'sha256'
        kdf_salt = rand_bytes(key_length)
        iterations = pbkdf2_iteration_calculator(kdf_hmac,
                                                 key_length,
                                                 target_ms=target_ms,
                                                 quiet=True)
        # Need a bare minimum of 10,000 iterations for PBKDF2 as of 2015
        if iterations < 10000:
            iterations = 10000

        passphrase_bytes = passphrase.encode('utf-8')
        key = pbkdf2(kdf_hmac, passphrase_bytes, kdf_salt, iterations,
                     key_length)
        iv, ciphertext = aes_cbc_pkcs7_encrypt(key, output, None)

        output = keys.EncryptedPrivateKeyInfo({
            'encryption_algorithm': {
                'algorithm': 'pbes2',
                'parameters': {
                    'key_derivation_func': {
                        'algorithm': 'pbkdf2',
                        'parameters': {
                            'salt':
                            algos.Pbkdf2Salt(name='specified', value=kdf_salt),
                            'iteration_count':
                            iterations,
                            'prf': {
                                'algorithm': kdf_hmac,
                                'parameters': core.Null()
                            }
                        }
                    },
                    'encryption_scheme': {
                        'algorithm': cipher,
                        'parameters': iv
                    }
                }
            },
            'encrypted_data': ciphertext
        }).dump()

    if encoding == 'pem':
        if passphrase is None:
            object_type = 'PRIVATE KEY'
        else:
            object_type = 'ENCRYPTED PRIVATE KEY'
        output = pem.armor(object_type, output)

    return output
예제 #42
0
파일: tests.py 프로젝트: bbits/django-pki
 def public_key_pem(self, key):
     return pem.armor('PUBLIC KEY', asymmetric.dump_private_key(key, None, 'der'))
예제 #43
0
def dump_openssl_private_key(private_key, passphrase):
    """
    Serializes a private key object into a byte string of the PEM formats used
    by OpenSSL. The format chosen will depend on the type of private key - RSA,
    DSA or EC.

    Do not use this method unless you really must interact with a system that
    does not support PKCS#8 private keys. The encryption provided by PKCS#8 is
    far superior to the OpenSSL formats. This is due to the fact that the
    OpenSSL formats don't stretch the passphrase, making it very easy to
    brute-force.

    :param private_key:
        An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
        object

    :param passphrase:
        A unicode string of the passphrase to encrypt the private key with.
        A passphrase of None will result in no encryption. A blank string will
        result in a ValueError to help ensure that the lack of passphrase is
        intentional.

    :raises:
        ValueError - when a blank string is provided for the passphrase

    :return:
        A byte string of the encoded and encrypted public key
    """

    if passphrase is not None:
        if not isinstance(passphrase, str_cls):
            raise TypeError(
                pretty_message(
                    '''
                passphrase must be a unicode string, not %s
                ''', type_name(passphrase)))
        if passphrase == '':
            raise ValueError(
                pretty_message('''
                passphrase may not be a blank string - pass None to disable
                encryption
                '''))

    is_oscrypto = isinstance(private_key, PrivateKey)
    if not isinstance(private_key, keys.PrivateKeyInfo) and not is_oscrypto:
        raise TypeError(
            pretty_message(
                '''
            private_key must be an instance of oscrypto.asymmetric.PrivateKey or
            asn1crypto.keys.PrivateKeyInfo, not %s
            ''', type_name(private_key)))

    if is_oscrypto:
        private_key = private_key.asn1

    output = _unwrap_private_key_info(private_key).dump()

    headers = None
    if passphrase is not None:
        iv = rand_bytes(16)

        headers = OrderedDict()
        headers['Proc-Type'] = '4,ENCRYPTED'
        headers['DEK-Info'] = 'AES-128-CBC,%s' % binascii.hexlify(iv).decode(
            'ascii')

        key_length = 16
        passphrase_bytes = passphrase.encode('utf-8')

        key = hashlib.md5(passphrase_bytes + iv[0:8]).digest()
        while key_length > len(key):
            key += hashlib.md5(key + passphrase_bytes + iv[0:8]).digest()
        key = key[0:key_length]

        iv, output = aes_cbc_pkcs7_encrypt(key, output, iv)

    if private_key.algorithm == 'ec':
        object_type = 'EC PRIVATE KEY'
    elif private_key.algorithm == 'rsa':
        object_type = 'RSA PRIVATE KEY'
    elif private_key.algorithm == 'dsa':
        object_type = 'DSA PRIVATE KEY'

    return pem.armor(object_type, output, headers=headers)
예제 #44
0
def dump_private_key(private_key, passphrase, encoding='pem', target_ms=200):
    """
    Serializes a private key object into a byte string of the PKCS#8 format

    :param private_key:
        An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
        object

    :param passphrase:
        A unicode string of the passphrase to encrypt the private key with.
        A passphrase of None will result in no encryption. A blank string will
        result in a ValueError to help ensure that the lack of passphrase is
        intentional.

    :param encoding:
        A unicode string of "pem" or "der"

    :param target_ms:
        Use PBKDF2 with the number of iterations that takes about this many
        milliseconds on the current machine.

    :raises:
        ValueError - when a blank string is provided for the passphrase

    :return:
        A byte string of the encoded and encrypted public key
    """

    if encoding not in set(['pem', 'der']):
        raise ValueError(pretty_message(
            '''
            encoding must be one of "pem", "der", not %s
            ''',
            repr(encoding)
        ))

    if passphrase is not None:
        if not isinstance(passphrase, str_cls):
            raise TypeError(pretty_message(
                '''
                passphrase must be a unicode string, not %s
                ''',
                type_name(passphrase)
            ))
        if passphrase == '':
            raise ValueError(pretty_message(
                '''
                passphrase may not be a blank string - pass None to disable
                encryption
                '''
            ))

    is_oscrypto = isinstance(private_key, PrivateKey)
    if not isinstance(private_key, keys.PrivateKeyInfo) and not is_oscrypto:
        raise TypeError(pretty_message(
            '''
            private_key must be an instance of oscrypto.asymmetric.PrivateKey
            or asn1crypto.keys.PrivateKeyInfo, not %s
            ''',
            type_name(private_key)
        ))

    if is_oscrypto:
        private_key = private_key.asn1

    output = private_key.dump()

    if passphrase is not None:
        cipher = 'aes256_cbc'
        key_length = 32
        kdf_hmac = 'sha256'
        kdf_salt = rand_bytes(key_length)
        iterations = pbkdf2_iteration_calculator(kdf_hmac, key_length, target_ms=target_ms, quiet=True)
        # Need a bare minimum of 10,000 iterations for PBKDF2 as of 2015
        if iterations < 10000:
            iterations = 10000

        passphrase_bytes = passphrase.encode('utf-8')
        key = pbkdf2(kdf_hmac, passphrase_bytes, kdf_salt, iterations, key_length)
        iv, ciphertext = aes_cbc_pkcs7_encrypt(key, output, None)

        output = keys.EncryptedPrivateKeyInfo({
            'encryption_algorithm': {
                'algorithm': 'pbes2',
                'parameters': {
                    'key_derivation_func': {
                        'algorithm': 'pbkdf2',
                        'parameters': {
                            'salt': algos.Pbkdf2Salt(
                                name='specified',
                                value=kdf_salt
                            ),
                            'iteration_count': iterations,
                            'prf': {
                                'algorithm': kdf_hmac,
                                'parameters': core.Null()
                            }
                        }
                    },
                    'encryption_scheme': {
                        'algorithm': cipher,
                        'parameters': iv
                    }
                }
            },
            'encrypted_data': ciphertext
        }).dump()

    if encoding == 'pem':
        if passphrase is None:
            object_type = 'PRIVATE KEY'
        else:
            object_type = 'ENCRYPTED PRIVATE KEY'
        output = pem.armor(object_type, output)

    return output