1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package xmldsig
20
21 import (
22 "crypto"
23 "crypto/ecdsa"
24 "crypto/rand"
25 "crypto/rsa"
26 "crypto/x509"
27 "encoding/base64"
28 "errors"
29 "fmt"
30 "math/big"
31
32 "github.com/sassoftware/relic/lib/x509tools"
33
34 "github.com/beevik/etree"
35 )
36
37 type SignOptions struct {
38
39 MsCompatHashNames bool
40
41 UseRecC14n bool
42
43 IncludeX509 bool
44
45 IncludeKeyValue bool
46 }
47
48 func (s SignOptions) c14nNamespace() string {
49 if s.UseRecC14n {
50 return AlgXMLExcC14nRec
51 } else {
52 return AlgXMLExcC14n
53 }
54 }
55
56
57
58 func Sign(root, parent *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) error {
59 pubKey := privKey.Public()
60 if len(certs) < 1 || !x509tools.SameKey(pubKey, certs[0].PublicKey) {
61 return errors.New("xmldsig: first certificate must match private key")
62 }
63 RemoveElements(parent, "Signature")
64
65 refDigest, err := hashCanon(root, hash)
66 hashAlg, sigAlg, err := hashAlgs(hash, pubKey, opts)
67 if err != nil {
68 return err
69 }
70
71 signature := parent.CreateElement("Signature")
72 signature.CreateAttr("xmlns", NsXMLDsig)
73 signedinfo := buildSignedInfo(signature, "", hashAlg, sigAlg, refDigest, opts)
74 return finishSignature(signature, signedinfo, hash, privKey, certs, opts)
75 }
76
77
78 func SignEnveloping(object *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) (*etree.Element, error) {
79 pubKey := privKey.Public()
80 if len(certs) < 1 || !x509tools.SameKey(pubKey, certs[0].PublicKey) {
81 return nil, errors.New("xmldsig: first certificate must match private key")
82 }
83
84
85 signature := etree.NewElement("Signature")
86 signature.CreateAttr("xmlns", NsXMLDsig)
87 signature.AddChild(object)
88 refDigest, err := hashCanon(object, hash)
89 hashAlg, sigAlg, err := hashAlgs(hash, pubKey, opts)
90 if err != nil {
91 return nil, err
92 }
93 if object.Tag != "Object" {
94 return nil, errors.New("object must have tag \"Object\"")
95 }
96 refId := object.SelectAttrValue("Id", "")
97 if refId == "" {
98 return nil, errors.New("object lacks an Id attribute")
99 }
100
101 signedinfo := buildSignedInfo(signature, refId, hashAlg, sigAlg, refDigest, opts)
102
103 if err := finishSignature(signature, signedinfo, hash, privKey, certs, opts); err != nil {
104 return nil, err
105 }
106 signature.RemoveChild(object)
107 signature.AddChild(object)
108 return signature, nil
109 }
110
111 func buildSignedInfo(signature *etree.Element, refId, hashAlg, sigAlg string, refDigest []byte, opts SignOptions) *etree.Element {
112 signedinfo := signature.CreateElement("SignedInfo")
113 signedinfo.CreateElement("CanonicalizationMethod").CreateAttr("Algorithm", opts.c14nNamespace())
114 signedinfo.CreateElement("SignatureMethod").CreateAttr("Algorithm", sigAlg)
115 reference := signedinfo.CreateElement("Reference")
116 if refId == "" {
117 reference.CreateAttr("URI", "")
118 } else {
119 reference.CreateAttr("URI", "#"+refId)
120 reference.CreateAttr("Type", NsXMLDsig+"Object")
121 }
122 transforms := reference.CreateElement("Transforms")
123 if refId == "" {
124 transforms.CreateElement("Transform").CreateAttr("Algorithm", AlgDsigEnvelopedSignature)
125 }
126 transforms.CreateElement("Transform").CreateAttr("Algorithm", opts.c14nNamespace())
127 reference.CreateElement("DigestMethod").CreateAttr("Algorithm", hashAlg)
128 reference.CreateElement("DigestValue").SetText(base64.StdEncoding.EncodeToString(refDigest))
129 return signedinfo
130 }
131
132 func finishSignature(signature, signedinfo *etree.Element, hash crypto.Hash, privKey crypto.Signer, certs []*x509.Certificate, opts SignOptions) error {
133 siDigest, err := hashCanon(signedinfo, hash)
134 sig, err := privKey.Sign(rand.Reader, siDigest, hash)
135 if err != nil {
136 return err
137 }
138
139 if _, ok := privKey.Public().(*ecdsa.PublicKey); ok {
140
141 esig, err := x509tools.UnmarshalEcdsaSignature(sig)
142 if err != nil {
143 return err
144 }
145 sig = esig.Pack()
146 }
147 signature.CreateElement("SignatureValue").SetText(base64.StdEncoding.EncodeToString(sig))
148 keyinfo := etree.NewElement("KeyInfo")
149 if opts.IncludeKeyValue {
150 if err := addKeyInfo(keyinfo, privKey.Public()); err != nil {
151 return err
152 }
153 }
154 if opts.IncludeX509 && len(certs) > 0 {
155 addCerts(keyinfo, certs)
156 }
157 if len(keyinfo.Child) > 0 {
158 signature.AddChild(keyinfo)
159 }
160 return nil
161 }
162
163 func hashCanon(root *etree.Element, hash crypto.Hash) ([]byte, error) {
164 canon, err := SerializeCanonical(root)
165 if err != nil {
166 return nil, fmt.Errorf("xmldsig: %s", err)
167 }
168 d := hash.New()
169 d.Write(canon)
170 return d.Sum(nil), nil
171 }
172
173
174 func RemoveElements(root *etree.Element, tag string) {
175 for i := 0; i < len(root.Child); {
176 token := root.Child[i]
177 if elem, ok := token.(*etree.Element); ok && elem.Tag == tag {
178 root.Child = append(root.Child[:i], root.Child[i+1:]...)
179 } else {
180 i++
181 }
182 }
183 }
184
185
186 func hashAlgs(hash crypto.Hash, pubKey crypto.PublicKey, opts SignOptions) (string, string, error) {
187 hashName := hashNames[hash]
188 if hashName == "" {
189 return "", "", errors.New("unsupported hash type")
190 }
191 var pubName string
192 switch pubKey.(type) {
193 case *rsa.PublicKey:
194 pubName = "rsa"
195 case *ecdsa.PublicKey:
196 pubName = "ecdsa"
197 default:
198 return "", "", errors.New("unsupported key type")
199 }
200 var hashAlg, sigAlg string
201 if opts.MsCompatHashNames {
202 hashAlg = NsXMLDsig + hashName
203 } else {
204 hashAlg = HashUris[hash]
205 }
206 if pubName == "rsa" && (hashName == "sha1" || opts.MsCompatHashNames) {
207 sigAlg = NsXMLDsig + pubName + "-" + hashName
208 } else {
209 sigAlg = NsXMLDsigMore + pubName + "-" + hashName
210 }
211 return hashAlg, sigAlg, nil
212 }
213
214
215 func addKeyInfo(keyinfo *etree.Element, pubKey crypto.PublicKey) error {
216 keyvalue := keyinfo.CreateElement("KeyValue")
217 switch k := pubKey.(type) {
218 case *rsa.PublicKey:
219 e := big.NewInt(int64(k.E))
220 rkv := keyvalue.CreateElement("RSAKeyValue")
221 rkv.CreateElement("Modulus").SetText(base64.StdEncoding.EncodeToString(k.N.Bytes()))
222 rkv.CreateElement("Exponent").SetText(base64.StdEncoding.EncodeToString(e.Bytes()))
223 case *ecdsa.PublicKey:
224 curve, err := x509tools.CurveByCurve(k.Curve)
225 if err != nil {
226 return err
227 }
228 curveUrn := fmt.Sprintf("urn:oid:%s", curve.Oid)
229 ekv := keyvalue.CreateElement("ECDSAKeyValue")
230 ekv.CreateElement("DomainParameters").CreateElement("NamedCurve").CreateAttr("URN", curveUrn)
231 pk := ekv.CreateElement("PublicKey")
232 x := pk.CreateElement("X")
233 x.CreateAttr("Value", k.X.String())
234 x.CreateAttr("xmlns:xsi", NsXsi)
235 x.CreateAttr("xsi:type", "PrimeFieldElemType")
236 y := pk.CreateElement("Y")
237 y.CreateAttr("Value", k.Y.String())
238 y.CreateAttr("xmlns:xsi", NsXsi)
239 y.CreateAttr("xsi:type", "PrimeFieldElemType")
240 default:
241 return errors.New("unsupported key type")
242 }
243 return nil
244 }
245
246 func addCerts(keyinfo *etree.Element, certs []*x509.Certificate) {
247 x509data := keyinfo.CreateElement("X509Data")
248 for _, cert := range certs {
249 x509data.CreateElement("X509Certificate").SetText(base64.StdEncoding.EncodeToString(cert.Raw))
250 }
251 }
252
View as plain text