2025-05-17 11:36:26 -04:00

156 lines
4.8 KiB
Go

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
}