1 ' Copyright (c) Microsoft Corporation. All Rights Reserved.
5 Imports System
.Collections
.ObjectModel
7 Imports System
.IdentityModel
.Tokens
9 Imports System
.Security
.Cryptography
10 Imports System
.Security
.Cryptography
.X509Certificates
12 Imports System
.ServiceModel
13 Imports System
.ServiceModel
.Channels
14 Imports System
.ServiceModel
.Security
.Tokens
18 Imports Microsoft
.VisualBasic
20 Namespace Microsoft
.ServiceModel
.Samples
.Federation
23 ''' Abstract base class for STS implementations
25 Public MustInherit Class SecurityTokenService
26 Implements ISecurityTokenService
28 Private stsName
As String ' The name of the STS. Used to populate saml:Assertion/@Issuer
29 Private m_issuerToken
As SecurityToken
' The SecurityToken used to sign issued tokens
30 Private m_proofKeyEncryptionToken
As SecurityToken
' The SecurityToken used to encrypt the proof key in the issued token.
35 ''' <param name="stsName">The name of the STS. Used to populate saml:Assertion/@Issuer</param>
36 ''' <param name="token">The X509SecurityToken that the STS uses to sign SAML assertions</param>
37 ''' <param name="targetServiceName">The X509SecurityToken that is used to encrypt the proof key in the SAML token.</param>
38 Protected
Sub New(ByVal stsName
As String, ByVal issuerToken
As X509SecurityToken
, ByVal encryptionToken
As X509SecurityToken
)
41 Me.m_issuerToken
= issuerToken
42 Me.m_proofKeyEncryptionToken
= encryptionToken
47 ''' The name of the STS.
49 Protected ReadOnly
Property SecurityTokenServiceName() As String
60 ''' The SecurityToken used to sign tokens the STS issues.
62 Protected ReadOnly
Property IssuerToken() As SecurityToken
66 Return Me.m_issuerToken
73 ''' The SecurityToken used to encrypt the proof key in the issued token.
75 Protected ReadOnly
Property ProofKeyEncryptionToken() As SecurityToken
79 Return Me.m_proofKeyEncryptionToken
85 #Region
"Abstract methods"
88 ''' abstract method for setting up claims in the SAML Token issued by the STS
89 ''' Should be overridden by STS implementations that derive from this base class
90 ''' to set up appropriate claims
92 Protected MustOverride
Function GetIssuedClaims(ByVal requestSecurityToken
As RequestSecurityToken
) As Collection(Of SamlAttribute
)
96 #Region
"Helper Methods"
98 ''' Validate action header and discard messages with inappropriate headers
100 Protected Shared
Sub EnsureRequestSecurityTokenAction(ByVal message
As Message
)
102 If message Is
Nothing Then
103 Throw
New ArgumentNullException("message")
106 If message
.Headers
.Action
<> Constants
.Trust
.Actions
.Issue
Then
107 Throw
New InvalidOperationException([String].Format("Bad or Unsupported Action: {0}", message
.Headers
.Action
))
113 ''' Helper Method to Create Proof Token. Creates BinarySecretSecuryToken
114 ''' with the requested number of bits of random key material
116 ''' <param name="keySize">keySize</param>
117 ''' <returns>Proof Token</returns>
118 Protected Shared
Function CreateProofToken(ByVal keySize
As Integer) As BinarySecretSecurityToken
120 ' Create an array to store the key bytes
121 Dim key() As Byte = New Byte((keySize
/ 8) - 1) {}
122 ' Create some random bytes
123 Dim random
As New RNGCryptoServiceProvider()
124 random
.GetNonZeroBytes(key
)
125 ' Create a BinarySecretSecurityToken from the random bytes and return it
126 Return New BinarySecretSecurityToken(key
)
131 ''' Helper Method to set up the RSTR
133 ''' <param name="rst">RequestSecurityToken</param>
134 ''' <param name="keySize">keySize</param>
135 ''' <param name="proofToken">proofToken</param>
136 ''' <param name="samlToken">The SAML Token to be issued</param>
137 ''' <returns>RequestSecurityTokenResponse</returns>
138 Protected Shared
Function GetRequestSecurityTokenResponse(ByVal requestSecurityToken
As RequestSecurityToken
, ByVal keySize
As Integer, ByVal proofToken
As SecurityToken
, ByVal samlToken
As SecurityToken
, ByVal senderEntropy() As Byte, ByVal stsEntropy() As Byte) As RequestSecurityTokenResponse
140 ' Create an uninitialized RequestSecurityTokenResponse object and set the various properties
141 Dim rstr
As New RequestSecurityTokenResponse()
142 rstr
.TokenType
= Constants
.SamlTokenTypeUri
143 rstr
.RequestedSecurityToken
= samlToken
144 rstr
.RequestedUnattachedReference
= samlToken
.CreateKeyIdentifierClause(Of SamlAssertionKeyIdentifierClause
)()
145 rstr
.RequestedAttachedReference
= samlToken
.CreateKeyIdentifierClause(Of SamlAssertionKeyIdentifierClause
)()
146 rstr
.Context
= requestSecurityToken
.Context
147 rstr
.KeySize
= keySize
149 ' If sender provided entropy then use combined entropy so set the IssuerEntropy
150 If senderEntropy IsNot
Nothing Then
152 rstr
.IssuerEntropy
= New BinarySecretSecurityToken(stsEntropy
)
153 rstr
.ComputeKey
= True
157 ' Issuer entropy only...
158 rstr
.RequestedProofToken
= proofToken
168 ''' Virtual method for ProcessRequestSecurityToken
169 ''' Should be overridden by STS implementations that derive from this base class
171 Public Overridable
Function ProcessRequestSecurityToken(ByVal msg
As Message
) As Message Implements ISecurityTokenService
.ProcessRequestSecurityToken
173 ' Check for appropriate action header
174 EnsureRequestSecurityTokenAction(msg
)
176 ' Extract the MessageID from the request message
177 Dim requestMessageID
As UniqueId
= msg
.Headers
.MessageId
178 If requestMessageID Is
Nothing Then
179 Throw
New InvalidOperationException("The request message does not have a message ID.")
182 ' Get the RST from the message
183 Dim rst
As RequestSecurityToken
= RequestSecurityToken
.CreateFrom(msg
.GetReaderAtBodyContents())
185 ' Set up the claims we are going to issue
186 Dim samlAttributes
As Collection(Of SamlAttribute
) = GetIssuedClaims(rst
)
188 ' get the key size, default to 192
189 Dim keySize
As Integer = IIf((rst
.KeySize
<> 0), rst
.KeySize
, 192)
192 ' Get requester entropy, if any
193 Dim senderEntropy() As Byte = Nothing
194 Dim entropyToken
As SecurityToken
= rst
.RequestorEntropy
195 If entropyToken IsNot
Nothing Then
197 senderEntropy
= (DirectCast(entropyToken
, BinarySecretSecurityToken
)).GetKeyBytes()
201 Dim key() As Byte = Nothing
202 Dim stsEntropy() As Byte = Nothing
204 ' If sender provided entropy, then use combined entropy
205 If senderEntropy IsNot
Nothing Then
207 ' Create an array to store the entropy bytes
208 stsEntropy
= New Byte((keySize \
8) - 1) {}
209 ' Create some random bytes
210 Dim random
As New RNGCryptoServiceProvider()
211 random
.GetNonZeroBytes(stsEntropy
)
212 ' Compute the combined key
213 key
= RequestSecurityTokenResponse
.ComputeCombinedKey(senderEntropy
, stsEntropy
, keySize
)
217 ' Issuer entropy only...
218 ' Create an array to store the entropy bytes
219 key
= New Byte((keySize \
8) - 1) {}
220 ' Create some random bytes
221 Dim random
As New RNGCryptoServiceProvider()
222 random
.GetNonZeroBytes(key
)
226 ' Create a BinarySecretSecurityToken to be the proof token, based on the key material
227 ' in key. The key is the combined key in the combined entropy case, or the issuer entropy
229 Dim proofToken
As New BinarySecretSecurityToken(key
)
231 ' Create a SAML token, valid for around 10 hours
232 Dim samlToken
As SamlSecurityToken
= SamlTokenCreator
.CreateSamlToken(Me.stsName
, proofToken
, Me.IssuerToken
, Me.ProofKeyEncryptionToken
, New SamlConditions(DateTime
.UtcNow
- TimeSpan
.FromMinutes(5), DateTime
.UtcNow
+ TimeSpan
.FromHours(10)), samlAttributes
)
235 Dim rstr
As RequestSecurityTokenResponse
= GetRequestSecurityTokenResponse(rst
, keySize
, proofToken
, samlToken
, senderEntropy
, stsEntropy
)
237 ' Create a message from the RSTR
238 Dim rstrMessage
As Message
= Message
.CreateMessage(msg
.Version
, Constants
.Trust
.Actions
.IssueReply
, rstr
)
240 ' Set RelatesTo of response message to MessageID of request message
241 rstrMessage
.Headers
.RelatesTo
= requestMessageID
243 ' Return the create message