def makecert(reqdir, subject, principal): """ Generate a certificate that can be used during unit testing. """ ra = rabase.rabase(api) if (not os.path.exists(ra.sec_dir) and api.env.xmlrpc_uri == 'http://localhost:8888/ipa/xml'): raise AssertionError('The self-signed CA is not configured, ' 'see ipatests/test_xmlrpc/test_cert.py') pwname = os.path.join(reqdir, "pwd") # Create an empty password file with open(pwname, "w") as fp: fp.write("\n") # Generate NSS cert database to store the private key for our CSR run_certutil(reqdir, ["-N", "-f", pwname]) res = api.Command['config_show']() subject_base = res['result']['ipacertificatesubjectbase'][0] cert = None csr = unicode(generate_csr(reqdir, pwname, str(subject))) res = api.Command['cert_request'](csr, principal=principal, add=True) return x509.make_pem(res['result']['certificate'])
def makecert(reqdir): """ Generate a service certificate that can be used during unit testing. """ ra = rabase.rabase() if (not os.path.exists(ra.sec_dir) and api.env.xmlrpc_uri == 'http://*****:*****@%s' % (api.env.host, api.env.realm) csr = unicode(generate_csr(reqdir, pwname, str(subject))) res = api.Command['cert_request'](csr, principal=princ, add=True) return x509.make_pem(res['result']['certificate'])
def list_users_by_cert(self, cert): """ Look for users matching the cert. Call Users.ListByCertificate interface and return a dict with key = domain, value = list of uids corresponding to the users matching the provided cert :param cert: DER cert :raise RemoteRetrieveError: if DBus error occurs """ try: pem = x509.make_pem(base64.b64encode(cert)) # bug 3306 in sssd returns 0 entry when max_entries = 0 # Temp workaround is to use a non-null value, not too high # to avoid reserving unneeded memory max_entries = dbus.UInt32(100) user_paths = self._users_iface.ListByCertificate(pem, max_entries) users = dict() for user_path in user_paths: user_obj = self._bus.get_object(DBUS_SSSD_NAME, user_path) user_iface = dbus.Interface(user_obj, DBUS_PROPERTY_IF) user_login = user_iface.Get(DBUS_SSSD_USER_IF, 'name') # Extract name@domain items = user_login.split('@') domain = api.env.realm if len(items) < 2 else items[1] name = items[0] # Retrieve the list of users for the given domain, # or initialize to an empty list # and add the name users_for_dom = users.setdefault(domain, list()) users_for_dom.append(name) return users except dbus.DBusException as e: err_name = e.get_dbus_name() # If there is no matching user, do not consider this as an # exception and return an empty list if err_name == 'org.freedesktop.sssd.Error.NotFound': return dict() logger.error( 'Failed to use interface %s. DBus ' 'exception is %s.', DBUS_SSSD_USERS_IF, e) raise errors.RemoteRetrieveError( reason=_('Failed to find users over SystemBus. ' ' See details in the error_log'))
def forward(self, *args, **options): certificate_out = options.pop('certificate_out', None) if certificate_out is not None: util.check_writable_file(certificate_out) result = super(CertRetrieveOverride, self).forward(*args, **options) if certificate_out is not None: if options.get('chain', False): certs = result['result']['certificate_chain'] else: certs = [result['result']['certificate']] certs = (x509.normalize_certificate(cert) for cert in certs) certs = (x509.make_pem(base64.b64encode(cert)) for cert in certs) with open(certificate_out, 'w') as f: f.write('\n'.join(certs)) return result
def forward(self, *args, **options): if 'certificate_out' in options: certificate_out = options.pop('certificate_out') try: util.check_writable_file(certificate_out) except errors.FileError as e: raise errors.ValidationError(name='certificate-out', error=str(e)) else: certificate_out = None result = super(CertRetrieveOverride, self).forward(*args, **options) if certificate_out is not None: if options.get('chain', False): certs = result['result']['certificate_chain'] else: certs = [result['result']['certificate']] certs = (x509.normalize_certificate(cert) for cert in certs) certs = (x509.make_pem(base64.b64encode(cert)) for cert in certs) with open(certificate_out, 'w') as f: f.write('\n'.join(certs)) return result
def insert_ca_certs_into_systemwide_ca_store(self, ca_certs): new_cacert_path = paths.SYSTEMWIDE_IPA_CA_CRT if os.path.exists(new_cacert_path): try: os.remove(new_cacert_path) except OSError as e: root_logger.error( "Could not remove %s: %s", new_cacert_path, e) return False new_cacert_path = paths.IPA_P11_KIT try: f = open(new_cacert_path, 'w') except IOError as e: root_logger.info("Failed to open %s: %s" % (new_cacert_path, e)) return False f.write("# This file was created by IPA. Do not edit.\n" "\n") has_eku = set() for cert, nickname, trusted, ext_key_usage in ca_certs: try: subject = x509.get_der_subject(cert, x509.DER) issuer = x509.get_der_issuer(cert, x509.DER) serial_number = x509.get_der_serial_number(cert, x509.DER) public_key_info = x509.get_der_public_key_info(cert, x509.DER) except (NSPRError, PyAsn1Error, ValueError) as e: root_logger.warning( "Failed to decode certificate \"%s\": %s", nickname, e) continue label = urllib.parse.quote(nickname) subject = urllib.parse.quote(subject) issuer = urllib.parse.quote(issuer) serial_number = urllib.parse.quote(serial_number) public_key_info = urllib.parse.quote(public_key_info) cert = base64.b64encode(cert) cert = x509.make_pem(cert) obj = ("[p11-kit-object-v1]\n" "class: certificate\n" "certificate-type: x-509\n" "certificate-category: authority\n" "label: \"%(label)s\"\n" "subject: \"%(subject)s\"\n" "issuer: \"%(issuer)s\"\n" "serial-number: \"%(serial_number)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" % dict(label=label, subject=subject, issuer=issuer, serial_number=serial_number, public_key_info=public_key_info)) if trusted is True: obj += "trusted: true\n" elif trusted is False: obj += "x-distrusted: true\n" obj += "%s\n\n" % cert f.write(obj) if ext_key_usage is not None and public_key_info not in has_eku: if not ext_key_usage: ext_key_usage = {x509.EKU_PLACEHOLDER} try: ext_key_usage = x509.encode_ext_key_usage(ext_key_usage) except PyAsn1Error as e: root_logger.warning( "Failed to encode extended key usage for \"%s\": %s", nickname, e) continue value = urllib.parse.quote(ext_key_usage) obj = ("[p11-kit-object-v1]\n" "class: x-certificate-extension\n" "label: \"ExtendedKeyUsage for %(label)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" "object-id: 2.5.29.37\n" "value: \"%(value)s\"\n\n" % dict(label=label, public_key_info=public_key_info, value=value)) f.write(obj) has_eku.add(public_key_info) f.close() # Add the CA to the systemwide CA trust database if not self.reload_systemwide_ca_store(): return False return True
def insert_ca_certs_into_systemwide_ca_store(self, ca_certs): # pylint: disable=ipa-forbidden-import from ipalib import x509 # FixMe: break import cycle from ipalib.errors import CertificateError # pylint: enable=ipa-forbidden-import new_cacert_path = paths.SYSTEMWIDE_IPA_CA_CRT if os.path.exists(new_cacert_path): try: os.remove(new_cacert_path) except OSError as e: root_logger.error("Could not remove %s: %s", new_cacert_path, e) return False new_cacert_path = paths.IPA_P11_KIT try: f = open(new_cacert_path, 'w') except IOError as e: root_logger.info("Failed to open %s: %s" % (new_cacert_path, e)) return False f.write("# This file was created by IPA. Do not edit.\n" "\n") has_eku = set() for cert, nickname, trusted, ext_key_usage in ca_certs: try: subject = x509.get_der_subject(cert, x509.DER) issuer = x509.get_der_issuer(cert, x509.DER) serial_number = x509.get_der_serial_number(cert, x509.DER) public_key_info = x509.get_der_public_key_info(cert, x509.DER) except (PyAsn1Error, ValueError, CertificateError) as e: root_logger.warning("Failed to decode certificate \"%s\": %s", nickname, e) continue label = urllib.parse.quote(nickname) subject = urllib.parse.quote(subject) issuer = urllib.parse.quote(issuer) serial_number = urllib.parse.quote(serial_number) public_key_info = urllib.parse.quote(public_key_info) cert = base64.b64encode(cert) cert = x509.make_pem(cert) obj = ("[p11-kit-object-v1]\n" "class: certificate\n" "certificate-type: x-509\n" "certificate-category: authority\n" "label: \"%(label)s\"\n" "subject: \"%(subject)s\"\n" "issuer: \"%(issuer)s\"\n" "serial-number: \"%(serial_number)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" % dict(label=label, subject=subject, issuer=issuer, serial_number=serial_number, public_key_info=public_key_info)) if trusted is True: obj += "trusted: true\n" elif trusted is False: obj += "x-distrusted: true\n" obj += "%s\n\n" % cert f.write(obj) if ext_key_usage is not None and public_key_info not in has_eku: if not ext_key_usage: ext_key_usage = {x509.EKU_PLACEHOLDER} try: ext_key_usage = x509.encode_ext_key_usage(ext_key_usage) except PyAsn1Error as e: root_logger.warning( "Failed to encode extended key usage for \"%s\": %s", nickname, e) continue value = urllib.parse.quote(ext_key_usage) obj = ("[p11-kit-object-v1]\n" "class: x-certificate-extension\n" "label: \"ExtendedKeyUsage for %(label)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" "object-id: 2.5.29.37\n" "value: \"%(value)s\"\n\n" % dict(label=label, public_key_info=public_key_info, value=value)) f.write(obj) has_eku.add(public_key_info) f.close() # Add the CA to the systemwide CA trust database if not self.reload_systemwide_ca_store(): return False return True
def import_files(self, files, db_password_filename, import_keys=False, key_password=None, key_nickname=None): """ Import certificates and a single private key from multiple files The files may be in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. :param files: Names of files to import :param db_password_filename: Name of file containing the database password :param import_keys: Whether to import private keys :param key_password: Password to decrypt private keys :param key_nickname: Nickname of the private key to import from PKCS#12 files """ key_file = None extracted_key = None extracted_certs = '' for filename in files: try: with open(filename, 'rb') as f: data = f.read() except IOError as e: raise RuntimeError( "Failed to open %s: %s" % (filename, e.strerror)) # Try to parse the file as PEM file matches = list(re.finditer( r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL)) if matches: loaded = False for match in matches: body = match.group() label = match.group(1) line = len(data[:match.start() + 1].splitlines()) if label in ('CERTIFICATE', 'X509 CERTIFICATE', 'X.509 CERTIFICATE'): try: x509.load_certificate(match.group(2)) except NSPRError as e: if label != 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) continue else: extracted_certs += body + '\n' loaded = True continue if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'): args = [ paths.OPENSSL, 'pkcs7', '-print_certs', ] try: result = ipautil.run( args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: if label == 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) else: root_logger.warning( "Skipping PKCS#7 in %s at line %s: %s", filename, line, e) continue else: extracted_certs += result.output + '\n' loaded = True continue if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY', 'RSA PRIVATE KEY', 'DSA PRIVATE KEY', 'EC PRIVATE KEY'): if not import_keys: continue if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) args = [ paths.OPENSSL, 'pkcs8', '-topk8', '-passout', 'file:' + db_password_filename, ] if ((label != 'PRIVATE KEY' and key_password) or label == 'ENCRYPTED PRIVATE KEY'): key_pwdfile = ipautil.write_tmp_file(key_password) args += [ '-passin', 'file:' + key_pwdfile.name, ] try: result = ipautil.run( args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: root_logger.warning( "Skipping private key in %s at line %s: %s", filename, line, e) continue else: extracted_key = result.output key_file = filename loaded = True continue if loaded: continue raise RuntimeError("Failed to load %s" % filename) # Try to load the file as DER certificate try: x509.load_certificate(data, x509.DER) except NSPRError: pass else: data = x509.make_pem(base64.b64encode(data)) extracted_certs += data + '\n' continue # Try to import the file as PKCS#12 file if import_keys: try: self.import_pkcs12( filename, db_password_filename, key_password) except RuntimeError: pass else: if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) key_file = filename server_certs = self.find_server_certs() if key_nickname: for nickname, trust_flags in server_certs: if nickname == key_nickname: break else: raise RuntimeError( "Server certificate \"%s\" not found in %s" % (key_nickname, filename)) else: if len(server_certs) > 1: raise RuntimeError( "%s server certificates found in %s, " "expecting only one" % (len(server_certs), filename)) continue raise RuntimeError("Failed to load %s" % filename) if import_keys and not key_file: raise RuntimeError( "No server certificates found in %s" % (', '.join(files))) nss_certs = x509.load_certificate_list(extracted_certs) nss_cert = None for nss_cert in nss_certs: nickname = str(nss_cert.subject) self.add_cert(nss_cert.der_data, nickname, ',,') del nss_certs, nss_cert if extracted_key: in_file = ipautil.write_tmp_file(extracted_certs + extracted_key) out_file = tempfile.NamedTemporaryFile() out_password = ipautil.ipa_generate_password() out_pwdfile = ipautil.write_tmp_file(out_password) args = [ paths.OPENSSL, 'pkcs12', '-export', '-in', in_file.name, '-out', out_file.name, '-passin', 'file:' + db_password_filename, '-passout', 'file:' + out_pwdfile.name, ] try: ipautil.run(args) except ipautil.CalledProcessError as e: raise RuntimeError( "No matching certificate found for private key from %s" % key_file) self.import_pkcs12(out_file.name, db_password_filename, out_password)
def to_pem(x): return x509.make_pem(x)
issuer = x509.get_der_issuer(cert, x509.DER) serial_number = x509.get_der_serial_number(cert, x509.DER) public_key_info = x509.get_der_public_key_info(cert, x509.DER) except (NSPRError, PyAsn1Error), e: root_logger.warning( "Failed to decode certificate \"%s\": %s", nickname, e) continue label = urllib.quote(nickname) subject = urllib.quote(subject) issuer = urllib.quote(issuer) serial_number = urllib.quote(serial_number) public_key_info = urllib.quote(public_key_info) cert = base64.b64encode(cert) cert = x509.make_pem(cert) obj = ("[p11-kit-object-v1]\n" "class: certificate\n" "certificate-type: x-509\n" "certificate-category: authority\n" "label: \"%(label)s\"\n" "subject: \"%(subject)s\"\n" "issuer: \"%(issuer)s\"\n" "serial-number: \"%(serial_number)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" % dict(label=label, subject=subject, issuer=issuer, serial_number=serial_number, public_key_info=public_key_info))
issuer = x509.get_der_issuer(cert, x509.DER) serial_number = x509.get_der_serial_number(cert, x509.DER) public_key_info = x509.get_der_public_key_info(cert, x509.DER) except (NSPRError, PyAsn1Error), e: root_logger.warning("Failed to decode certificate \"%s\": %s", nickname, e) continue label = urllib.quote(nickname) subject = urllib.quote(subject) issuer = urllib.quote(issuer) serial_number = urllib.quote(serial_number) public_key_info = urllib.quote(public_key_info) cert = base64.b64encode(cert) cert = x509.make_pem(cert) obj = ("[p11-kit-object-v1]\n" "class: certificate\n" "certificate-type: x-509\n" "certificate-category: authority\n" "label: \"%(label)s\"\n" "subject: \"%(subject)s\"\n" "issuer: \"%(issuer)s\"\n" "serial-number: \"%(serial_number)s\"\n" "x-public-key-info: \"%(public_key_info)s\"\n" % dict(label=label, subject=subject, issuer=issuer, serial_number=serial_number, public_key_info=public_key_info))
def import_files(self, files, db_password_filename, import_keys=False, key_password=None, key_nickname=None): """ Import certificates and a single private key from multiple files The files may be in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. :param files: Names of files to import :param db_password_filename: Name of file containing the database password :param import_keys: Whether to import private keys :param key_password: Password to decrypt private keys :param key_nickname: Nickname of the private key to import from PKCS#12 files """ key_file = None extracted_key = None extracted_certs = '' for filename in files: try: with open(filename, 'rb') as f: data = f.read() except IOError as e: raise RuntimeError("Failed to open %s: %s" % (filename, e.strerror)) # Try to parse the file as PEM file matches = list( re.finditer(r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL)) if matches: loaded = False for match in matches: body = match.group() label = match.group(1) line = len(data[:match.start() + 1].splitlines()) if label in ('CERTIFICATE', 'X509 CERTIFICATE', 'X.509 CERTIFICATE'): try: x509.load_certificate(match.group(2)) except ValueError as e: if label != 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) continue else: extracted_certs += body + '\n' loaded = True continue if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'): args = [ paths.OPENSSL, 'pkcs7', '-print_certs', ] try: result = ipautil.run(args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: if label == 'CERTIFICATE': root_logger.warning( "Skipping certificate in %s at line %s: %s", filename, line, e) else: root_logger.warning( "Skipping PKCS#7 in %s at line %s: %s", filename, line, e) continue else: extracted_certs += result.output + '\n' loaded = True continue if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY', 'RSA PRIVATE KEY', 'DSA PRIVATE KEY', 'EC PRIVATE KEY'): if not import_keys: continue if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) args = [ paths.OPENSSL, 'pkcs8', '-topk8', '-passout', 'file:' + db_password_filename, ] if ((label != 'PRIVATE KEY' and key_password) or label == 'ENCRYPTED PRIVATE KEY'): key_pwdfile = ipautil.write_tmp_file(key_password) args += [ '-passin', 'file:' + key_pwdfile.name, ] try: result = ipautil.run(args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: root_logger.warning( "Skipping private key in %s at line %s: %s", filename, line, e) continue else: extracted_key = result.output key_file = filename loaded = True continue if loaded: continue raise RuntimeError("Failed to load %s" % filename) # Try to load the file as DER certificate try: x509.load_certificate(data, x509.DER) except ValueError: pass else: data = x509.make_pem(base64.b64encode(data)) extracted_certs += data + '\n' continue # Try to import the file as PKCS#12 file if import_keys: try: self.import_pkcs12(filename, db_password_filename, key_password) except RuntimeError: pass else: if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) key_file = filename server_certs = self.find_server_certs() if key_nickname: for nickname, _trust_flags in server_certs: if nickname == key_nickname: break else: raise RuntimeError( "Server certificate \"%s\" not found in %s" % (key_nickname, filename)) else: if len(server_certs) > 1: raise RuntimeError( "%s server certificates found in %s, " "expecting only one" % (len(server_certs), filename)) continue raise RuntimeError("Failed to load %s" % filename) if import_keys and not key_file: raise RuntimeError("No server certificates found in %s" % (', '.join(files))) certs = x509.load_certificate_list(extracted_certs) for cert in certs: nickname = str(DN(cert.subject)) data = cert.public_bytes(serialization.Encoding.DER) self.add_cert(data, nickname, ',,') if extracted_key: in_file = ipautil.write_tmp_file(extracted_certs + extracted_key) out_file = tempfile.NamedTemporaryFile() out_password = ipautil.ipa_generate_password() out_pwdfile = ipautil.write_tmp_file(out_password) args = [ paths.OPENSSL, 'pkcs12', '-export', '-in', in_file.name, '-out', out_file.name, '-passin', 'file:' + db_password_filename, '-passout', 'file:' + out_pwdfile.name, ] try: ipautil.run(args) except ipautil.CalledProcessError as e: raise RuntimeError( "No matching certificate found for private key from %s" % key_file) self.import_pkcs12(out_file.name, db_password_filename, out_password)