xmlsec
xmlsec is a Go binding for XML Security Library (https://www.aleksey.com/xmlsec/index.html)
Status
- API still unstable.
- There's enough code to generate signatures for a libxml2 Document, and to verify it, but not much else.
- PRs, suggestions for more coverage welcome.
Example
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
"os"
"github.com/lestrrat-go/libxml2/parser"
"github.com/lestrrat-go/xmlsec"
"github.com/lestrrat-go/xmlsec/crypto"
"github.com/lestrrat-go/xmlsec/dsig"
)
func ExampleSignature_Sign() {
xmlsec.Init()
defer xmlsec.Shutdown()
p := parser.New(parser.XMLParseDTDLoad | parser.XMLParseDTDAttr | parser.XMLParseNoEnt)
doc, err := p.ParseString(`<?xml version="1.0" encoding="UTF-8"?>
<Message><Data>Hello, World!</Data></Message>`)
n, err := doc.DocumentElement()
if err != nil {
log.Printf("DocumentElement failed: %s", err)
return
}
// n is the node where you want your signature to be
// generated under
sig, err := dsig.NewSignature(n, dsig.ExclC14N, dsig.RsaSha1, "")
if err != nil {
log.Printf("failed to create signature: %s", err)
return
}
sig.AddReference(dsig.Sha1, "", "", "")
sig.AddTransform(dsig.Enveloped)
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Printf("failed to generate key: %s", err)
return
}
if err := sig.Sign(key); err != nil {
log.Printf("failed to sign: %s", err)
return
}
log.Printf("%s", doc.Dump(true))
}
func ExampleDSigCtx_Sign() {
xmlsec.Init()
defer xmlsec.Shutdown()
ctx, err := dsig.NewCtx()
if err != nil {
log.Printf("Failed to create signature context: %s", err)
return
}
defer ctx.Free()
// This stuff isn't necessary if you already have a key file
privkey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Printf("Failed to generate private key: %s", err)
return
}
var pemkey = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privkey),
}
pemfile, err := ioutil.TempFile("", "xmlsec-test-")
if err != nil {
log.Printf("Failed to create temporary pemfile")
return
}
defer os.Remove(pemfile.Name())
defer pemfile.Close()
if err := pem.Encode(pemfile, pemkey); err != nil {
log.Printf("Failed to write to pemfile: %s", err)
return
}
if err := pemfile.Sync(); err != nil {
log.Printf("Failed to sync pemfile: %s", err)
return
}
key, err := crypto.LoadKeyFromFile(pemfile.Name(), crypto.KeyDataFormatPem)
if err != nil {
log.Printf("Faild to load key: %s", err)
return
}
ctx.SetKey(key)
p := parser.New(parser.XMLParseDTDLoad | parser.XMLParseDTDAttr | parser.XMLParseNoEnt)
doc, err := p.ParseString(`<?xml version="1.0" encoding="UTF-8"?>
<!-- XML Security Library example: Simple signature template file for sign1 example. -->
<Envelope xmlns="urn:envelope">
<Data>
Hello, World!
</Data>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue></DigestValue>
</Reference>
</SignedInfo>
<SignatureValue/>
<KeyInfo>
<KeyName/>
</KeyInfo>
</Signature>
</Envelope>`)
if err != nil {
log.Printf("Failed to parse source XML: %s", err)
return
}
defer doc.Free()
if err := ctx.Sign(doc); err != nil {
log.Printf("Failed to sign document: %s", err)
return
}
log.Printf("%s", doc.Dump(true))
}
Caveats
cgo and pkg-config sometimes have problems with quoting. For example, on my local
machine (OS X 10.10.5 + go 1.5.1), I get this:
shoebill% go test .
# github.com/lestrrat-go/xmlsec
In file included from <built-in>:326:
<command line>:1:24: warning: missing terminating '"' character [-Winvalid-pp-token]
If it annoys you, explicitly specifying #cgo CFLAGS:
and #cgo LDFLAGS:
may help,
but we don't do that in this library because it makes it unportable.
See Also
Credits