1. Localización de los ficheros de certificados
En Linux-Mozilla los certificados se guardan en la carpeta hija de:
~/.mozilla/firefox
que contiene entre otros el fichero "cert9.db"
Por tanto hay que buscar las carpetas contenedoras de dicho fichero (puede haber varias)
En Linux-Chrome se guardan en la carpeta:
~/.pki/nssdb
2. Como se hace en Linux bash?
Para manejar los certificados contenidos en cada carpeta hay que instalar algunas librerías del paquete NSS (Network Security Services).
sudo apt install libnss3-tools
certutil -L -d sql:$HOME/.mozilla/firefox/XXXX
certutil -L -d sql:$HOME/.pki/nsb
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
Mi nick u,u,u
ACME Root CA CT,C,C
Ahora podemos extraer los certificados a partir de la carpeta y el Nickname. En el lisado anterior vemos un nickname llamado "Mi nick". Para obtener este ceerificamos SIN CLAVE PRIVADA hacemos:
certutil -L -n "Mi nick" -d sql:$HOME/.pki/nsb -a > cert.pem
# Guardamos la conraseña en un fichero echo "mypasword" > temp_pass_file_path #Guardamos el certificado con la clave privada, protegidos por la contraseña "mypassword" pk12util -o, cert.pem -d sql:$HOME/.pki/nsb -n "My nick" -k $HOME/.pki/nsb/key4.db -w temp_pass_file_path
#Cambiamos el nombre y nos pedirá la nueva contraseña
openssl rsa -in cert.pem -out nuevo_certificado.pem -aes256
#Comprobamos que podemos leer el fichero con la nueva contraseña
openssl rsa -in nuevo_certificado.pem
3. Como se hace en Windows powershell?
Ejecutamos este programa de powershell para extrar todos los certificados a formato pfx y los protegemos con la contraseña "password_pfx"
# Ruta donde se guardarán los archivos PFX $outputFolder = "C:\ruta\certificados" # Crear la carpeta si no existe if (-not (Test-Path -Path $outputFolder)) { New-Item -ItemType Directory -Path $outputFolder } # Iterar sobre todos los certificados que se listan con "Get-ChildItem Cert:\CurrentUser\My" $certificates = Get-ChildItem Cert:\CurrentUser\My foreach ($cert in $certificates) { $filePath = Join-Path -Path $outputFolder -ChildPath ("cert_" + $cert.Thumbprint + ".pfx") $password = ConvertTo-SecureString -String "password_pfx" -Force -AsPlainText Export-PfxCertificate -Cert $cert -FilePath $filePath -Password $password Write-Host "Exportado: $filePath" }
Y para convertir de pfx a pem utilizamos el power shell, manteniendo la misma contraseña:
# Ruta donde están los archivos PFX $pfxFolder = "C:\ruta\certificados" # Ruta donde se guardarán los archivos PEM $pemFolder = "C:\ruta\certificados_pem" # Crear la carpeta para los PEM si no existe if (-not (Test-Path -Path $pemFolder)) { New-Item -ItemType Directory -Path $pemFolder } # Convertir cada PFX a PEM $pfxFiles = Get-ChildItem -Path $pfxFolder -Filter "*.pfx" foreach ($pfx in $pfxFiles) { $pemFilePath = Join-Path -Path $pemFolder -ChildPath ($pfx.BaseName + ".pem") # Ejecutar OpenSSL para la conversión & "openssl" pkcs12 -in $pfx.FullName -out $pemFilePath -nodes -password pass:password_pfx Write-Host "Convertido a PEM: $pemFilePath" }
4. En Python
Solo se ha comprobado en Linux.
Veamos el código python
import os import platform import subprocess from cryptography.hazmat.primitives.serialization import Encoding, pkcs12, NoEncryption from cryptography.x509 import load_der_x509_certificate, load_pem_x509_certificate from cryptography import x509 from Crypto.IO import PEM from Crypto.PublicKey import RSA from OpenSSL.crypto import PKCS12, FILETYPE_PEM, dump_certificate, dump_privatekey import base64 import ssl import tempfile # ------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path for i in range(2):sys.path.append(str(Path(__file__).parents[i])) try: # if in the same folder then needs a try and import directly from basicutils import xmfiles except Exception as error: import xmfiles # ------Fin imprescindible #def save_certificate_to_file(cert, filename): # """Save a certificate to a file in PEM format.""" # try: # with open(filename, "wb") as f: # f.write(cert.public_bytes(Encoding.PEM)) # print(f"Certificate saved to {filename}") # except Exception as e: # print(f"Error saving certificate to file: {e}") def change_password(input_pem, input_password, output_pem, output_password): ''' Cambia la contraseña de un archivo PEM''' try: # Leer contenido del archivo PEM with open(input_pem, "rb") as file: pem_data = file.read() # Desencriptar la clave privada usando la contraseña existente key, _ = PEM.decode(pem_data, passphrase=input_password.encode("utf-8")) # Cargar la clave como objeto RSA rsa_key = RSA.import_key(key) # Re-encriptar la clave privada con la nueva contraseña encrypted_key = rsa_key.export_key(format="PEM", passphrase=output_password, pkcs=8) # Escribir el nuevo archivo PEM con la nueva contraseña with open(output_pem, "wb") as file: file.write(encrypted_key) print(f"Archivo PEM guardado como '{output_pem}' con la nueva contraseña.") except Exception as e: print(f"Error: {e}") def parse_certificate(browser:str, cert_data)->dict: """Parse a certificate into a dict and print details of an X.509 certificate.""" try: cert = load_der_x509_certificate(cert_data) except ValueError: try: cert = load_pem_x509_certificate(cert_data) except Exception as e: print(f"Failed to parse certificate: {e}") return None try: #print("\nCertificate Details:"); #print(f" Subject: {cert.subject}") # Obtener el CN subject = cert.subject cn = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value print(f" CN: {cn}") browseCN=browser+"-"+cn #print(f" Issuer: {cert.issuer}") ; #print(f" Serial Number: {cert.serial_number}") #print(f" Valid From: {cert.not_valid_before}"); #print(f" Valid To: {cert.not_valid_after}") #print(f" Signature Algorithm: {cert.signature_algorithm_oid}") except Exception as e: print(f"Failed to print certificate details: {e}") return {browseCN:cert} if cn else {'No CN':cert} def export_certificate_and_key_linux(nssDir:str, output_path:str, nickname:str, pkcs12_password:str,exportKey:bool=False): ''' Exports the certificate and key in a file "pem" and also into 2 diferent files, one for the cert (.crt) and another for te key (.key) if exportKey is False, the files ".crt" and ".key" are not created Parameters: nssDir (str): The path to the NSS database. output_path (str): The path where the files will be saved. nickname (str): The nickname of the certificate. pkcs12_password (str): The password for the PKCS#12 file. exportKey (bool): Whether to create crt and key files. ''' try: # Step 1: Define paths #nss_dir = os.path.expanduser("~/.pki/nssdb") if not os.path.exists(nssDir): raise FileNotFoundError(f"NSS database not found in {nssDir}. Ensure Chrome has certificates stored there.") # Step 2: Write password to a temporary file with tempfile.NamedTemporaryFile(delete=False, mode="w", prefix="pkcs12_", suffix=".pass") as temp_pass_file: temp_pass_file.write(pkcs12_password) temp_pass_file_path = temp_pass_file.name # Step 3: Export the certificate and key as a PKCS#12 file pkcs12_file = os.path.join(output_path, f"{nickname}.p12") pk12util_cmd = [ "pk12util", "-o", pkcs12_file, "-d", f"sql:{nssDir}", "-n", nickname, "-k", f"{nssDir}/key4.db", "-w", temp_pass_file_path ] print("Exporting PKCS#12 file...") subprocess.run(pk12util_cmd, check=True) print(f"PKCS#12 file exported to: {pkcs12_file}") if exportKey: # Step 4: Convert PKCS#12 to separate certificate and private key cert_file = os.path.join(output_path, f"{nickname}.crt") key_file = os.path.join(output_path, f"{nickname}.key") openssl_cmd = [ "openssl", "pkcs12", "-in", pkcs12_file, "-clcerts", "-nokeys", "-out", cert_file, "-nodes", "-password", f"pass:{pkcs12_password}" ] print("Extracting certificate...") subprocess.run(openssl_cmd, check=True) print(f"Certificate exported to: {cert_file}") openssl_key_cmd = [ "openssl", "pkcs12", "-in", pkcs12_file, "-nocerts", "-out", key_file, "-nodes", "-password", f"pass:{pkcs12_password}" ] print("Extracting private key...") subprocess.run(openssl_key_cmd, check=True) print(f"Private key exported to: {key_file}") except subprocess.CalledProcessError as e: print(f"Error executing command: {e}") except FileNotFoundError as e: print(e) except Exception as e: print(f"An unexpected error occurred: {e}") def export_user_certs_windows(output_path, password="my-password", export_format="pem"): """ Busca un certificado por nombre en el almacén de Windows, extrae su clave privada y lo guarda como PEM o PFX. Args: name (str): Nombre del certificado a buscar. output_path (str): Ruta donde se guardará el archivo exportado. password (str, optional): Contraseña para proteger el archivo PFX. export_format (str): Formato del archivo de salida ('pem' o 'pfx'). """ import wincertstore try: with wincertstore.CertSystemStore("MY") as store: for cert in store.itercerts(usage=wincertstore.CERT_FIND_ANY): # Extraer el certificado y la clave privada private_key = cert.get_key() certificate = cert.get_cert() # Verificar si la clave privada está disponible if not private_key: print("Advertencia: Clave privada no disponible o no exportable.") return if export_format == "pem": # Exportar como PEM cert_pem = dump_certificate(FILETYPE_PEM, certificate) key_pem = dump_privatekey(FILETYPE_PEM, private_key) with open(output_path, "wb") as f: f.write(key_pem) f.write(cert_pem) print(f"Certificado exportado como PEM: {output_path}") elif export_format == "pfx": # Exportar como PFX pfx = PKCS12() pfx.set_privatekey(private_key) pfx.set_certificate(certificate) pfx_data = pfx.export(password.encode() if password else None) with open(output_path, "wb") as f: f.write(pfx_data) print(f"Certificado exportado como PFX: {output_path}") else: print("Formato no soportado. Usa 'pem' o 'pfx'.") except Exception as e: print(f"Error: {e}") def list_user_certificates_linux(): """List user-installed certificates on Linux and display their details.""" certs = [] certificates=[] allCertsDict={} home_dir = os.path.expanduser("~") # Firefox Certificates firefox_path = os.path.join(home_dir, ".mozilla/firefox") if os.path.exists(firefox_path): #profiles = [d for d in os.listdir(firefox_path) if d.endswith(".default")] #for profile in profiles: # cert_db = os.path.join(firefox_path, profile, "cert9.db") # if os.path.exists(cert_db): # certs.append(("Firefox", cert_db)) certDBs=xmfiles.findFileInFolderRecursive('cert9.db', firefox_path) for i, certDB in enumerate(certDBs): certs.append(("Mz-"+str(i), certDB[1])) # Chrome Certificates chrome_path = os.path.join(home_dir, ".pki/nssdb") if os.path.exists(chrome_path): certs.append(("Ch", chrome_path)) # Display certificate details using `certutil` for browser, cert_db in certs: print(f"\n{browser} Certificates from {cert_db}:") try: output = subprocess.check_output(["certutil", "-L", "-d", f"sql:{cert_db}"], text=True) print(output) #================================================================ # Extract certificate nicknames and details lines = output.splitlines()[2:] # Skip the header lines for line in lines: try: #nickname = line.split()[0] nickname = line.split(' ')[0] print(f"Nickname: {nickname} Browser: {browser}") # Export each certificate in PEM format cert_output = subprocess.check_output( ["certutil", "-L", "-n", nickname, "-d", f"sql:{cert_db}", "-a"], text=True ) certDict=parse_certificate(browser,[cert_output.encode(), nickname, cert_db ]) allCertsDict.update(certDict) #add to all except Exception as e: print(f"Failed to export certificate '{line}'") except Exception as e: print(f"Error reading {browser} certificates: {e}") #return certs return allCertsDict def list_user_certificates_mac(): """List user-installed certificates on macOS and display their details.""" user_certs = [] allCertsDict={} try: output = subprocess.check_output( ["security", "find-certificate", "-c", "User", "-a", "-p"], text=True ) certs = output.split("-----END CERTIFICATE-----") for cert in certs: if cert.strip(): pem_cert = cert + "-----END CERTIFICATE-----" print("\nCertificate PEM Format:") print(pem_cert) user_certs.append(pem_cert) certDict=parse_certificate(pem_cert.encode()) allCertsDict.update(certDict) except Exception as e: print(f"Error accessing user certificates on macOS: {e}") #return user_certs return allCertsDict def main(): print("Listing all user-installed certificates...\n") user_certs = [] if platform.system() == "Linux": # Linux home_dir = os.path.expanduser("~") chrome_path = os.path.join(home_dir, ".pki/nssdb") #export_certificate_and_key_linux(chrome_path, "/home/edu/MyCerts/Prova", "EPN1", "my-password") print("User Certificates (Linux):") user_certs = list_user_certificates_linux() print("===================================================================") for k,v in user_certs.items(): print(k) print(" ",v) export_certificate_and_key_linux(nssDir=v[2], output_path="/home/edu/MyCerts/Prova", nickname=v[1],pkcs12_password="my-password", exportKey=False ) print("-------------------------------") elif platform.system() == "Windows": # Windows print("User Certificates (Windows):") export_user_certs_windows("C:\\Users\\edu\\MyCerts\\Prova", export_format="pem") elif platform.system() == "Darwin": # macOS print("User Certificates (macOS):") user_certs = list_user_certificates_mac() if __name__ == "__main__": main()
No hay comentarios :
Publicar un comentario