initial commit, xmldsig support
This commit is contained in:
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libxml2-dev
|
||||||
|
- libxmlsec1-dev
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
52
README.md
Normal file
52
README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# go-xmlsec
|
||||||
|
|
||||||
|
A (partial) wrapper for [xmlsec](https://www.aleksey.com/xmlsec).
|
||||||
|
|
||||||
|
## Signing Example
|
||||||
|
|
||||||
|
key := []byte(`-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9Lc
|
||||||
|
GorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6w
|
||||||
|
sKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786Uluj
|
||||||
|
Tvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1
|
||||||
|
ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXB
|
||||||
|
Bgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6Rn
|
||||||
|
fdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi
|
||||||
|
+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3
|
||||||
|
Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL
|
||||||
|
2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXz
|
||||||
|
VYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRX
|
||||||
|
Eouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKC
|
||||||
|
Un0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x
|
||||||
|
9+QU/vJr7lMLzdoy
|
||||||
|
-----END PRIVATE KEY-----`)
|
||||||
|
|
||||||
|
docStr := `<?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>`
|
||||||
|
|
||||||
|
signedDoc, err := xmldsig.Sign(key, doc)
|
||||||
|
os.Stdout.Write(signedDoc)
|
||||||
163
xmldsig/xmldsig.go
Normal file
163
xmldsig/xmldsig.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package xmldsig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Note: on mac you need: brew install libxmlsec1 libxml2
|
||||||
|
|
||||||
|
// #cgo pkg-config: xmlsec1
|
||||||
|
// #include <xmlsec/xmlsec.h>
|
||||||
|
// #include <xmlsec/xmltree.h>
|
||||||
|
// #include <xmlsec/xmldsig.h>
|
||||||
|
// #include <xmlsec/crypto.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// #cgo pkg-config: libxml-2.0
|
||||||
|
// #include "libxml/parser.h"
|
||||||
|
// #include "libxml/parserInternals.h"
|
||||||
|
// #include "libxml/xmlmemory.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
C.xmlInitParser()
|
||||||
|
|
||||||
|
if rv := C.xmlSecInit(); rv < 0 {
|
||||||
|
panic("xmlsec failed to initialize")
|
||||||
|
}
|
||||||
|
if rv := C.xmlSecCryptoAppInit(nil); rv < 0 {
|
||||||
|
panic("xmlsec crypto initialization failed.")
|
||||||
|
}
|
||||||
|
if rv := C.xmlSecCryptoInit(); rv < 0 {
|
||||||
|
panic("xmlsec crypto initialization failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext(pemFormatKey []byte) (*C.xmlSecDSigCtx, error) {
|
||||||
|
ctx := C.xmlSecDSigCtxCreate(nil)
|
||||||
|
if ctx == nil {
|
||||||
|
return nil, errors.New("failed to create signature context")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.signKey = C.xmlSecCryptoAppKeyLoadMemory(
|
||||||
|
(*C.xmlSecByte)(unsafe.Pointer(&pemFormatKey[0])),
|
||||||
|
C.xmlSecSize(len(pemFormatKey)),
|
||||||
|
C.xmlSecKeyDataFormatPem,
|
||||||
|
nil, nil, nil)
|
||||||
|
if ctx.signKey == nil {
|
||||||
|
return nil, errors.New("failed to load pem key")
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeContext(ctx *C.xmlSecDSigCtx) {
|
||||||
|
C.xmlSecDSigCtxDestroy(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDoc(s string) (*C.xmlDoc, error) {
|
||||||
|
ctx := C.xmlCreateMemoryParserCtxt(C.CString(s), C.int(len(s)))
|
||||||
|
if ctx == nil {
|
||||||
|
return nil, errors.New("error creating parser")
|
||||||
|
}
|
||||||
|
defer C.xmlFreeParserCtxt(ctx)
|
||||||
|
|
||||||
|
//C.xmlCtxtUseOptions(ctx, C.int(p.Options))
|
||||||
|
C.xmlParseDocument(ctx)
|
||||||
|
|
||||||
|
if ctx.wellFormed == C.int(0) {
|
||||||
|
return nil, errors.New("malformed XML")
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := ctx.myDoc
|
||||||
|
if doc == nil {
|
||||||
|
return nil, errors.New("parse failed")
|
||||||
|
}
|
||||||
|
return doc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpDoc(doc *C.xmlDoc) string {
|
||||||
|
var buffer *C.xmlChar
|
||||||
|
var bufferSize C.int
|
||||||
|
C.xmlDocDumpMemory(doc, &buffer, &bufferSize)
|
||||||
|
rv := C.GoStringN((*C.char)(unsafe.Pointer(buffer)), bufferSize)
|
||||||
|
C.xmlMemFree(unsafe.Pointer(buffer))
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeDoc(doc *C.xmlDoc) {
|
||||||
|
C.xmlFreeDoc(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns a version of docStr signed with key according to
|
||||||
|
// the XML-DSIG standard. docStr is a template document meaning
|
||||||
|
// that it contains a `Signature` element in the
|
||||||
|
// http://www.w3.org/2000/09/xmldsig# namespace.
|
||||||
|
func Sign(key []byte, docStr string) (string, error) {
|
||||||
|
ctx, err := newContext(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer closeContext(ctx)
|
||||||
|
|
||||||
|
doc, err := newDoc(docStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer closeDoc(doc)
|
||||||
|
|
||||||
|
node := C.xmlSecFindNode(C.xmlDocGetRootElement(doc),
|
||||||
|
(*C.xmlChar)(unsafe.Pointer(&C.xmlSecNodeSignature)),
|
||||||
|
(*C.xmlChar)(unsafe.Pointer(&C.xmlSecDSigNs)))
|
||||||
|
if node == nil {
|
||||||
|
return "", errors.New("cannot find start node")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv := C.xmlSecDSigCtxSign(ctx, node); rv < 0 {
|
||||||
|
return "", errors.New("failed to sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
return dumpDoc(doc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrVerificationFailed = errors.New("signature verification failed")
|
||||||
|
|
||||||
|
const (
|
||||||
|
xmlSecDSigStatusUnknown = 0
|
||||||
|
xmlSecDSigStatusSucceeded = 1
|
||||||
|
xmlSecDSigStatusInvalid = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify checks that the signature in docStr is valid according
|
||||||
|
// to the XML-DSIG specification. publicKey is the public part of
|
||||||
|
// the key used to sign docStr. If the signature is not correct,
|
||||||
|
// this function returns ErrVarificationFailed.
|
||||||
|
func Verify(publicKey []byte, docStr string) error {
|
||||||
|
ctx, err := newContext(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closeContext(ctx)
|
||||||
|
|
||||||
|
doc, err := newDoc(docStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closeDoc(doc)
|
||||||
|
|
||||||
|
node := C.xmlSecFindNode(C.xmlDocGetRootElement(doc),
|
||||||
|
(*C.xmlChar)(unsafe.Pointer(&C.xmlSecNodeSignature)),
|
||||||
|
(*C.xmlChar)(unsafe.Pointer(&C.xmlSecDSigNs)))
|
||||||
|
if node == nil {
|
||||||
|
return errors.New("cannot find start node")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv := C.xmlSecDSigCtxVerify(ctx, node); rv < 0 {
|
||||||
|
return errors.New("failed to verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.status != xmlSecDSigStatusSucceeded {
|
||||||
|
return ErrVerificationFailed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
77
xmldsig/xmldsig_test.go
Normal file
77
xmldsig/xmldsig_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package xmldsig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
key := []byte(`-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9Lc
|
||||||
|
GorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6w
|
||||||
|
sKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786Uluj
|
||||||
|
Tvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1
|
||||||
|
ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXB
|
||||||
|
Bgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6Rn
|
||||||
|
fdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi
|
||||||
|
+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3
|
||||||
|
Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL
|
||||||
|
2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXz
|
||||||
|
VYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRX
|
||||||
|
Eouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKC
|
||||||
|
Un0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x
|
||||||
|
9+QU/vJr7lMLzdoy
|
||||||
|
-----END PRIVATE KEY-----`)
|
||||||
|
|
||||||
|
docStr := `<?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>`
|
||||||
|
|
||||||
|
expectedSignedString := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\nXML Security Library example: Simple signature template file for sign1 example.\n-->\n<Envelope xmlns=\"urn:envelope\">\n <Data>\n\tHello, World!\n </Data>\n <Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n <SignedInfo>\n <CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/>\n <SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n <Reference URI=\"\">\n <Transforms>\n <Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\n </Transforms>\n <DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\n <DigestValue>9H/rQr2Axe9hYTV2n/tCp+3UIQQ=</DigestValue>\n </Reference>\n </SignedInfo>\n <SignatureValue>2rM7C8ZzCjxEY4kueUaSevvEZjORQ7hBTWGxUJXStyQScLtX1drFx9dRmUdk/uRr\n0O37B3gsbKzlpQNfdVYPIfWgswjEVLBH7Ncl1dJ6dTofkQrogIF5CQE+PIAG3MPh\nnWsIcBahRQ+rNaRB/TDscuEV3+V3Je4K7E0OEKEuP1I=</SignatureValue>\n <KeyInfo>\n\t<KeyName/>\n </KeyInfo>\n </Signature>\n</Envelope>\n"
|
||||||
|
|
||||||
|
actualSignedString, err := Sign(key, docStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("sign: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if actualSignedString != expectedSignedString {
|
||||||
|
t.Errorf("signed: expected %q, got `%q`", expectedSignedString, actualSignedString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Verify(key, expectedSignedString); err != nil {
|
||||||
|
t.Errorf("verify: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
brokenDoc := strings.Replace(expectedSignedString, "Hello", "Goodbye", 1)
|
||||||
|
err = Verify(key, brokenDoc)
|
||||||
|
if err != ErrVerificationFailed {
|
||||||
|
t.Errorf("verify: expected verification failed, got %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user