lunes, 16 de diciembre de 2024

Onshape (IV): Exportar la piezas en formato step

Para exportar las pieas en formato STEP hay que pinchar la pieza con el botón derecho y darle a exportar. 


Y nos aparece esta pantalla donde elegimos el formato STEP que es uno de los que mas se utilizan para el mecanizado CNC.


 

Onshape (III) Crear piezas especulares

 Para crear una pieza especular hay que seleccionar la herramienta "Mirror" la pieza y un plano que hará de espejo.

Si la pieza toca o es atravesada por el plano espejo, entonces nos solapa la pieza. Para ello es conveniente que el plano espejo no intersecte con la pieza.

En este caso hemos elegido la herramienta espejo ("Mirror") , se ha seleccionado la pieza "Part1" y en la vista isométrica se ha elegido el plano "Right" tal como se muestra en la figura, y con eso se crea una nueva pieza "Part3" que es la imagen especular de "Part1". Para ver mejor la cosa se ha seleccionado la vista isométrica



Onshape (II) Desplazamientos y copias de piezas 3D. Union, intersección y creación de huecos.

1. Desplazamientos y/o copias

Para crear desplazamientos y copias de piezas 3D utilizamos la herramienta "Transform". Debemos elegir la pieza a mover (o copiar) y las coordenadas a desplazarnos. En el cubo de la derecha podemos ver la orientación de los ejes y por tanto saber si desplazamos en e eje "Y" en nuestro caso y un desplazamiento positivo en ese eje. También es importante si queremos copiar o simplemente desplazar.


2. Herramienta boolean (unión, intersección, crear huecos)

Para crear el hueco de una pieza sobre otra, la pieza que da forma al hueco es la herramienta "tool".

Veamos como hacemos un hueco sobre el toro. Para ello primeramente hay que desplazar las piezas involucradas y dejarlas en la posición deseada.

Como ya sabemos crear piezas 3D por revolución y estrusión, vamos a quitarle un trozo cúbico al toro. Para ello creamos un cuadrado en sketch y lo extruimos formando un cubo y lo desplazamos a la posición desada para hacer el hueco en el toro.

Para ello es mas fácil hacer el sketch y no ocultar el toro. Y luego hacer una extrusión, tal como se muestra en la figura



Ahora se cambia de vista, por ejemplo a una isométrica para tener una mejor visión de lo que se hace  un transform (desplazamiento) deseado


Ahora ya hemos tenemos las piezas en la posición deseada. Ahora vamos a la herramienta booleana y damos como herramienta "tool" al cubo y como objetivo "target" el toro y le decimos que conserve la herramienta. Ojo, en este caso modifica el toro, si nos hiciera falta posteriormente el toro sin agujeros, se tendrá que copiar antes de hacer esta transformación


Y queda tras ocultar la pieza2 (Part2) que es el cubo y mostrar solo la pieza1 (Part1) que es el toro con el agujero nos queda










Onshape (I) Introducción. Crear un sketch

 1 Entrar y crear un documento.

Buscar en google "onshape sign in" y te pregunta usuario y contraseña.

Ahora vamos a "create" y "document" ambos en la parte superior izquierda



Le damos nombre y guardamos. A partir de aquí todo lo que se hace se guarda automáticamente

Ahora nos aparecen los 3 planos que corresponden a planta, alzado y perfil (top, front y right). Realmente el perfil no es el plano right sino "left"

Tambien hay un dibujo de un cubo en la parte superior izquierda que nos indica la orientación. Para cambiar la orientacion (rotar) se aprita el botón derecho del ratón y movemos el ratón y vemos como giramos. 



Se puede elegir también con el cubo pequeñín que hay un poco mas abajo el tipo de perspectiva  a utilizar para poder ver mejor la pieza cuando está en 3D

2. Crear un sketch


Un sketch es un dibujo en 2 D o sea sobre un plano. El plano puede ser top, front y right o una cara de una pieza tridimensional. 

2.1 Elección del plano de trabajo


Para empezar seleccionamos cualquiera de los tres planos (top, front y right). Supongamos que quermos hacer un sketch de la planta, pero vista desde abajo. 
Para ello con el botón derecho del ratón apretado movemos hasta que vemos la parte inferior del plano top que se ve al revés y en el cubo de la derecha apretamos con el botón izquierdo del mouse el lado "bottom"



Ahora que hemos seleccionado el plano del sketch, podemos ver esta pantalla. El "botón verde check" sirve para guardar el sketch una vez que hayamos dibujado lo que queramos. 



El sketch es muy importante pues todas las medidas que queramos hacer se hacen sobre el. Una vez está la pieza en 3 D hay que volver al sketch para dar medidas nuevas o crear un sketch sobre una cara y dar medidas (como por ejemplo los puntos donde queramos hacer agujeros de taladro o rosca).

2.2 Elección de los elementos de dibujo y de restricción 

Cuando hemos seleccionado el sketch, para comenzar debemos utilizar elementos de dibujo:  líneas (segmentos), círculos,  arcos de círculo, polígonos principalmente.



Y también elementos de restricción: como medir (o mejor dicho ajustar distancias o ángulos), horizontalidad y verticalidad de segmentos, coincidencia de puntos .... Se marca en rojo el ajuste de distancias y ángulos y en amarillo las otras indicadas, pues la primera es la que más se va a utilizar.


Para utilizar cualquiera de las restricciones, primeramente se aprieta el botón izquierdo del ratón sobre la restricción y a continuación de marca el elemento o elementos afectados. Por ejemplo para longitudes de segmento se marca el segmento y soltamos el ratón y nos desplazamos un poco y aparece la longitud y haciendo click izquierdo sobre la cantidad se puede introducir por teclado. Para medir distancias hay que marcar 2 elementos. Para horizontalidad, se marca el segmento candidato a ser horizontal. En la imagen aparece como cambiamos la longitud del segmento.

2.3 El sketch debe de ser una figura cerrada.

Para que el sketch sea correcto, debe de contener una figura cerrada, ya que en principio un boceto es para "extruir-lo", aunque también podemos hacer una figura de revolución. Para el caso de figuras de revolución debemos tener una figura cerrada y dibujar un segmento que será el eje de revolución.

Vemos como extruimos un círculo: Primeramente creamos el círculo en el sketch1 (círculo 1 en azul) , le damos al icono "extrude" y le indicamos el sketch que queremos estruir y la profundidad (40 mm en nuestro caso)  y en el check verde aceptamos la transformación y nos aparece el dibujo de la derecha



Si hubiésemos querido generar un toro de revolución (un donut) , tendríamos que dibujar un segmento que sea el eje de revolución y darle al icono "revolute".

Si nos fijamos, al modificar el sketch1 para dibujar el eje de rotación, la operación de extrusión anterior "Extrude1" sale en rojo, indicando que no es válida pues no podemos estruir una línea ya que no tiene área.

Cada vez que aplicamos una transformación ya sea "Extrude" o "Revolute", se crea una pieza 3D, en este caso es el Part1 que corresponde al toro creado por revolución del círculo.



2.4 Solo se permite una sola transformación por sketch 


Para no tener problemas, si queremos hacer otra transformación sobre el sketch, debemos copiarlo. Para ello le damos con el botón derecho y elegimos "Copy sketch"






















martes, 10 de diciembre de 2024

Python, fastHTML,htmx Actualizar un campo con una petición AJAX

 1. Introducción

Con htmx, es facil modificar un componente tipo <div> con la información devuelta por una petición ajax provocada por un elemento de un formulario. En este ejemplo llenamos el componente <div> con el string "Hola

<form>
  <input type="text" name="miCampo" 
     hx-get="/ruta-del-endpoint"
     hx-trigger="blur,change"
     hx-target="#resultado">
</form>

<div id="resultado"></div>
#Python y fastHTML
@app.get("/ruta-del-endpoint")
def getFieldEvent(request:Request):
  return "Hola!

Pero si queremos actualizar el valor de un element tipo input, no funciona.

2. Modificar el valor de elementos "input" 

Para ello tenemos que recurrir a javascript

Supongamos que tenemos 4 campos. De ellos 2 se encargan de modificar el conenido de los otros dos campos. En este ejemplo el campo1 modifica al campo2 y el campo3 modifica al campo4

<form>
  <input type="text" id="campo1" name="campo1" hx-get="/ruta-del-endpoint"
     hx-trigger="blur,change" hx-target="#campo2">
  <input type="text" id="campo2" name="campo2">
  <input type="text" id="campo3" name="campo3" hx-get="/ruta-del-endpoint"
     hx-trigger="blur,change" hx-target="#campo4">
<input type="text" id="campo4" name="campo4">
</form>

Supongamos que el código Python del endpoint es el mismo

Para ello vamos a crear un program Python con FastHTML que haga los siguientes pasos:

1. Crear un objeto que guarde los ids de los campos que lanzan el evento como nombre del campo y como valor que sea el id del campo a modificar. En Python sería:

strJs="var paramsChange={'campo1':'campo2', 'campo3':'campo4'}"

2. Creamos un fichero (static/js/fieldChange.js) con  el contenido de la función javascript que actualiza los campos: Para ello busca el evento "htmx:afterRequest" y filtra aquellos cuyo endpoint es "/ruta-del-endpoint" y si el campo que lanza el evento (event.target) se encuentra dentro de la variable paramsChange entonces actualiza el valor del campo con lo que retorno la llamada ajax

// Update an input element with the value of the request
document.addEventListener("htmx:afterRequest", (event) => {
    const hxget=event.target.getAttribute('hx-get');
	if (hxget) {
		const elemId=event.target.id;
		//Only for request type get to the url "/fieldEvent"
		if (hxget.startsWith("/ruta-del-endpoint") && elemId in paramsChange) {
const targetId=paramsChange[elemId]; const input = document.querySelector(targetId); input.value = event.detail.xhr.responseText; } } });

3. A la variable python "strJs" hay que añadirle el contenido de este fichero: que es javascript

with open('static/js/fieldChange.js', 'r') as file:
  content = file.read()
  strJs+="\n"+content

4. Para crear los campos con FastHTML podemos creatr un diccionario para cada campo con los atributos de cada "input". Podríamos haber creado un form , pero para simplificar meto el javascript y los 4 campos dentro de un <div> (fh.Div). A mi me encanta como resuelve fastHTML la creaciópn de campos. Ojo que utiliza guiones bajos "_" en vez de "-" cuando define los parámetros de htmx!

fieldDicts=[
	{'type':'text', 'id':'campo1', 'name':'campo1','hx_post':'/ruta-del-endpoint', 'hx_target':'#campo2', 'hx_trigger':'blur,change'},
	{'type':'text', 'id':'campo2', 'name':'campo2'},  
	{'type':'text', 'id':'campo3', 'name':'campo3','hx_post':'/ruta-del-endpoint', 'hx_target':'#campo4', 'hx_trigger':'blur,change'},  
	{'type':'text', 'id':'campo4', 'name':'campo4'}, 
]	 
return fh.Div(
	fh.Script(strJs),
	*[fh.Input(**fieldDict) for fieldDict in fieldDicts],
)











domingo, 8 de diciembre de 2024

Python exportar certificados de los navegadores

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

Para listar los certificados contenidos en una carpeta de las mencionadas arriba, por ejemplo para mozilla, donde XXXX es la carpeta que contiene el fichero "cert9.db":

certutil -L -d sql:$HOME/.mozilla/firefox/XXXX

Ojo hay que tener cuiidado de no incluir "cer9.db" en la ruta

Y para Chrome

certutil -L -d sql:$HOME/.pki/nsb

Y nos mostará algo parecido a:

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

Pero hay que tener cuidado pues no incluye la clave privada. Si queremos que incluya la clave pivada, hay que guardar una contraseña en un fichero y ejecutar el comando. En este ejmplo hacemos las dos cosas

# 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

Ahora supongamos que queremos leer el fichero pem generado "cert.pem" y queremos guardarlo con otro nombre y con otra contraseña:

#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()










jueves, 28 de noviembre de 2024

Python utilizar librerias java (INCOMPLETO)

 Para ello se opta por utilizar Pyjnius, py4j, jpype, scyjava ..


1. Descargar los jars a una carpeta

Primeramente se instala maven en ubuntu:

sudo apt install maven -y

Creamos una carpeta que será la de nuestro proyecto, nos situamos dentro de ella

Vamos a mavencentral y buscamos nuestra dependencia por ejempo itext7 y descargamos el pom.xml en la carpteta


Veamos un ejemplo de pom.xml para poder descargar la libreria itext7

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>mi-proyecto-itext</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <itext.version>9.0.0</itext.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itext7-core</artifactId>
      <version>${itext.version}</version>
      <type>pom</type>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>


Ejecutamos 

mvn dependency:copy-dependencies -DoutputDirectory=target/dependencies

si no se especifica -DoutputDirectory entonces lo descarga en target/dependencies

o tambien

mvn dependency:resolve -U

pero las copia en ~/.m2/repository/com/itextpdf/itext7-core/7.2.5/itext7-core-7.2.5.jar

Y ya tenemos las dependencia en el directorio que hemos indicado. Ahora falta usar dichos jars en python


2. Usar las librerias en python

 

Python obtener el valor de un nodo de un fichero xml que tiene namespaces

 Queremos extraer el contenido del nodo <ValorBinario> de este fichero xml que namespaces: 

Nos fijamos que el namespace marcado en naranja no tiene nombre, por tanto se aplicará a todas los nodos cuyas etiquetas no tenganprefijo del namespace como "contenido" o "ValorBinario"


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<insidews:documento xmlns="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido" 
    xmlns:ns2="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos" 
    xmlns:ns3="http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma" 
    xmlns:ns4="http://www.w3.org/2000/09/xmldsig#" 
    xmlns:ns5="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e" 
    xmlns:ns6="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales" 
    xmlns:ns7="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/documento-e" 
    xmlns:insidews="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService">
    <ns5:documento Id="ES_L01462384_2022_2_ACORDS_PLE000001">
        <contenido Id="CONTENIDO_DOCUMENTO">
            <ValorBinario>JVB..</ValorBinario>
            <NombreFormato>PDF</NombreFormato>
        </contenido>
        <ns2:metadatos Id="DOC_ES_L01462384_2022_2_ACORDS_PLE000001_METADATOS">
            <ns2:VersionNTI>http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e</ns2:VersionNTI>
            <ns2:Identificador>ES_L01462384_2022_2_ACORDS_PLE000001</ns2:Identificador>
            <ns2:Organo>L01462384</ns2:Organo>
            <ns2:FechaCaptura>2024-11-05T00:00:00.000+01:00</ns2:FechaCaptura>
            <ns2:OrigenCiudadanoAdministracion>true</ns2:OrigenCiudadanoAdministracion>
            <ns2:EstadoElaboracion>
                <ns2:ValorEstadoElaboracion>EE99</ns2:ValorEstadoElaboracion>
            </ns2:EstadoElaboracion>
            <ns2:TipoDocumental>TD02</ns2:TipoDocumental>
        </ns2:metadatos>
        <ns3:firmas>
	   <ns3:firma Id="FIRMA_1">
                <ns3:TipoFirma>TF06</ns3:TipoFirma>
                <ns3:ContenidoFirma>
                    <ns3:FirmaConCertificado>
                        <ns3:ReferenciaFirma xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">#CONTENIDO_DOCUMENTO</ns3:ReferenciaFirma>
                    </ns3:FirmaConCertificado>
                </ns3:ContenidoFirma>
            </ns3:firma>
            <ns3:firma Id="FIRMA_2">
                <ns3:TipoFirma>TF06</ns3:TipoFirma>
                <ns3:ContenidoFirma>
                    <ns3:FirmaConCertificado>
                        <ns3:ReferenciaFirma xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">#CONTENIDO_DOCUMENTO</ns3:ReferenciaFirma>
                    </ns3:FirmaConCertificado>
                </ns3:ContenidoFirma>
            </ns3:firma>
        </ns3:firmas>
    </ns5:documento>
    <ns7:metadatosAdicionales>
        <ns6:MetadatoAdicional tipo="string" nombre="eEMGDE3.1.Nombre.NombreNatural">
            <ns6:valor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">$5.NOM_DOC$</ns6:valor>
        </ns6:MetadatoAdicional>
    </ns7:metadatosAdicionales>
</insidews:documento>


para ello se ha creado este programa python

import base64
from lxml import etree

def extraer_pdf(xml_path, output_pdf_path):
    try:
        # Cargar el archivo XML
        with open(xml_path, "rb") as xml_file:
            xml_data = xml_file.read()
        
        # Parsear el XML
        tree = etree.fromstring(xml_data)
        
        # Definir los namespaces utilizados en el XML
        namespaces = {
            "insidews": "https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService",
            "ns5": "http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e",
            "contenido": "http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido"
        }
        
        # Listar nodos para depuración
        print("Nodos disponibles:")
        for elem in tree.iter():
            print(f"{elem.tag}")

        # Buscar el contenido del tag <ValorBinario>
        valor_binario_node = tree.xpath(
            "//insidews:documento/ns5:documento/contenido:contenido/contenido:ValorBinario",
            namespaces=namespaces
        )
        
        if not valor_binario_node:
            raise ValueError("No se encontró el tag <ValorBinario> en el documento XML.")
        
        # Extraer el contenido en Base64
        valor_binario = valor_binario_node[0].text
        
        # Decodificar el contenido Base64
        pdf_data = base64.b64decode(valor_binario)
        
        # Guardar el contenido en el archivo PDF
        with open(output_pdf_path, "wb") as pdf_file:
            pdf_file.write(pdf_data)
        
        print(f"Archivo PDF extraído correctamente y guardado en: {output_pdf_path}")
    
    except Exception as e:
        print(f"Error al procesar el archivo XML: {e}")


# Ruta al archivo XML y al PDF de salida
xml_path = "/home/eduard/MyPython/02.llibreriesOLD/ximo-python-library/ES_L01462384_2022_2_ACORDS_PLE000001.xml"
output_pdf_path = "mifichero.pdf"

# Ejecutar la función
extraer_pdf(xml_path, output_pdf_path)


Y cuando nos lista los nodos aparece

{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}contenido
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}ValorBinario
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}NombreFormato
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}metadatos
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}VersionNTI
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}Identificador
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}Organo
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}FechaCaptura
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}OrigenCiudadanoAdministracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}EstadoElaboracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}ValorEstadoElaboracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}TipoDocumental
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firmas
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}TipoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ContenidoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}FirmaConCertificado
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ReferenciaFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}TipoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ContenidoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}FirmaConCertificado
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ReferenciaFirma
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/documento-e}metadatosAdicionales
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales}MetadatoAdicional
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales}valor

A raiz de todo esto vemos que en el fichero xml, la ruta a <ValorBinario> es:

<insidews:documento><ns5:documento><contenido><ValorBinario>

Por otra parte si reducimos el listado anterior:

{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}contenido
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}ValorBinario

Vemos que contenido si tiene un namespace que es "contenido" y tambien ValorBinario tiene el namespace "contenido". Por tanto la ruta para llegar a valor binario es:


# Buscar el contenido del tag <ValorBinario>
valor_binario_node = tree.xpath(
   "//insidews:documento/ns5:documento/contenido:contenido/contenido:ValorBinario",
   namespaces=namespaces
)