156 lines
4.8 KiB
Go
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
|
|
}
|