validator.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package signedxml
  2. import (
  3. "crypto/x509"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "log"
  8. "github.com/beevik/etree"
  9. )
  10. // Validator provides options for verifying a signed XML document
  11. type Validator struct {
  12. Certificates []x509.Certificate
  13. signingCert x509.Certificate
  14. signatureData
  15. }
  16. // NewValidator returns a *Validator for the XML provided
  17. func NewValidator(xml string) (*Validator, error) {
  18. doc := etree.NewDocument()
  19. err := doc.ReadFromString(xml)
  20. if err != nil {
  21. return nil, err
  22. }
  23. v := &Validator{signatureData: signatureData{xml: doc}}
  24. return v, nil
  25. }
  26. // SetReferenceIDAttribute set the referenceIDAttribute
  27. func (v *Validator) SetReferenceIDAttribute(refIDAttribute string) {
  28. v.signatureData.refIDAttribute = refIDAttribute
  29. }
  30. // SetXML is used to assign the XML document that the Validator will verify
  31. func (v *Validator) SetXML(xml string) error {
  32. doc := etree.NewDocument()
  33. err := doc.ReadFromString(xml)
  34. v.xml = doc
  35. return err
  36. }
  37. // SigningCert returns the certificate, if any, that was used to successfully
  38. // validate the signature of the XML document. This will be a zero value
  39. // x509.Certificate before Validator.Validate is successfully called.
  40. func (v *Validator) SigningCert() x509.Certificate {
  41. return v.signingCert
  42. }
  43. // Validate validates the Reference digest values, and the signature value
  44. // over the SignedInfo.
  45. //
  46. // Deprecated: Use ValidateReferences instead
  47. func (v *Validator) Validate() error {
  48. _, err := v.ValidateReferences()
  49. return err
  50. }
  51. // ValidateReferences validates the Reference digest values, and the signature value
  52. // over the SignedInfo.
  53. //
  54. // If the signature is enveloped in the XML, then it will be used.
  55. // Otherwise, an external signature should be assigned using
  56. // Validator.SetSignature.
  57. //
  58. // The references returned by this method can be used to verify what was signed.
  59. func (v *Validator) ValidateReferences() ([]string, error) {
  60. if err := v.loadValuesFromXML(); err != nil {
  61. return nil, err
  62. }
  63. referenced, err := v.validateReferences()
  64. if err != nil {
  65. return nil, err
  66. }
  67. var ref []string
  68. for _, doc := range referenced {
  69. docStr, err := doc.WriteToString()
  70. if err != nil {
  71. return nil, err
  72. }
  73. ref = append(ref, docStr)
  74. }
  75. err = v.validateSignature()
  76. return ref, err
  77. }
  78. func (v *Validator) loadValuesFromXML() error {
  79. if v.signature == nil {
  80. if err := v.parseEnvelopedSignature(); err != nil {
  81. return err
  82. }
  83. }
  84. if err := v.parseSignedInfo(); err != nil {
  85. return err
  86. }
  87. if err := v.parseSigValue(); err != nil {
  88. return err
  89. }
  90. if err := v.parseSigAlgorithm(); err != nil {
  91. return err
  92. }
  93. if err := v.parseCanonAlgorithm(); err != nil {
  94. return err
  95. }
  96. if err := v.loadCertificates(); err != nil {
  97. return err
  98. }
  99. return nil
  100. }
  101. func (v *Validator) validateReferences() (referenced []*etree.Document, err error) {
  102. references := v.signedInfo.FindElements("./Reference")
  103. for _, ref := range references {
  104. doc := v.xml.Copy()
  105. transforms := ref.SelectElement("Transforms")
  106. for _, transform := range transforms.SelectElements("Transform") {
  107. doc, err = processTransform(transform, doc)
  108. if err != nil {
  109. return nil, err
  110. }
  111. }
  112. doc, err = v.getReferencedXML(ref, doc)
  113. if err != nil {
  114. return nil, err
  115. }
  116. referenced = append(referenced, doc)
  117. digestValueElement := ref.SelectElement("DigestValue")
  118. if digestValueElement == nil {
  119. return nil, errors.New("signedxml: unable to find DigestValue")
  120. }
  121. digestValue := digestValueElement.Text()
  122. calculatedValue, err := calculateHash(ref, doc)
  123. if err != nil {
  124. return nil, err
  125. }
  126. if calculatedValue != digestValue {
  127. return nil, fmt.Errorf("signedxml: Calculated digest does not match the"+
  128. " expected digestvalue of %s", digestValue)
  129. }
  130. }
  131. return referenced, nil
  132. }
  133. func (v *Validator) validateSignature() error {
  134. doc := etree.NewDocument()
  135. doc.SetRoot(v.signedInfo.Copy())
  136. signedInfo, err := doc.WriteToString()
  137. if err != nil {
  138. return err
  139. }
  140. canonSignedInfo, err := v.canonAlgorithm.Process(signedInfo, "")
  141. if err != nil {
  142. return err
  143. }
  144. b64, err := base64.StdEncoding.DecodeString(v.sigValue)
  145. if err != nil {
  146. return err
  147. }
  148. sig := []byte(b64)
  149. v.signingCert = x509.Certificate{}
  150. for _, cert := range v.Certificates {
  151. err := cert.CheckSignature(v.sigAlgorithm, []byte(canonSignedInfo), sig)
  152. if err == nil {
  153. v.signingCert = cert
  154. return nil
  155. }
  156. }
  157. return errors.New("signedxml: Calculated signature does not match the " +
  158. "SignatureValue provided")
  159. }
  160. func (v *Validator) loadCertificates() error {
  161. // If v.Certificates is already populated, then the client has already set it
  162. // to the desired cert. Otherwise, let's pull the public keys from the XML
  163. if len(v.Certificates) < 1 {
  164. keydata := v.xml.FindElements(".//X509Certificate")
  165. for _, key := range keydata {
  166. cert, err := getCertFromPEMString(key.Text())
  167. if err != nil {
  168. log.Printf("signedxml: Unable to load certificate: (%s). "+
  169. "Looking for another cert.", err)
  170. } else {
  171. v.Certificates = append(v.Certificates, *cert)
  172. }
  173. }
  174. }
  175. if len(v.Certificates) < 1 {
  176. return errors.New("signedxml: a certificate is required, but was not found")
  177. }
  178. return nil
  179. }