signer.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package signedxml
  2. import (
  3. "crypto"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/x509"
  7. "encoding/base64"
  8. "errors"
  9. "github.com/beevik/etree"
  10. )
  11. var signingAlgorithms map[x509.SignatureAlgorithm]cryptoHash
  12. func init() {
  13. signingAlgorithms = map[x509.SignatureAlgorithm]cryptoHash{
  14. // MD2 not supported
  15. // x509.MD2WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.MD2},
  16. x509.MD5WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.MD5},
  17. x509.SHA1WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA1},
  18. x509.SHA256WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA256},
  19. x509.SHA384WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA384},
  20. x509.SHA512WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA512},
  21. // DSA not supported
  22. // x509.DSAWithSHA1: cryptoHash{algorithm: "dsa", hash: crypto.SHA1},
  23. // x509.DSAWithSHA256:cryptoHash{algorithm: "dsa", hash: crypto.SHA256},
  24. // Golang ECDSA support is lacking, can't seem to load private keys
  25. // x509.ECDSAWithSHA1: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA1},
  26. // x509.ECDSAWithSHA256: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA256},
  27. // x509.ECDSAWithSHA384: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA384},
  28. // x509.ECDSAWithSHA512: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA512},
  29. }
  30. }
  31. type cryptoHash struct {
  32. algorithm string
  33. hash crypto.Hash
  34. }
  35. // Signer provides options for signing an XML document
  36. type Signer struct {
  37. signatureData
  38. privateKey interface{}
  39. }
  40. // NewSigner returns a *Signer for the XML provided
  41. func NewSigner(xml string) (*Signer, error) {
  42. doc := etree.NewDocument()
  43. err := doc.ReadFromString(xml)
  44. if err != nil {
  45. return nil, err
  46. }
  47. s := &Signer{signatureData: signatureData{xml: doc}}
  48. return s, nil
  49. }
  50. // Sign populates the XML digest and signature based on the parameters present and privateKey given
  51. func (s *Signer) Sign(privateKey interface{}) (string, error) {
  52. s.privateKey = privateKey
  53. if s.signature == nil {
  54. if err := s.parseEnvelopedSignature(); err != nil {
  55. return "", err
  56. }
  57. }
  58. if err := s.parseSignedInfo(); err != nil {
  59. return "", err
  60. }
  61. if err := s.parseSigAlgorithm(); err != nil {
  62. return "", err
  63. }
  64. if err := s.parseCanonAlgorithm(); err != nil {
  65. return "", err
  66. }
  67. if err := s.setDigest(); err != nil {
  68. return "", err
  69. }
  70. if err := s.setSignature(); err != nil {
  71. return "", err
  72. }
  73. xml, err := s.xml.WriteToString()
  74. if err != nil {
  75. return "", err
  76. }
  77. return xml, nil
  78. }
  79. // SetReferenceIDAttribute set the referenceIDAttribute
  80. func (s *Signer) SetReferenceIDAttribute(refIDAttribute string) {
  81. s.signatureData.refIDAttribute = refIDAttribute
  82. }
  83. func (s *Signer) setDigest() (err error) {
  84. references := s.signedInfo.FindElements("./Reference")
  85. for _, ref := range references {
  86. doc := s.xml.Copy()
  87. transforms := ref.SelectElement("Transforms")
  88. for _, transform := range transforms.SelectElements("Transform") {
  89. doc, err = processTransform(transform, doc)
  90. if err != nil {
  91. return err
  92. }
  93. }
  94. doc, err := s.getReferencedXML(ref, doc)
  95. if err != nil {
  96. return err
  97. }
  98. calculatedValue, err := calculateHash(ref, doc)
  99. if err != nil {
  100. return err
  101. }
  102. digestValueElement := ref.SelectElement("DigestValue")
  103. if digestValueElement == nil {
  104. return errors.New("signedxml: unable to find DigestValue")
  105. }
  106. digestValueElement.SetText(calculatedValue)
  107. }
  108. return nil
  109. }
  110. func (s *Signer) setSignature() error {
  111. doc := etree.NewDocument()
  112. doc.SetRoot(s.signedInfo.Copy())
  113. signedInfo, err := doc.WriteToString()
  114. if err != nil {
  115. return err
  116. }
  117. canonSignedInfo, err := s.canonAlgorithm.Process(signedInfo, "")
  118. if err != nil {
  119. return err
  120. }
  121. var hashed, signature []byte
  122. //var h1, h2 *big.Int
  123. signingAlgorithm, ok := signingAlgorithms[s.sigAlgorithm]
  124. if !ok {
  125. return errors.New("signedxml: unsupported algorithm")
  126. }
  127. hasher := signingAlgorithm.hash.New()
  128. hasher.Write([]byte(canonSignedInfo))
  129. hashed = hasher.Sum(nil)
  130. switch signingAlgorithm.algorithm {
  131. case "rsa":
  132. signature, err = rsa.SignPKCS1v15(rand.Reader, s.privateKey.(*rsa.PrivateKey), signingAlgorithm.hash, hashed)
  133. /*
  134. case "dsa":
  135. h1, h2, err = dsa.Sign(rand.Reader, s.privateKey.(*dsa.PrivateKey), hashed)
  136. case "ecdsa":
  137. h1, h2, err = ecdsa.Sign(rand.Reader, s.privateKey.(*ecdsa.PrivateKey), hashed)
  138. */
  139. }
  140. if err != nil {
  141. return err
  142. }
  143. // DSA and ECDSA has not been validated
  144. /*
  145. if signature == nil && h1 != nil && h2 != nil {
  146. signature = append(h1.Bytes(), h2.Bytes()...)
  147. }
  148. */
  149. b64 := base64.StdEncoding.EncodeToString(signature)
  150. sigValueElement := s.signature.SelectElement("SignatureValue")
  151. sigValueElement.SetText(b64)
  152. return nil
  153. }