Last week I showed a peculiar XML Signature that validates even though the containing document was changed. The reason is that the signature lacks References. Before explaining what’s wrong with the signature – and with the validation code, we’ll have a look at how XML Signatures work.
XML DSig Primer
XML in general is a powerful beast, with so many options available that it quickly gets really complex. The XML Digital Signatures standard is no exception to that. The extra features complexity of XML DSig compared to other signature standard is that one or more different blocks of data can be signed by the same signature block. That data can be the containing XML Document, part of an XML document or some other resource such as a web page. In this post we’ll only look at signing resources in the document containing the signature.
XML Signatures are powerful, but also a bit tricky to get right. Here’s a challenge: I have a signature that will validate, even though the contents of the XML document are altered.
This is the “magic” signature that validates regardless of what XML document it is placed in.
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
</SignedInfo>
<SignatureValue>ZlSHRu+tG6bVTPxyKvp2PcNqg+lVmBSB2TnzABa2eJZiFcIBtD4/0dhT8Mhr+TbcpzGTbPgMFOx1JmZzfQVzbcMUJfnU674C368VlULAnRa3Avqb+MNJWdAIT8cgdB1yJyZbtuChww13e2CMhikW+xZhaZFzD11KTKqiR+7DRcd+k026w7y6JYy/XgUkS3y1MzuUFO0Uk+tIKI2ik/iLH0XvO0TFO+5uLoz3VaVYj4BIgwFZlYFE/y36EVTeVC4fW7bxPiLdKfZNMin2ZpOusgr/Lyp+J5NQBUOV1FklGsHJbFLN7GJcaUBwnmAib8yReVmAIjsEWBRhB5b6TLojbw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>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</X509Certificate>
</X509Data>
</KeyInfo>
</Signature> |
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
</SignedInfo>
<SignatureValue>ZlSHRu+tG6bVTPxyKvp2PcNqg+lVmBSB2TnzABa2eJZiFcIBtD4/0dhT8Mhr+TbcpzGTbPgMFOx1JmZzfQVzbcMUJfnU674C368VlULAnRa3Avqb+MNJWdAIT8cgdB1yJyZbtuChww13e2CMhikW+xZhaZFzD11KTKqiR+7DRcd+k026w7y6JYy/XgUkS3y1MzuUFO0Uk+tIKI2ik/iLH0XvO0TFO+5uLoz3VaVYj4BIgwFZlYFE/y36EVTeVC4fW7bxPiLdKfZNMin2ZpOusgr/Lyp+J5NQBUOV1FklGsHJbFLN7GJcaUBwnmAib8yReVmAIjsEWBRhB5b6TLojbw==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIGGDCCBQCgAwIBAgIDDdIRMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3MgMSBQcmltYXJ5IEludGVybWVkaWF0ZSBDbGllbnQgQ0EwHhcNMTUwNDI0MTIzMjU4WhcNMTYwNDI0MDQzMDI4WjA4MRcwFQYDVQQDDA5hbmRlcnNAYWJlbC5udTEdMBsGCSqGSIb3DQEJARYOYW5kZXJzQGFiZWwubnUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCeNuAxBAFeueFVtptqq6xY8GetxhZYI3ytgNG3IEHWzN4Pf9Ql58V0dUdYBSDZENOv9WArr4sLkpsB5U9n/C/6pn5auN1Db6GQyo7zTlrs+duuwpcWIodoGrrutqOdabIULrxDbQ+nbCelH64iBCh3YtzsPnGjHUo6JlpfVYau99d7Oh/+8dOKqT1TVs/w1mt1l3AEYSM3SfBx0L8k2xuzi42EcGIySJj2U3eFYqv/kO1sIg/X3Kvuo4CWz4hHGdeRNyZatBZyXtE7SZNdGjvP+D+iuqYH8gWPVTO8TQg9QY+/BpVBfa9JSfSwi4KcAe/a7m/qkyu+gBJ9M21cJXEZAgMBAAGjggLUMIIC0DAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwHQYDVR0OBBYEFDZrDRuDS0GGF8c3iC9drCI9p63oMB8GA1UdIwQYMBaAFFNy7ZKc4NrLAVx8fpY1TvLUuFGCMBkGA1UdEQQSMBCBDmFuZGVyc0BhYmVsLm51MIIBTAYDVR0gBIIBQzCCAT8wggE7BgsrBgEEAYG1NwECAzCCASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5nIHRvIHRoZSBDbGFzcyAxIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRoZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkgb2JsaWdhdGlvbnMuMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL2NydHUxLWNybC5jcmwwgY4GCCsGAQUFBwEBBIGBMH8wOQYIKwYBBQUHMAGGLWh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbS9zdWIvY2xhc3MxL2NsaWVudC9jYTBCBggrBgEFBQcwAoY2aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvc3ViLmNsYXNzMS5jbGllbnQuY2EuY3J0MCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tLzANBgkqhkiG9w0BAQUFAAOCAQEAPqjFybD6u6vLrRWZNe61WVZK2GMuv3Sm03acHLZIy8+zfoQfM9NhPvsmDmPpAelujJMOHISIkUxBB2qL9y/fDPqMsOg2wXIAJ96in+rmvjmnUd75bdbHJtVKOyx2m2HAjy4kA5bBF96asE0NVVTr8VR4N/NAoO0jPlNDLe8L5M5n1R4rttfjZ8IGQ6++DkGsatD8jh2ZuZUoI8q4gEo+8WKvBmX6bbRhp69ZO7eZoV+KBW66y5DfnBI0LHT0gC93otSO4wClyj8KGmRaAFzIt0f+f7VXl3Zjttc0lUF8mJoqnGDXByg9Q50h0nGp+rxjdURYkWW+WHvnVWo3ejSYiQ==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
I’ve made a small test program where I try it out.
Distribution of credentials to new users of a system is often done in an insecure way, with passwords being sent over unsecure e-mail. With ASP.NET Identity, the password recovery functionality can be used to create a secure account activation mechanism.
The scenario for ASP.NET Identity, in the default MVC template is to let users self register. Then there are mechanisms to confirm the e-mail address, to make sure that the user actually is in control of the given e-mail address. There are also support for letting the user associate the account with external sign on solutions such as Google, Facebook and Twitter. That’s perfectly fine, but not for most applications I build.
I’m building line of business applications. They are actually often exposed on the Internet as they need to be available for partners. But, they are not meant to be available through self registration for anyone on the Internet. Those applications are invite only. That means that a user account is created for a new user. Then that user somehow has to be notified that the account has been created. The usual way to do that is to create the account, set a good password like “ChangeMe123” and send the user a mail with the new credentials. There are two problems with this
- A lot of users don’t get the hint and keep the “ChangeMe123” password.
- The e-mail can be sitting unread for a long time in the inbox, until someone gets hold of it – and the account.
Fortunately, there is a much more secure way to do account activation with ASP.NET Identity without much coding at all – by reusing the password recovery mechanism.
Using the Kentor.AuthServices SAML2 Service Provider with Thinktecture IdentityServer 3 bridges the gap between SAML2 and OAuth2/OpenID Connect. Thinktecture IdentityServer 3 support clients using the modern OAuth2 and OpenID Connect protocols. It can either have a local account database through e.g. ASP.NET Identity, or use external authentication services. By registering Kentor.AuthServices with IdentityServer, IdentityServer can authenticate to a SAML2 Idp.
I know that SAML2 is often regarded as legacy, but the truth is that there is still vast amounts of infrastructure out there that supports SAML2, but has not yet taken the leap to OpenID Connect. When the client applications prefer modern standards, a bridge between them is needed. With Kentor.AuthServices, Thinktecture IdentityServer can be that bridge.
Get It Running
To add SAML2 to IdentityServier, changes are needed in three places: Installing the Kentor.AuthServices.Owin
package, alter the startup configuration method for IdentityServer and add two lines to the web/app.config file.
Cookies set through the Owin API sometimes mysteriously disappear. The problem is that deep within System.Web
, there has been a cookie monster sleeping since the dawn of time (well, at least since .NET and System.Web
was released). The monster has been sleeping for all this time, but now, with the new times arriving with Owin, the monster is awake. Being starved from the long sleep, it eats cookies set through the Owin API for breakfast. Even if the cookies are properly set, they are eaten by the monster before the Set-Cookie
headers are sent out to the client browser. This typically results in heisenbugs affecting sign in and sign out functionality.
TL;DR
The problem is that System.Web
has its own master source of cookie information and that isn’t the Set-Cookie
header. Owin only knows about the Set-Cookie
header. A workaround is to make sure that any cookies set by Owin are also set in the HttpContext.Current.Response.Cookies
collection.
This is exactly what my Kentor.OwinCookieSaver middleware does. It should be added in to the Owin pipeline (typically in Startup.Auth.cs
), before any middleware that handles cookies.
app.UseKentorOwinCookieSaver(); |
app.UseKentorOwinCookieSaver();
The cookie saver middleware preserves cookies set by other middleware. Unfortunately it is not reliable for cookies set by the application code (such as in MVC Actions). The reason is that the System.Web
cookie handling code might be run after the application code, but before the middleware. For cookies set by the application code, the workaround by storing a dummy value in the sessions is more safe.