package generadorFirmaDigital import ( "bytes" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" "strings" "github.com/beevik/etree" dsig "github.com/russellhaering/goxmldsig" ) // ----------------------------------------------------- // implementa dsig.X509KeyStore // ----------------------------------------------------- type MyKeyStore struct { PrivateKey *rsa.PrivateKey CertBytes []byte } func (ks *MyKeyStore) GetKeyPair() (*rsa.PrivateKey, []byte, error) { return ks.PrivateKey, ks.CertBytes, nil } // ----------------------------------------------------- // lee un PEM (PKCS#8 o PKCS#1) y retorna *rsa.PrivateKey // ----------------------------------------------------- func CargarLlavePrivada(path string) (*rsa.PrivateKey, error) { data, err := ioutil.ReadFile(path) if err != nil { return nil, err } block, _ := pem.Decode(data) if block == nil || !strings.Contains(block.Type, "PRIVATE KEY") { return nil, fmt.Errorf("clave PEM inválida") } // Intentar PKCS#8 if keyIfc, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil { if priv, ok := keyIfc.(*rsa.PrivateKey); ok { return priv, nil } return nil, fmt.Errorf("no es llave RSA válida") } // Fallback a PKCS#1 return x509.ParsePKCS1PrivateKey(block.Bytes) } // ----------------------------------------------------- // lee un PEM de certificado X.509 // ----------------------------------------------------- func CargarCertificado(path string) (*x509.Certificate, error) { data, err := ioutil.ReadFile(path) if err != nil { return nil, err } block, _ := pem.Decode(data) if block == nil || block.Type != "CERTIFICATE" { return nil, fmt.Errorf("certificado PEM inválido") } return x509.ParseCertificate(block.Bytes) } // ----------------------------------------------------- // Carga clave privada desde un string PEM // ----------------------------------------------------- func CargarLlavePrivadaDesdeString(pemStr string) (*rsa.PrivateKey, error) { pemStr = strings.TrimSpace(pemStr) block, _ := pem.Decode([]byte(pemStr)) if block == nil || !strings.Contains(block.Type, "PRIVATE KEY") { return nil, fmt.Errorf("clave PEM inválida") } // Intentar PKCS#8 if keyIfc, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil { if priv, ok := keyIfc.(*rsa.PrivateKey); ok { return priv, nil } return nil, fmt.Errorf("no es llave RSA válida") } // Fallback a PKCS#1 return x509.ParsePKCS1PrivateKey(block.Bytes) } // ----------------------------------------------------- // Carga certificado desde un string PEM // ----------------------------------------------------- func CargarCertificadoDesdeString(pemStr string) (*x509.Certificate, error) { pemStr = strings.TrimSpace(pemStr) block, _ := pem.Decode([]byte(pemStr)) if block == nil || block.Type != "CERTIFICATE" { return nil, fmt.Errorf("certificado PEM inválido") } return x509.ParseCertificate(block.Bytes) } // ----------------------------------------------------- // Parsea un XML desde bytes // ----------------------------------------------------- func LeerXML(xmlBytes []byte) (*etree.Document, error) { doc := etree.NewDocument() if err := doc.ReadFromBytes(xmlBytes); err != nil { return nil, err } return doc, nil } // ----------------------------------------------------- // firma el XML (enveloped, RSA-SHA256), sin prefijo y C14N inclusiva con comentarios // ----------------------------------------------------- func FirmarXML(xmlBytes []byte, priv *rsa.PrivateKey, cert *x509.Certificate) ([]byte, error) { // 1) Parsear el XML con etree doc := etree.NewDocument() if err := doc.ReadFromBytes(xmlBytes); err != nil { return nil, err } root := doc.Root() // 2) Crear nuestro KeyStore ks := &MyKeyStore{ PrivateKey: priv, CertBytes: cert.Raw, } // 3) Contexto de firmado ctx := dsig.NewDefaultSigningContext(ks) // — usar namespace por defecto (sin prefijo "ds") ctx.Prefix = "" // — canonicalización inclusiva con comentarios (C14N 1.0 #WithComments) ctx.Canonicalizer = dsig.MakeC14N10WithCommentsCanonicalizer() // 4) Firmar en modo enveloped signedElem, err := ctx.SignEnveloped(root) if err != nil { return nil, err } // 5) Serializar el documento firmado doc.SetRoot(signedElem) return doc.WriteToBytes() } // ----------------------------------------------------- // Convierte un objeto a XML usando etree (alternativa a JAXB en Java) // ----------------------------------------------------- func ConvertirObjetoAXML(obj interface{}) ([]byte, error) { // Esta función es un placeholder - en Go normalmente se usaría // encoding/xml o alguna biblioteca específica en lugar de JAXB de Java // Ejemplo simplificado para demostración: var buf bytes.Buffer // Aquí iría la lógica real de conversión dependiendo de la estructura // encoding/xml puede manejar esto con struct tags para mapear campos return buf.Bytes(), nil }