diff --git a/xmldsig/signature.go b/xmldsig/signature.go
index 9a1734c..800d45c 100644
--- a/xmldsig/signature.go
+++ b/xmldsig/signature.go
@@ -1,6 +1,10 @@
package xmldsig
-import "encoding/xml"
+import (
+ "encoding/base64"
+ "encoding/pem"
+ "encoding/xml"
+)
// Method is part of Signature.
type Method struct {
@@ -29,7 +33,7 @@ type Signature struct {
DigestMethod Method `xml:"SignedInfo>Reference>DigestMethod"`
DigestValue string `xml:"SignedInfo>Reference>DigestValue"`
SignatureValue string `xml:"SignatureValue"`
- KeyName string `xml:"KeyInfo>KeyName"`
+ KeyName string `xml:"KeyInfo>KeyName,omitempty"`
X509Certificate *SignatureX509Data `xml:"KeyInfo>X509Data,omitempty"`
}
@@ -38,7 +42,12 @@ type SignatureX509Data struct {
}
// DefaultSignature populates a default Signature that uses c14n and SHA1.
-func DefaultSignature() Signature {
+func DefaultSignature(pemEncodedPublicKey []byte) Signature {
+ // xmlsec wants the key to be base64-encoded but *not* wrapped with the
+ // PEM flags
+ pemBlock, _ := pem.Decode(pemEncodedPublicKey)
+ certStr := base64.StdEncoding.EncodeToString(pemBlock.Bytes)
+
return Signature{
CanonicalizationMethod: Method{
Algorithm: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
@@ -52,5 +61,8 @@ func DefaultSignature() Signature {
DigestMethod: Method{
Algorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
},
+ X509Certificate: &SignatureX509Data{
+ X509Certificate: certStr,
+ },
}
}
diff --git a/xmldsig/xmldsig.go b/xmldsig/xmldsig.go
index 13190fe..a159257 100644
--- a/xmldsig/xmldsig.go
+++ b/xmldsig/xmldsig.go
@@ -2,6 +2,7 @@ package xmldsig
import (
"errors"
+ "fmt"
"unsafe"
)
@@ -39,19 +40,45 @@ func init() {
}
func newContext(pemFormatKey []byte) (*C.xmlSecDSigCtx, error) {
- ctx := C.xmlSecDSigCtxCreate(nil)
+ keysMngr := C.xmlSecKeysMngrCreate()
+ if rv := C.xmlSecCryptoAppDefaultKeysMngrInit(keysMngr); rv < 0 {
+ return nil, fmt.Errorf("xmlSecCryptoAppDefaultKeysMngrInit: %d", rv)
+ }
+ ctx := C.xmlSecDSigCtxCreate(keysMngr)
if ctx == nil {
return nil, errors.New("failed to create signature context")
}
- ctx.signKey = C.xmlSecCryptoAppKeyLoadMemory(
+ key := 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")
+ if key == nil {
+ key = C.xmlSecKeyCreate()
+ if rv := C.xmlSecCryptoAppKeyCertLoadMemory(key,
+ (*C.xmlSecByte)(unsafe.Pointer(&pemFormatKey[0])),
+ C.xmlSecSize(len(pemFormatKey)),
+ C.xmlSecKeyDataFormatPem); rv < 0 {
+ return nil, fmt.Errorf("xmlSecCryptoAppKeyCertLoadMemory: %d", rv)
+ }
+ C.xmlSecCryptoAppDefaultKeysMngrAdoptKey(keysMngr, key)
}
+ ctx.signKey = key
+
+ /*
+ if rv := C.xmlSecCryptoAppKeysMngrCertLoadMemory(keysMngr,
+ (*C.xmlSecByte)(unsafe.Pointer(&pemFormatKey[0])),
+ C.xmlSecSize(len(pemFormatKey)),
+ C.xmlSecKeyDataFormatPem,
+ C.xmlSecKeyDataTypeTrusted); rv < 0 {
+
+ // Failed to load the key as a certificate, try as a key
+
+ ctx.
+ }
+ */
+
return ctx, nil
}
@@ -142,11 +169,42 @@ const (
// the key used to sign docStr. If the signature is not correct,
// this function returns ErrVarificationFailed.
func Verify(publicKey []byte, doc []byte) error {
- ctx, err := newContext(publicKey)
- if err != nil {
- return err
+ keysMngr := C.xmlSecKeysMngrCreate()
+ if keysMngr == nil {
+ return fmt.Errorf("xmlSecKeysMngrCreate failed")
}
- defer closeContext(ctx)
+ defer C.xmlSecKeysMngrDestroy(keysMngr)
+
+ if rv := C.xmlSecCryptoAppDefaultKeysMngrInit(keysMngr); rv < 0 {
+ return fmt.Errorf("xmlSecCryptoAppDefaultKeysMngrInit failed")
+ }
+
+ key := C.xmlSecCryptoAppKeyLoadMemory(
+ (*C.xmlSecByte)(unsafe.Pointer(&publicKey[0])),
+ C.xmlSecSize(len(publicKey)),
+ C.xmlSecKeyDataFormatCertPem,
+ nil, nil, nil)
+ if key == nil {
+ return fmt.Errorf("xmlSecCryptoAppKeyLoadMemory failed")
+ }
+
+ if rv := C.xmlSecCryptoAppKeyCertLoadMemory(key,
+ (*C.xmlSecByte)(unsafe.Pointer(&publicKey[0])),
+ C.xmlSecSize(len(publicKey)),
+ C.xmlSecKeyDataFormatCertPem); rv < 0 {
+ C.xmlSecKeyDestroy(key)
+ return fmt.Errorf("xmlSecCryptoAppKeyCertLoad failed")
+ }
+
+ if rv := C.xmlSecCryptoAppDefaultKeysMngrAdoptKey(keysMngr, key); rv < 0 {
+ return fmt.Errorf("xmlSecCryptoAppDefaultKeysMngrAdoptKey failed")
+ }
+
+ dsigCtx := C.xmlSecDSigCtxCreate(keysMngr)
+ if dsigCtx == nil {
+ return fmt.Errorf("xmlSecDSigCtxCreate failed")
+ }
+ defer C.xmlSecDSigCtxDestroy(dsigCtx)
parsedDoc, err := newDoc(doc)
if err != nil {
@@ -161,11 +219,11 @@ func Verify(publicKey []byte, doc []byte) error {
return errors.New("cannot find start node")
}
- if rv := C.xmlSecDSigCtxVerify(ctx, node); rv < 0 {
- return errors.New("failed to verify")
+ if rv := C.xmlSecDSigCtxVerify(dsigCtx, node); rv < 0 {
+ return ErrVerificationFailed
}
- if ctx.status != xmlSecDSigStatusSucceeded {
+ if dsigCtx.status != xmlSecDSigStatusSucceeded {
return ErrVerificationFailed
}
return nil
diff --git a/xmldsig/xmldsig_test.go b/xmldsig/xmldsig_test.go
index 71805e0..91dc614 100644
--- a/xmldsig/xmldsig_test.go
+++ b/xmldsig/xmldsig_test.go
@@ -12,22 +12,40 @@ type Envelope struct {
}
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-----`)
+ key := []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBANPQbQ92nlbeg1Q5JNHSO1Yey46nZ7GJltLWw1ccSvp7pnvmfUm+
+M521CpFpfr4EAE3UVBMoU9j/hqq3dFAc2H0CAwEAAQJBALFVCjmsAZyQ5jqZLO5N
+qEfNuHZSSUol+xPBogFIOq3BWa269eNNcAK5or5g0XWWon7EPdyGT4qyDVH9KzXK
+RLECIQDzm/Nj0epUGN51/rKJgRXWkXW/nfSCMO9fvQR6Ujoq3wIhAN6WeHK9vgWg
+wBWqMdq5sR211+LlDH7rOUQ6rBpbsoQjAiEA7jzpfglgPPZFOOfo+oh/LuP6X3a+
+FER/FQXpRyb7M8kCIETUrwZ8WkiPPxbz/Fqw1W5kjw/g2I5e2uSYaCP2eyuVAiEA
+mOI6RhRyMqgxQyy0plJVjG1s4fdu92AWYy9AwYeyd/8=
+-----END RSA PRIVATE KEY-----
+`)
+
+ cert := []byte(`-----BEGIN CERTIFICATE-----
+MIIDpzCCA1GgAwIBAgIJAK+ii7kzrdqvMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy
+aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEWMBQG
+A1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtz
+ZXkuY29tMCAXDTE0MDUyMzE3NTUzNFoYDzIxMTQwNDI5MTc1NTM0WjCBxzELMAkG
+A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1
+cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxKTAn
+BgNVBAsTIFRlc3QgVGhpcmQgTGV2ZWwgUlNBIENlcnRpZmljYXRlMRYwFAYDVQQD
+Ew1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5j
+b20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA09BtD3aeVt6DVDkk0dI7Vh7Ljqdn
+sYmW0tbDVxxK+nume+Z9Sb4znbUKkWl+vgQATdRUEyhT2P+Gqrd0UBzYfQIDAQAB
+o4IBRTCCAUEwDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
+ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNf0xkZ3zjcEI60pVPuwDqTM
+QygZMIHjBgNVHSMEgdswgdiAFP7k7FMk8JWVxxC14US1XTllWuN+oYG0pIGxMIGu
+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1M
+IFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2Vj
+KTEQMA4GA1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8G
+CSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggkAr6KLuTOt2q0wDQYJKoZI
+hvcNAQEFBQADQQAOXBj0yICp1RmHXqnUlsppryLCW3pKBD1dkb4HWarO7RjA1yJJ
+fBjXssrERn05kpBcrRfzou4r3DCgQFPhjxga
+-----END CERTIFICATE-----
+`)
docStr := []byte(`
\n\n \n\tHello, World!\n \n \n \n \n \n \n \n \n \n \n 9H/rQr2Axe9hYTV2n/tCp+3UIQQ=\n \n \n 2rM7C8ZzCjxEY4kueUaSevvEZjORQ7hBTWGxUJXStyQScLtX1drFx9dRmUdk/uRr\n0O37B3gsbKzlpQNfdVYPIfWgswjEVLBH7Ncl1dJ6dTofkQrogIF5CQE+PIAG3MPh\nnWsIcBahRQ+rNaRB/TDscuEV3+V3Je4K7E0OEKEuP1I=\n \n\t\n \n \n\n"
-
+
+`)
+ expectedSignedString := `
+
+
+
+ Hello, World!
+
+
+
+
+
+
+
+
+
+
+ 9H/rQr2Axe9hYTV2n/tCp+3UIQQ=
+
+
+ fDKK0so/zFcmmq2X+BaVFmS0t8KB7tyW53YN6n221OArzGCs4OyWsAjj/BUR+wNF
+elOnt4fo2gPK1a3IVEhMGg==
+
+
+ MIIDpzCCA1GgAwIBAgIJAK+ii7kzrdqvMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy
+aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEWMBQG
+A1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtz
+ZXkuY29tMCAXDTE0MDUyMzE3NTUzNFoYDzIxMTQwNDI5MTc1NTM0WjCBxzELMAkG
+A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1
+cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxKTAn
+BgNVBAsTIFRlc3QgVGhpcmQgTGV2ZWwgUlNBIENlcnRpZmljYXRlMRYwFAYDVQQD
+Ew1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5j
+b20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA09BtD3aeVt6DVDkk0dI7Vh7Ljqdn
+sYmW0tbDVxxK+nume+Z9Sb4znbUKkWl+vgQATdRUEyhT2P+Gqrd0UBzYfQIDAQAB
+o4IBRTCCAUEwDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
+ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNf0xkZ3zjcEI60pVPuwDqTM
+QygZMIHjBgNVHSMEgdswgdiAFP7k7FMk8JWVxxC14US1XTllWuN+oYG0pIGxMIGu
+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1M
+IFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2Vj
+KTEQMA4GA1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8G
+CSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggkAr6KLuTOt2q0wDQYJKoZI
+hvcNAQEFBQADQQAOXBj0yICp1RmHXqnUlsppryLCW3pKBD1dkb4HWarO7RjA1yJJ
+fBjXssrERn05kpBcrRfzou4r3DCgQFPhjxga
+
+
+
+
+`
actualSignedString, err := Sign(key, docStr)
if err != nil {
t.Errorf("sign: %s", err)
@@ -65,14 +151,14 @@ XML Security Library example: Simple signature template file for sign1 example.
}
if string(actualSignedString) != expectedSignedString {
- t.Errorf("signed: expected %q, got `%q`", expectedSignedString, string(actualSignedString))
+ t.Errorf("sign: expected:\n%q\ngot:\n%q", expectedSignedString, string(actualSignedString))
return
}
// Try again but this time construct the message from a struct having a Signature member
doc := Envelope{Data: "Hello, World!"}
- doc.Signature = DefaultSignature()
- docStr, err = xml.Marshal(doc)
+ doc.Signature = DefaultSignature(cert)
+ docStr, err = xml.MarshalIndent(doc, "", " ")
if err != nil {
t.Errorf("marshal: %s", err)
}
@@ -81,19 +167,44 @@ XML Security Library example: Simple signature template file for sign1 example.
t.Errorf("sign: %s", err)
return
}
- expectedSignedString = "\nHello, World!09XOMG8zghPZhJYD8kM2uJsr1cc=fqL9oHtcNiFFaTy7AJoQ1hs5Wz0fTqjq0xANLz/mSLBLiFv2OEicuwyo4InyBnyf\njSjmCBaz8QPX9rTW49a2wv1RMkls0WnqP65DUY2ofM4wKHWcjnGt1p1rlYdDv5Sl\njk5Wqwy2EmoqGSXQovRZmn4jidThmoqgum4LNKC2lFI=\n"
+
+ expectedSignedString = `
+
+ Hello, World!
+
+
+
+
+
+
+
+
+
+ sEenIPkW9ssFSB9t4UU6VUrytqc=
+
+
+ chSWfpQBIQraySsUHzs5N51+ruelu2HMHh5Mnd3EjcLqFBVD0f23kmXUp7zVhCVD
+vCfqu9yXDYKVOBI57F0Efg==
+
+
+ MIIDpzCCA1GgAwIBAgIJAK+ii7kzrdqvMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMCAXDTE0MDUyMzE3NTUzNFoYDzIxMTQwNDI5MTc1NTM0WjCBxzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxKTAnBgNVBAsTIFRlc3QgVGhpcmQgTGV2ZWwgUlNBIENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA09BtD3aeVt6DVDkk0dI7Vh7LjqdnsYmW0tbDVxxK+nume+Z9Sb4znbUKkWl+vgQATdRUEyhT2P+Gqrd0UBzYfQIDAQABo4IBRTCCAUEwDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNf0xkZ3zjcEI60pVPuwDqTMQygZMIHjBgNVHSMEgdswgdiAFP7k7FMk8JWVxxC14US1XTllWuN+oYG0pIGxMIGuMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEQMA4GA1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggkAr6KLuTOt2q0wDQYJKoZIhvcNAQEFBQADQQAOXBj0yICp1RmHXqnUlsppryLCW3pKBD1dkb4HWarO7RjA1yJJfBjXssrERn05kpBcrRfzou4r3DCgQFPhjxga
+
+
+
+
+`
if string(actualSignedString) != expectedSignedString {
t.Errorf("signed: expected %q, got `%q`", expectedSignedString, string(actualSignedString))
return
}
- if err := Verify(key, []byte(expectedSignedString)); err != nil {
+ if err := Verify(cert, actualSignedString); err != nil {
t.Errorf("verify: %s", err)
return
}
brokenDoc := strings.Replace(expectedSignedString, "Hello", "Goodbye", 1)
- err = Verify(key, []byte(brokenDoc))
+ err = Verify(cert, []byte(brokenDoc))
if err != ErrVerificationFailed {
t.Errorf("verify: expected verification failed, got %s", err)
return