1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.IdentityModel
6 {
7     using System.Diagnostics;
8     using System.IO;
9     using System.Security.Cryptography;
10     using System.IdentityModel.Tokens;
11     using System.Text;
12     using System.Xml;
13 
14     sealed class PreDigestedSignedInfo : SignedInfo
15     {
16         const int InitialReferenceArraySize = 8;
17         bool addEnvelopedSignatureTransform;
18         int count;
19         string digestMethod;
20         XmlDictionaryString digestMethodDictionaryString;
21         ReferenceEntry[] references;
22 
PreDigestedSignedInfo(DictionaryManager dictionaryManager)23         public PreDigestedSignedInfo(DictionaryManager dictionaryManager)
24             : base(dictionaryManager)
25         {
26             this.references = new ReferenceEntry[InitialReferenceArraySize];
27         }
28 
PreDigestedSignedInfo(DictionaryManager dictionaryManager, string canonicalizationMethod, XmlDictionaryString canonicalizationMethodDictionaryString, string digestMethod, XmlDictionaryString digestMethodDictionaryString, string signatureMethod, XmlDictionaryString signatureMethodDictionaryString)29         public PreDigestedSignedInfo(DictionaryManager dictionaryManager, string canonicalizationMethod, XmlDictionaryString canonicalizationMethodDictionaryString, string digestMethod, XmlDictionaryString digestMethodDictionaryString, string signatureMethod, XmlDictionaryString signatureMethodDictionaryString)
30             : base(dictionaryManager)
31         {
32             this.references = new ReferenceEntry[InitialReferenceArraySize];
33             this.CanonicalizationMethod = canonicalizationMethod;
34             this.CanonicalizationMethodDictionaryString = canonicalizationMethodDictionaryString;
35             this.DigestMethod = digestMethod;
36             this.digestMethodDictionaryString = digestMethodDictionaryString;
37             this.SignatureMethod = signatureMethod;
38             this.SignatureMethodDictionaryString = signatureMethodDictionaryString;
39         }
40 
41         public bool AddEnvelopedSignatureTransform
42         {
43             get { return this.addEnvelopedSignatureTransform; }
44             set { this.addEnvelopedSignatureTransform = value; }
45         }
46 
47         public string DigestMethod
48         {
49             get { return this.digestMethod; }
50             set { this.digestMethod = value; }
51         }
52 
53         public override int ReferenceCount
54         {
55             get { return this.count; }
56         }
57 
AddReference(string id, byte[] digest)58         public void AddReference(string id, byte[] digest)
59         {
60             AddReference(id, digest, false);
61         }
62 
AddReference(string id, byte[] digest, bool useStrTransform)63         public void AddReference(string id, byte[] digest, bool useStrTransform)
64         {
65             if (this.count == this.references.Length)
66             {
67                 ReferenceEntry[] newReferences = new ReferenceEntry[this.references.Length * 2];
68                 Array.Copy(this.references, 0, newReferences, 0, this.count);
69                 this.references = newReferences;
70             }
71             this.references[this.count++].Set(id, digest, useStrTransform);
72         }
73 
ComputeHash(HashStream hashStream)74         protected override void ComputeHash(HashStream hashStream)
75         {
76             if (this.AddEnvelopedSignatureTransform)
77             {
78                 base.ComputeHash(hashStream);
79             }
80             else
81             {
82                 SignedInfoCanonicalFormWriter.Instance.WriteSignedInfoCanonicalForm(
83                     hashStream, this.SignatureMethod, this.DigestMethod,
84                     this.references, this.count,
85                     this.ResourcePool.TakeEncodingBuffer(), this.ResourcePool.TakeBase64Buffer());
86             }
87         }
88 
ComputeReferenceDigests()89         public override void ComputeReferenceDigests()
90         {
91             // all digests pre-computed
92         }
93 
ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager)94         public override void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager)
95         {
96             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); // sender side use only
97         }
98 
EnsureAllReferencesVerified()99         public override void EnsureAllReferencesVerified()
100         {
101             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); // sender side use only
102         }
103 
EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource)104         public override bool EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource)
105         {
106             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); // sender side use only
107         }
108 
WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)109         public override void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
110         {
111             string prefix = XmlSignatureStrings.Prefix;
112             XmlDictionaryString ns = dictionaryManager.XmlSignatureDictionary.Namespace;
113 
114             writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.SignedInfo, ns);
115             if (this.Id != null)
116             {
117                 writer.WriteAttributeString(dictionaryManager.UtilityDictionary.IdAttribute, null, this.Id);
118             }
119             WriteCanonicalizationMethod(writer, dictionaryManager);
120             WriteSignatureMethod(writer, dictionaryManager);
121             for (int i = 0; i < this.count; i++)
122             {
123                 writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.Reference, ns);
124                 writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.URI, null);
125                 writer.WriteString("#");
126                 writer.WriteString(this.references[i].id);
127                 writer.WriteEndAttribute();
128 
129                 writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.Transforms, ns);
130                 if (this.addEnvelopedSignatureTransform)
131                 {
132                     writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.Transform, ns);
133                     writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
134                     writer.WriteString(dictionaryManager.XmlSignatureDictionary.EnvelopedSignature);
135                     writer.WriteEndAttribute();
136                     writer.WriteEndElement(); // Transform
137                 }
138 
139                 if (this.references[i].useStrTransform)
140                 {
141                     writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.Transform, ns);
142                     writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
143                     writer.WriteString(SecurityAlgorithms.StrTransform);
144                     writer.WriteEndAttribute();
145                     writer.WriteStartElement(XmlSignatureStrings.SecurityJan2004Prefix, XmlSignatureStrings.TransformationParameters, XmlSignatureStrings.SecurityJan2004Namespace);  //<wsse:TransformationParameters>
146                     writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.CanonicalizationMethod, ns);
147                     writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
148                     writer.WriteString(dictionaryManager.SecurityAlgorithmDictionary.ExclusiveC14n);
149                     writer.WriteEndAttribute();
150                     writer.WriteEndElement(); //CanonicalizationMethod
151                     writer.WriteEndElement(); // TransformationParameters
152                     writer.WriteEndElement(); // Transform
153                 }
154                 else
155                 {
156                     writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.Transform, ns);
157                     writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
158                     writer.WriteString(dictionaryManager.SecurityAlgorithmDictionary.ExclusiveC14n);
159                     writer.WriteEndAttribute();
160                     writer.WriteEndElement(); // Transform
161                 }
162 
163                 writer.WriteEndElement(); // Transforms
164 
165                 writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.DigestMethod, ns);
166                 writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
167                 if (this.digestMethodDictionaryString != null)
168                 {
169                     writer.WriteString(this.digestMethodDictionaryString);
170                 }
171                 else
172                 {
173                     writer.WriteString(this.digestMethod);
174                 }
175                 writer.WriteEndAttribute();
176                 writer.WriteEndElement(); // DigestMethod
177 
178                 byte[] digest = this.references[i].digest;
179                 writer.WriteStartElement(prefix, dictionaryManager.XmlSignatureDictionary.DigestValue, ns);
180                 writer.WriteBase64(digest, 0, digest.Length);
181                 writer.WriteEndElement(); // DigestValue
182 
183                 writer.WriteEndElement(); // Reference
184             }
185             writer.WriteEndElement(); // SignedInfo
186         }
187 
188 
189         struct ReferenceEntry
190         {
191             internal string id;
192             internal byte[] digest;
193             internal bool useStrTransform;
194 
SetSystem.IdentityModel.PreDigestedSignedInfo.ReferenceEntry195             public void Set(string id, byte[] digest, bool useStrTransform)
196             {
197                 if (useStrTransform && string.IsNullOrEmpty(id))
198                 {
199                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(id));
200                 }
201 
202                 this.id = id;
203                 this.digest = digest;
204                 this.useStrTransform = useStrTransform;
205             }
206         }
207 
208         sealed class SignedInfoCanonicalFormWriter : CanonicalFormWriter
209         {
210             const string xml1 = "<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></CanonicalizationMethod><SignatureMethod Algorithm=\"";
211             const string xml2 = "\"></SignatureMethod>";
212             const string xml3 = "<Reference URI=\"#";
213             const string xml4 = "\"><Transforms><Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform></Transforms><DigestMethod Algorithm=\"";
214             const string xml4WithStrTransform = "\"><Transforms><Transform Algorithm=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform\"><o:TransformationParameters xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"><CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></CanonicalizationMethod></o:TransformationParameters></Transform></Transforms><DigestMethod Algorithm=\"";
215             const string xml5 = "\"></DigestMethod><DigestValue>";
216             const string xml6 = "</DigestValue></Reference>";
217             const string xml7 = "</SignedInfo>";
218 
219             readonly byte[] fragment1;
220             readonly byte[] fragment2;
221             readonly byte[] fragment3;
222             readonly byte[] fragment4;
223             readonly byte[] fragment4StrTransform;
224             readonly byte[] fragment5;
225             readonly byte[] fragment6;
226             readonly byte[] fragment7;
227 
228             readonly byte[] sha1Digest;
229             readonly byte[] sha256Digest;
230             readonly byte[] hmacSha1Signature;
231             readonly byte[] rsaSha1Signature;
232 
233             static readonly SignedInfoCanonicalFormWriter instance = new SignedInfoCanonicalFormWriter();
234 
SignedInfoCanonicalFormWriter()235             SignedInfoCanonicalFormWriter()
236             {
237                 UTF8Encoding encoding = CanonicalFormWriter.Utf8WithoutPreamble;
238                 this.fragment1 = encoding.GetBytes(xml1);
239                 this.fragment2 = encoding.GetBytes(xml2);
240                 this.fragment3 = encoding.GetBytes(xml3);
241                 this.fragment4 = encoding.GetBytes(xml4);
242                 this.fragment4StrTransform = encoding.GetBytes(xml4WithStrTransform);
243                 this.fragment5 = encoding.GetBytes(xml5);
244                 this.fragment6 = encoding.GetBytes(xml6);
245                 this.fragment7 = encoding.GetBytes(xml7);
246                 this.sha1Digest = encoding.GetBytes(SecurityAlgorithms.Sha1Digest);
247                 this.sha256Digest = encoding.GetBytes(SecurityAlgorithms.Sha256Digest);
248                 this.hmacSha1Signature = encoding.GetBytes(SecurityAlgorithms.HmacSha1Signature);
249                 this.rsaSha1Signature = encoding.GetBytes(SecurityAlgorithms.RsaSha1Signature);
250             }
251 
252             public static SignedInfoCanonicalFormWriter Instance
253             {
254                 get { return instance; }
255             }
256 
EncodeDigestAlgorithm(string algorithm)257             byte[] EncodeDigestAlgorithm(string algorithm)
258             {
259                 if (algorithm == SecurityAlgorithms.Sha1Digest)
260                 {
261                     return this.sha1Digest;
262                 }
263                 else if (algorithm == SecurityAlgorithms.Sha256Digest)
264                 {
265                     return this.sha256Digest;
266                 }
267                 else
268                 {
269                     return CanonicalFormWriter.Utf8WithoutPreamble.GetBytes(algorithm);
270                 }
271             }
272 
EncodeSignatureAlgorithm(string algorithm)273             byte[] EncodeSignatureAlgorithm(string algorithm)
274             {
275                 if (algorithm == SecurityAlgorithms.HmacSha1Signature)
276                 {
277                     return this.hmacSha1Signature;
278                 }
279                 else if (algorithm == SecurityAlgorithms.RsaSha1Signature)
280                 {
281                     return this.rsaSha1Signature;
282                 }
283                 else
284                 {
285                     return CanonicalFormWriter.Utf8WithoutPreamble.GetBytes(algorithm);
286                 }
287             }
288 
WriteSignedInfoCanonicalForm( Stream stream, string signatureMethod, string digestMethod, ReferenceEntry[] references, int referenceCount, byte[] workBuffer, char[] base64WorkBuffer)289             public void WriteSignedInfoCanonicalForm(
290                 Stream stream, string signatureMethod, string digestMethod,
291                 ReferenceEntry[] references, int referenceCount,
292                 byte[] workBuffer, char[] base64WorkBuffer)
293             {
294                 DiagnosticUtility.DebugAssert(XmlSignatureStrings.Prefix.Length == 0, "Update SignedInfoCanonicalFormWriter to match new XmlDSig prefix");
295 
296                 stream.Write(this.fragment1, 0, this.fragment1.Length);
297                 byte[] signatureMethodBytes = EncodeSignatureAlgorithm(signatureMethod);
298                 stream.Write(signatureMethodBytes, 0, signatureMethodBytes.Length);
299                 stream.Write(this.fragment2, 0, this.fragment2.Length);
300 
301                 byte[] digestMethodBytes = EncodeDigestAlgorithm(digestMethod);
302                 for (int i = 0; i < referenceCount; i++)
303                 {
304                     stream.Write(this.fragment3, 0, this.fragment3.Length);
305                     EncodeAndWrite(stream, workBuffer, references[i].id);
306                     if (references[i].useStrTransform)
307                     {
308                         stream.Write(this.fragment4StrTransform, 0, this.fragment4StrTransform.Length);
309                     }
310                     else
311                     {
312                         stream.Write(this.fragment4, 0, this.fragment4.Length);
313                     }
314 
315                     stream.Write(digestMethodBytes, 0, digestMethodBytes.Length);
316                     stream.Write(this.fragment5, 0, this.fragment5.Length);
317                     Base64EncodeAndWrite(stream, workBuffer, base64WorkBuffer, references[i].digest);
318                     stream.Write(this.fragment6, 0, this.fragment6.Length);
319                 }
320 
321                 stream.Write(this.fragment7, 0, this.fragment7.Length);
322             }
323 
324         }
325     }
326 }
327