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