1. Localización de los ficheros de certificados
En Linux-Mozilla los certificados se guardan en la carpeta hija de:
~/.mozilla/firefoxque 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/nssdb2. 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ñaopenssl 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