/
EnvelopedSignatureReader.cs
301 lines (270 loc) · 13.4 KB
/
EnvelopedSignatureReader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.IdentityModel
{
using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Xml;
/// <summary>
/// Wraps a reader pointing to a enveloped signed XML and provides
/// a reader that can be used to read the content without having to
/// process the signature. The Signature is automatically validated
/// when the last element of the envelope is read.
/// </summary>
public sealed class EnvelopedSignatureReader : DelegatingXmlDictionaryReader
{
bool _automaticallyReadSignature;
DictionaryManager _dictionaryManager;
int _elementCount;
bool _resolveIntrinsicSigningKeys;
bool _requireSignature;
SigningCredentials _signingCredentials;
SecurityTokenResolver _signingTokenResolver;
SignedXml _signedXml;
SecurityTokenSerializer _tokenSerializer;
WrappedReader _wrappedReader;
bool _disposed;
/// <summary>
/// Initializes an instance of <see cref="EnvelopedSignatureReader"/>
/// </summary>
/// <param name="reader">Reader pointing to the enveloped signed XML.</param>
/// <param name="securityTokenSerializer">Token Serializer to resolve the signing token.</param>
public EnvelopedSignatureReader(XmlReader reader, SecurityTokenSerializer securityTokenSerializer)
: this(reader, securityTokenSerializer, null)
{
}
/// <summary>
/// Initializes an instance of <see cref="EnvelopedSignatureReader"/>
/// </summary>
/// <param name="reader">Reader pointing to the enveloped signed XML.</param>
/// <param name="securityTokenSerializer">Token Serializer to deserialize the KeyInfo of the Signature.</param>
/// <param name="signingTokenResolver">Token Resolver to resolve the signing token.</param>
/// <exception cref="ArgumentNullException">One of the input parameter is null.</exception>
public EnvelopedSignatureReader(XmlReader reader, SecurityTokenSerializer securityTokenSerializer, SecurityTokenResolver signingTokenResolver)
: this(reader, securityTokenSerializer, signingTokenResolver, true, true, true)
{
}
/// <summary>
/// Initializes an instance of <see cref="EnvelopedSignatureReader"/>
/// </summary>
/// <param name="reader">Reader pointing to the enveloped signed XML.</param>
/// <param name="securityTokenSerializer">Token Serializer to deserialize the KeyInfo of the Signature.</param>
/// <param name="signingTokenResolver">Token Resolver to resolve the signing token.</param>
/// <param name="requireSignature">The value indicates whether the signature is optional.</param>
/// <param name="automaticallyReadSignature">This value indicates if the Signature should be read
/// when the Signature element is encountered or allow the caller to read the Signature manually.</param>
/// <param name="resolveIntrinsicSigningKeys">A value indicating if intrinsic signing keys should be resolved.</param>
public EnvelopedSignatureReader(XmlReader reader, SecurityTokenSerializer securityTokenSerializer, SecurityTokenResolver signingTokenResolver, bool requireSignature, bool automaticallyReadSignature, bool resolveIntrinsicSigningKeys)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
if (securityTokenSerializer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenSerializer");
}
_automaticallyReadSignature = automaticallyReadSignature;
_dictionaryManager = new DictionaryManager();
_tokenSerializer = securityTokenSerializer;
_requireSignature = requireSignature;
_signingTokenResolver = signingTokenResolver ?? EmptySecurityTokenResolver.Instance;
_resolveIntrinsicSigningKeys = resolveIntrinsicSigningKeys;
XmlDictionaryReader dictionaryReader = XmlDictionaryReader.CreateDictionaryReader(reader);
_wrappedReader = new WrappedReader(dictionaryReader);
base.InitializeInnerReader(_wrappedReader);
}
void OnEndOfRootElement()
{
if (null == _signedXml)
{
if (_requireSignature)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CryptographicException(SR.GetString(SR.ID3089)));
}
}
else
{
ResolveSigningCredentials();
_signedXml.StartSignatureVerification(_signingCredentials.SigningKey);
_wrappedReader.XmlTokens.SetElementExclusion(XD.XmlSignatureDictionary.Signature.Value, XD.XmlSignatureDictionary.Namespace.Value);
WifSignedInfo signedInfo = _signedXml.Signature.SignedInfo as WifSignedInfo;
_signedXml.EnsureDigestValidity(signedInfo[0].ExtractReferredId(), _wrappedReader);
_signedXml.CompleteSignatureVerification();
}
}
/// <summary>
/// Returns the SigningCredentials used in the signature after the
/// envelope is consumed and when the signature is validated.
/// </summary>
public SigningCredentials SigningCredentials
{
get
{
return _signingCredentials;
}
}
/// <summary>
/// Gets a XmlBuffer of the envelope that was enveloped signed.
/// The buffer is available after the XML has been read and
/// signature validated.
/// </summary>
internal XmlTokenStream XmlTokens
{
get
{
return _wrappedReader.XmlTokens.Trim();
}
}
/// <summary>
/// Overrides the base Read method. Checks if the end of the envelope is reached and
/// validates the signature if requireSignature is enabled. If the reader gets
/// positioned on a Signature element the whole signature is read in if automaticallyReadSignature
/// is enabled.
/// </summary>
/// <returns>true if the next node was read successfully; false if there are no more nodes</returns>
public override bool Read()
{
if ((base.NodeType == XmlNodeType.Element) && (!base.IsEmptyElement))
{
_elementCount++;
}
if (base.NodeType == XmlNodeType.EndElement)
{
_elementCount--;
if (_elementCount == 0)
{
OnEndOfRootElement();
}
}
bool result = base.Read();
if (_automaticallyReadSignature
&& (_signedXml == null)
&& result
&& base.InnerReader.IsLocalName(XD.XmlSignatureDictionary.Signature)
&& base.InnerReader.IsNamespaceUri(XD.XmlSignatureDictionary.Namespace))
{
ReadSignature();
}
return result;
}
void ReadSignature()
{
_signedXml = new SignedXml(new WifSignedInfo(_dictionaryManager), _dictionaryManager, _tokenSerializer);
_signedXml.TransformFactory = ExtendedTransformFactory.Instance;
_signedXml.ReadFrom(_wrappedReader);
if (_signedXml.Signature.SignedInfo.ReferenceCount != 1)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.ID3057)));
}
}
void ResolveSigningCredentials()
{
if (_signedXml.Signature == null || _signedXml.Signature.KeyIdentifier == null || _signedXml.Signature.KeyIdentifier.Count == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID3276)));
}
SecurityKey signingKey = null;
WifSignedInfo signedInfo = _signedXml.Signature.SignedInfo as WifSignedInfo;
if (!_signingTokenResolver.TryResolveSecurityKey(_signedXml.Signature.KeyIdentifier[0], out signingKey))
{
if (_resolveIntrinsicSigningKeys && _signedXml.Signature.KeyIdentifier.CanCreateKey)
{
if (_signedXml.Signature.KeyIdentifier.Count < 2 || LocalAppContextSwitches.ReturnMultipleSecurityKeyIdentifierClauses)
{
signingKey = _signedXml.Signature.KeyIdentifier.CreateKey();
_signingCredentials = new SigningCredentials(signingKey, _signedXml.Signature.SignedInfo.SignatureMethod, signedInfo[0].DigestMethod, _signedXml.Signature.KeyIdentifier);
return;
}
else
{
foreach (var clause in _signedXml.Signature.KeyIdentifier)
{
if (clause.CanCreateKey)
{
_signingCredentials = new SigningCredentials(clause.CreateKey(), _signedXml.Signature.SignedInfo.SignatureMethod, signedInfo[0].DigestMethod, new SecurityKeyIdentifier(clause));
return;
}
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.KeyIdentifierCannotCreateKey)));
}
}
else
{
//
// we cannot find the signing key to verify the signature
//
EncryptedKeyIdentifierClause encryptedKeyClause;
if (_signedXml.Signature.KeyIdentifier.TryFind<EncryptedKeyIdentifierClause>(out encryptedKeyClause))
{
//
// System.IdentityModel.Tokens.EncryptedKeyIdentifierClause.ToString() does not print out
// very good information except the cipher data in this case. We have worked around that
// by using the token serializer to serialize the key identifier clause again.
//
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new SignatureVerificationFailedException(
SR.GetString(SR.ID4036, XmlUtil.SerializeSecurityKeyIdentifier(_signedXml.Signature.KeyIdentifier, _tokenSerializer))));
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new SignatureVerificationFailedException(SR.GetString(SR.ID4037, _signedXml.Signature.KeyIdentifier.ToString())));
}
}
}
if (_signedXml.Signature.KeyIdentifier.Count < 2 || LocalAppContextSwitches.ReturnMultipleSecurityKeyIdentifierClauses)
_signingCredentials = new SigningCredentials(signingKey, _signedXml.Signature.SignedInfo.SignatureMethod, signedInfo[0].DigestMethod, _signedXml.Signature.KeyIdentifier);
else
_signingCredentials = new SigningCredentials(signingKey, _signedXml.Signature.SignedInfo.SignatureMethod, signedInfo[0].DigestMethod, new SecurityKeyIdentifier(_signedXml.Signature.KeyIdentifier[0]));
}
/// <summary>
/// Reads the signature if the reader is currently positioned at a Signature element.
/// </summary>
/// <returns>true if the signature was successfully read else false.</returns>
/// <remarks>Does not move the reader when returning false.</remarks>
public bool TryReadSignature()
{
if (IsStartElement(XD.XmlSignatureDictionary.Signature, XD.XmlSignatureDictionary.Namespace))
{
ReadSignature();
return true;
}
return false;
}
#region IDisposable Members
/// <summary>
/// Releases the unmanaged resources used by the System.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader and optionally
/// releases the managed resources.
/// </summary>
/// <param name="disposing">
/// True to release both managed and unmanaged resources; false to release only unmanaged resources.
/// </param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (_disposed)
{
return;
}
if (disposing)
{
//
// Free all of our managed resources
//
if (_wrappedReader != null)
{
_wrappedReader.Close();
_wrappedReader = null;
}
}
// Free native resources, if any.
_disposed = true;
}
#endregion
}
}