xmldsig

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 27, 2023 License: Apache-2.0 Imports: 24 Imported by: 8

README

XML DSig

Partial implementation of the XML DSig standard for Go. Can be used to manage certificates in .p12 format and generate signatures typically used with UBL invoice documents or similar local standards.

Lint Test Go Go Report Card GoDoc Latest Tag

NOTES

  • Canonicalisation: at the moment is EXTREMELY limited. It'll handle missing namespaces on root elements, but you MUST ensure the Go structures (type struct) you intend to Marshal contain attributes in their canonical order: first namespaces, then regular attributes.

Usage Example

type SampleDoc struct {
	XMLName       xml.Name `xml:"test:SampleDoc"`
	TestNamespace string   `xml:"xmlns:test,attr"`
	Title         string
	Signature     *xmldsig.Signature `xml:"ds:Signature,omitempty"` // Add signature object!
}

func main() {
    doc := &SampleDoc{
		TestNamespace: "http://invopop.com/xml/test",
		Title:         "This is a test",
	}
	// Using XAdES FacturaE example policy config
	xades := &xmldsig.XAdESConfig{
		Role:        xmldsig.XAdESSignerRole("third party"),
		Description: "test",
		Policy: &xmldsig.XAdESPolicyConfig{
			URL:         "http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf",
			Description: "Política de Firma FacturaE v3.1",
			Algorithm:   "http://www.w3.org/2000/09/xmldsig#sha1",
			Hash:        "Ohixl6upD6av8N7pEvDABhEL6hM=",
		},
	}
    data, _ := xml.Marshal(doc)
    cert, _ := xmldsig.LoadCertificate("./invopop.p12", "invopop")
    doc.Signature, _ = xmldsig.Sign(data,
		xmldsig.WithCertificate(cert),
		xmldsig.WithXAdES(xades),
	)

    // Now output the data
    out, _ := xml.Marshal(doc)
    fmt.Println(string(out))
}

Support is also included for using a Time Stamp Authority (TSA). Simply add the following to the Sign options with the URL of the service you want to use:

xmldsig.WithTimestamp(xmldsig.TimestampFreeTSA) // uses https://freetsa.org/tsr

Certificates

Signing and certificates can be overwhelming. OpenSSL is the tool to use for clarifying what the situation is and this page has a useful set of commands: https://www.sslshopper.com/article-most-common-openssl-commands.html

This library requires certificates in PKCS12 DER format (.pki or .p12 extension). If you don't have something like that, use the OpenSSL tools to convert between X509 (.pem) format and PKCS12.

The order of certificates is important, the main certificate must come first. You can check order using the following command:

openssl pkcs12 -info -in keyStore.p12

It might be a good idea to try exporting and re-creating your existing PKCS12 files if in doubt. First extract to pem:

openssl pkcs12 -in invopop.p12 -out invopop.pem -nodes

Split the resulting .pem file into multiple parts for the key, certificate, and CA certificate(s) using your text editor. Then rebuild:

openssl pkcs12 -export -out invopop.p12 -inkey invopop.key -in invopop.crt -certfile invopop.ca

This project is developed and maintained under the Apache 2.0 Open Source license by Invopop.

Copyright 2021-2023 Invopop Ltd.

Documentation

Index

Constants

View Source
const (
	NamespaceXAdES = "http://uri.etsi.org/01903/v1.3.2#"
	NamespaceDSig  = "http://www.w3.org/2000/09/xmldsig#"
)

Namespaces

View Source
const (
	XMLNS = "xmlns"
	XAdES = "xades"
	DSig  = "ds"
)

Namespace names (short)

View Source
const (
	AlgEncSHA256     = "http://www.w3.org/2001/04/xmlenc#sha256"
	AlgEncSHA512     = "http://www.w3.org/2001/04/xmlenc#sha512"
	AlgDSigSHA1      = "http://www.w3.org/2000/09/xmldsig#sha1"
	AlgDSigRSASHA1   = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
	AlgDSigRSASHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
)

Algorithms

View Source
const ISO8601 = "2006-01-02T15:04:05-07:00"

ISO8601 contains the time format used for signing times (based on https://en.wikipedia.org/wiki/ISO_8601)

View Source
const (
	TimestampFreeTSA = "https://freetsa.org/tsr"
)

List of free TSA servers: https://gist.github.com/Manouchehri/fd754e402d98430243455713efada710

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound is returned when a matching certificate was not found.

Functions

func NakedPEM

func NakedPEM(cert *x509.Certificate) string

NakedPEM converts a x509 formated certificate to the PEM format without the headers, useful for including in the XML document.

func PEMCertificate

func PEMCertificate(cert *x509.Certificate) []byte

PEMCertificate provides the complete PEM version of the certificate.

func PEMPrivateRSAKey

func PEMPrivateRSAKey(key *rsa.PrivateKey) []byte

PEMPrivateRSAKey issues a PEM string with the RSA Key.

Types

type AlgorithmMethod

type AlgorithmMethod struct {
	Algorithm string `xml:"Algorithm,attr"`
}

AlgorithmMethod contains ...

type Certificate

type Certificate struct {
	CaChain []*x509.Certificate
	// contains filtered or unexported fields
}

Certificate stores information about a signing Certificate which can be used to sign a facturae XML

func LoadCertificate

func LoadCertificate(path, password string) (*Certificate, error)

Load creates a new Certificate from the info obtained from pkcs12 formated data stream

func (*Certificate) Fingerprint

func (cert *Certificate) Fingerprint() string

Fingerprint will return the SHA512 hash of the public key

func (*Certificate) Issuer

func (cert *Certificate) Issuer() string

Issuer returns a description of the certificate issuer

func (*Certificate) NakedPEM

func (cert *Certificate) NakedPEM() string

ToPEM will return the public certificate encoded in base64 PEM (without markers like "-----BEGIN CERTIFICATE-----")

func (*Certificate) PEM

func (cert *Certificate) PEM() []byte

PEM provides the PEM representation of the certificate.

func (*Certificate) PrivateKey

func (cert *Certificate) PrivateKey() []byte

PrivateKey provides the private key in PEM format.

func (*Certificate) PrivateKeyInfo

func (cert *Certificate) PrivateKeyInfo() *PrivateKeyInfo

PrivateKeyInfo is the RSA private key info

func (*Certificate) SerialNumber

func (cert *Certificate) SerialNumber() string

SerialNumber returns the serial number of the certificate

func (*Certificate) Sign

func (cert *Certificate) Sign(data string) (string, error)

Sign will first create a hash of the data passed and then create a string (base64) representation of the signature obtained using the private key of the certificate

func (*Certificate) TLSAuthConfig

func (cert *Certificate) TLSAuthConfig() (*tls.Config, error)

TLSConfig prepares TLS authentication connection details ready to use with HTTP servers that require them in addition to the signatures of the XML-DSig signed payload.

type DataObjectFormat

type DataObjectFormat struct {
	ObjectReference string `xml:"ObjectReference,attr"`

	Description      string            `xml:"xades:Description"`
	ObjectIdentifier *ObjectIdentifier `xml:"xades:ObjectIdentifier"`
	MimeType         string            `xml:"xades:MimeType"`
	Encoding         string            `xml:"xades:Encoding"` // normally empty
}

DataObjectFormat contains ...

type Digest

type Digest struct {
	Method *AlgorithmMethod `xml:"ds:DigestMethod"`
	Value  string           `xml:"ds:DigestValue"`
}

Digest contains ...

type Identifier

type Identifier struct {
	Qualifier string `xml:"Qualifier,attr"`
	Value     string `xml:",chardata"`
}

Identifier contains ...

type IssuerSerial

type IssuerSerial struct {
	IssuerName   string `xml:"ds:X509IssuerName"`
	SerialNumber string `xml:"ds:X509SerialNumber"`
}

IssuerSerial contains ...

type KeyInfo

type KeyInfo struct {
	XMLName xml.Name `xml:"ds:KeyInfo"`
	ID      string   `xml:"Id,attr"`

	X509Data *X509Data `xml:"ds:X509Data,omitempty"`
	KeyValue *KeyValue `xml:"ds:KeyValue,omitempty"`
}

KeyInfo contains ...

type KeyValue

type KeyValue struct {
	Modulus  string `xml:"ds:RSAKeyValue>ds:Modulus"`
	Exponent string `xml:"ds:RSAKeyValue>ds:Exponent"`
}

KeyValue contains ...

type Namespaces

type Namespaces map[string]string

Namespaces defines special functionality for dealing with namespaces

func (Namespaces) Add

func (ns Namespaces) Add(name, url string) Namespaces

Add will add the namespace and return a new instance of the map without modifying the original.

type Object

type Object struct {
	QualifyingProperties *QualifyingProperties `xml:"xades:QualifyingProperties"`
}

Object contains ...

type ObjectIdentifier

type ObjectIdentifier struct {
	Identifier  *Identifier `xml:"xades:Identifier"`
	Description string      `xml:"xades:Description"`
}

ObjectIdentifier holds and identifier

type Option

type Option func(o *options) error

Option function to be used for defining startup options

func WithCertificate

func WithCertificate(cert *Certificate) Option

WithCertificate expects a path to a file containing a PKCS12 (.p12 or .pfx) certificate file, and a password used to open it.

func WithCurrentTime

func WithCurrentTime(fn func() time.Time) Option

WithCurrentTime allows a callback to be provided in order to using a different signing time method. This is especially useful for testing. Default is to provide `time.Now().UTC()`.

func WithDocID

func WithDocID(id string) Option

WithDocID assigns a document ID to the signatures

func WithNamespace

func WithNamespace(name, url string) Option

WithNamespace is used to define all the namespaces that must be included in canonicalization processes. DSig requires each segment that is used in a hash to reference all previously defined namespaces, even if they are not used inside the current segment.

func WithTimestamp

func WithTimestamp(url string) Option

WithTimestamp will add an official timestamp to the signature.

func WithXAdES

func WithXAdES(config *XAdESConfig) Option

WithXAdES adds the XAdES policy with the suggested role.

type PolicyIdentifier

type PolicyIdentifier struct {
	SigPolicyID   *SigPolicyID `xml:"xades:SignaturePolicyId>xades:SigPolicyId"`
	SigPolicyHash *Digest      `xml:"xades:SignaturePolicyId>xades:SigPolicyHash"`
}

PolicyIdentifier contains ...

type PrivateKeyInfo

type PrivateKeyInfo struct {
	Modulus  string
	Exponent string
}

PrivateKeyInfo contains info about modulus and exponent of the key

type QualifyingProperties

type QualifyingProperties struct {
	XAdESNamespace string `xml:"xmlns:xades,attr,omitempty"`
	ID             string `xml:"Id,attr"`
	Target         string `xml:"Target,attr"`

	SignedProperties   *SignedProperties   `xml:"xades:SignedProperties"`
	UnsignedProperties *UnsignedProperties `xml:"xades:UnsignedProperties,omitempty"`
}

QualifyingProperties the funny XaDES signature confirmation policy data. This is the only place the `xades` namespace is required, so we can add it just here.

type Reference

type Reference struct {
	ID   string `xml:"Id,attr,omitempty"`
	Type string `xml:"Type,attr,omitempty"`
	URI  string `xml:"URI,attr"`

	Transforms   *Transforms      `xml:"ds:Transforms,omitempty"`
	DigestMethod *AlgorithmMethod `xml:"ds:DigestMethod"`
	DigestValue  string           `xml:"ds:DigestValue"`
}

Reference contains ...

type Roles

type Roles struct {
	ClaimedRole []string `xml:"xades:ClaimedRole"`
}

Roles contains ...

type SigPolicyID

type SigPolicyID struct {
	Identifier  string `xml:"xades:Identifier"`
	Description string `xml:"xades:Description"`
}

SigPolicyID contains ...

type Signature

type Signature struct {
	DSigNamespace string   `xml:"xmlns:ds,attr,omitempty"`
	ID            string   `xml:"Id,attr"`
	XMLName       xml.Name `xml:"ds:Signature"`

	SignedInfo *SignedInfo `xml:"ds:SignedInfo"`
	Value      *Value      `xml:"ds:SignatureValue"`
	KeyInfo    *KeyInfo    `xml:"ds:KeyInfo"`
	Object     *Object     `xml:"ds:Object,omitempty"`
	// contains filtered or unexported fields
}

Signature contains the complete signature to be added to the document.

func Sign

func Sign(data []byte, opts ...Option) (*Signature, error)

Sign the provided data

type SignedInfo

type SignedInfo struct {
	XMLName xml.Name `xml:"ds:SignedInfo"`
	ID      string   `xml:"Id,attr,omitempty"`

	CanonicalizationMethod *AlgorithmMethod `xml:"ds:CanonicalizationMethod"`
	SignatureMethod        *AlgorithmMethod `xml:"ds:SignatureMethod"`
	Reference              []*Reference     `xml:"ds:Reference"`
}

SignedInfo contains the info that will be signed by the certificate.

type SignedProperties

type SignedProperties struct {
	XMLName xml.Name `xml:"xades:SignedProperties"`
	ID      string   `xml:"Id,attr"`

	SignatureProperties  *SignedSignatureProperties `xml:"xades:SignedSignatureProperties"`
	DataObjectProperties *DataObjectFormat          `xml:"xades:SignedDataObjectProperties>xades:DataObjectFormat"`
}

SignedProperties contains ...

type SignedSignatureProperties

type SignedSignatureProperties struct {
	SigningTime        string              `xml:"xades:SigningTime"`
	SigningCertificate *SigningCertificate `xml:"xades:SigningCertificate"`
	PolicyIdentifier   *PolicyIdentifier   `xml:"xades:SignaturePolicyIdentifier"`
	SignerRole         *SignerRole         `xml:"xades:SignerRole"`
}

SignedSignatureProperties contains ...

type SignerRole

type SignerRole struct {
	ClaimedRoles *Roles `xml:"xades:ClaimedRoles"`
}

SignerRole contains ...

type SigningCertificate

type SigningCertificate struct {
	CertDigest   *Digest       `xml:"xades:Cert>xades:CertDigest"`
	IssuerSerial *IssuerSerial `xml:"xades:Cert>xades:IssuerSerial"`
}

SigningCertificate contains ...

type Timestamp

type Timestamp struct {
	CanonicalizationMethod *AlgorithmMethod `xml:"ds:CanonicalizationMethod"`
	EncapsulatedTimeStamp  string           `xml:"xades:EncapsulatedTimeStamp"`
}

Timestamp contains ...

type TimestampSignatureValue

type TimestampSignatureValue struct {
	XMLName   xml.Name
	Namespace string `xml:"xmlns:ds,attr"`
	ID        string `xml:"Id,attr"`
	Value     string `xml:",chardata"`
}

TimestampSignatureValue contains ...

type Transforms

type Transforms struct {
	Transform []*AlgorithmMethod `xml:"ds:Transform"`
}

Transforms contains ...

type UnsignedProperties

type UnsignedProperties struct {
	SignatureTimestamp *Timestamp `xml:"xades:UnsignedSignatureProperties>xades:SignatureTimestamp"`
}

UnsignedProperties contains ...

type Value

type Value struct {
	ID    string `xml:"Id,attr"`
	Value string `xml:",chardata"`
}

Value contains ...

type X509Data

type X509Data struct {
	X509Certificate []string `xml:"ds:X509Certificate"`
}

X509Data contains ...

type XAdESConfig

type XAdESConfig struct {
	Role        XAdESSignerRole    `json:"role"`
	Description string             `json:"description,omitempty"`
	Policy      *XAdESPolicyConfig `json:"policy"`
}

XAdESConfig defines what is expected for the configuration.

type XAdESPolicyConfig

type XAdESPolicyConfig struct {
	URL         string `json:"url"`                   // URL to the policy definition
	Description string `json:"description,omitempty"` // Optional human description
	Algorithm   string `json:"algorithm"`             // eg. SHA1 o SHA256
	Hash        string `json:"hash"`                  // Base64 encoded hash (usually provided with policy)
}

XAdESPolicyConfig defines what policy details should be used.

type XAdESSignerRole

type XAdESSignerRole string

XAdESSignerRole defines the accepted signer roles

func (XAdESSignerRole) String

func (r XAdESSignerRole) String() string

String converts the XAdES role into a string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL