Imports System.Data Imports System.Web.Security Imports System.Security.Cryptography.X509Certificates Imports System.Xml Imports System.Collections Imports System.Collections.Generic Imports System.Security.Cryptography.Xml Imports System.Text Imports System.IO Imports ComponentSpace.SAML2 Imports ComponentSpace.SAML2.Assertions Imports ComponentSpace.SAML2.Protocols Imports ComponentSpace.SAML2.Bindings Imports ComponentSpace.SAML2.Profiles.ArtifactResolution Imports ComponentSpace.SAML2.Profiles.SSOBrowser Imports ComponentSpace.SAML2.Metadata 'Imports ComponentSpace.SAML2.Utility Partial Class SAML2 Inherits ArchiGenObj.PBasePage Private CC As CBroker Private SessionTimeOutPage As String = "Default.aspx" Private _CurrentX509Key As X509Certificate2 Protected Sub Page_BeforePreInit() Handles Me.BeforePreInit CC = New CBroker(AA) _CurrentX509Key = CC.Security.GetCertifyX509("162BD43B4EFA82D911B2B357A68958C2958C96AA") 'old key was C73104F8768F755FEA05610830F6823A444BC57F End Sub Protected Sub Page_Unload1(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Unload If IsNothing(CC) = False Then CC.Dispose() End Sub Public Overrides Function SecurePage() As Boolean Return False End Function Protected Sub Page_PrintForm() Handles Me.PrintForm Dim LogData As String = "" Dim strSQL As String = "" Dim Usr_Email As String = "" Dim ClientIP As String = "" Dim APIPartnerID As String = "" Dim boolLogUserIn As Boolean = False Dim SAMLDateTime As DateTime = Nothing 'Parse the incoming token - the format should be something like this: Dim token() As String = {""} If Len(Request.Form("SAMLResponse")) > 0 Then 'we need to receive the saml response and create a de-serialized saml repsonse object Dim samlResponseXml As XmlElement = Nothing Dim relayState As String = Nothing Try ServiceProvider.ReceiveSAMLResponseByHTTPPost(New System.Web.HttpRequestWrapper(Request), samlResponseXml, relayState) Catch ex As Exception FailLogin("Malformed SAML Assertion", ex.Message & ex.StackTrace, "The single sign on request was in an invalid format.") : Exit Sub End Try Dim samlResponse As New SAMLResponse(samlResponseXml) Dim samlAssertion As SAMLAssertion = Nothing Dim samlSignedAssertion As XmlElement Dim samlEncAssertion As ComponentSpace.SAML2.Assertions.EncryptedAssertion = Nothing Dim slOldKeys As SortedList = GetOldKeys() Dim strKey As String Dim booKeySuccess As Boolean = False Dim booUsedOldKey As Boolean = False Dim strOldKey As String = "" If samlResponse.GetAssertions.Count > 0 Then samlAssertion = samlResponse.GetAssertions(0) booKeySuccess = True ElseIf samlResponse.GetSignedAssertions.Count > 0 Then samlSignedAssertion = samlResponse.GetSignedAssertions(0) samlAssertion = New SAMLAssertion(samlSignedAssertion) booKeySuccess = True ElseIf samlResponse.GetEncryptedAssertions.Count > 0 Then samlEncAssertion = samlResponse.GetEncryptedAssertions(0) Try 'first try our current key samlAssertion = samlEncAssertion.Decrypt(_CurrentX509Key, Nothing, Nothing) booKeySuccess = True Catch ex As Exception 'loop through all our older keys For Each deKey As DictionaryEntry In slOldKeys strKey = CStr(deKey.Value) Try 'try an older key _CurrentX509Key = CC.Security.GetCertifyX509(strKey) 'CC.Security.GetCertifyX509("4F8D041DB3F84B5ADE499AE6E0971914E93291C7") samlAssertion = samlEncAssertion.Decrypt(_CurrentX509Key, Nothing, Nothing) booKeySuccess = True booUsedOldKey = True strOldKey = strKey SaveError("Attempted use of old x509 cert: " & strKey, Nothing) Exit For Catch ex2 As Exception 'we don't want to fail right now because we might have other keys to try End Try Next End Try Else FailLogin("SAML Response contained no assertion", "SAML Response contained no assertion", "The single sign on request did not contain a SAML assertion.") : Exit Sub End If 'we tried all our keys and none of them worked :( If booKeySuccess = False Then FailLogin("Could not decrypt SAML Assertions", "Tried all keys and could not decrypt SAML Assertions", "The single sign on request was not encrypted with a PGP key from Certify.") : Exit Sub End If Dim strCustomerX509 As String = Nothing If IsNothing(samlAssertion.ToXml.SelectSingleNode("//*[local-name() = 'X509Certificate']")) = False Then strCustomerX509 = samlAssertion.ToXml.SelectSingleNode("//*[local-name() = 'X509Certificate']").InnerText.Replace(" ", "") ElseIf IsNothing(samlResponseXml.SelectSingleNode("//*[local-name() = 'X509Certificate']")) = False Then strCustomerX509 = samlResponseXml.SelectSingleNode("//*[local-name() = 'X509Certificate']").InnerText.Replace(" ", "") Else FailLogin("No X509Certificate found in the assertion or response", "No X509Certificate found in the assertion or response", "The single sign on request did not contain a X509 certificate.") : Exit Sub End If Dim cerCustomerX509 As New X509Certificate2(Convert.FromBase64String(strCustomerX509)) 'now we can pick out the stuff we're after If IsNothing(samlAssertion.Subject.NameID) = False Then Usr_Email = samlAssertion.Subject.NameID.NameIdentifier ElseIf IsNothing(samlAssertion.GetAttributeValue("E-Mail")) = False Then Usr_Email = samlAssertion.GetAttributeValue("E-Mail") ' our ADFS documentation says to use 'E-Mail-Address' ElseIf IsNothing(samlAssertion.GetAttributeValue("E-Mail-Address")) = False Then Usr_Email = samlAssertion.GetAttributeValue("E-Mail-Address") End If If Usr_Email.Contains("#EXT#") = True Then 'AzureAD does this, so we should go get it from what comes over by default If IsNothing(samlAssertion.GetAttributes("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")) = False Then Usr_Email = samlAssertion.GetAttributeValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress") End If End If SAMLDateTime = samlAssertion.IssueInstant If Usr_Email.Length = 0 Then FailLogin("No email address", samlAssertion.ToString, "The single sign on request did not contain an email address.") : Exit Sub End If 'if we used an old key log it here because we have the user at this point If booUsedOldKey = True Then AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "Using an older key " & strOldKey & " EMAIL:" & Usr_Email & " REALIP:" & AA.Request.UserHostAddress) End If LogData = "X509Thumbprint:" & cerCustomerX509.Thumbprint & " EMAIL:" & Usr_Email & " CLIENT_IP:" & ClientIP & " DateTimeStamp:" & SAMLDateTime & " CLIENTIP:" & ClientIP & " REALIP:" & AA.Request.UserHostAddress If Usr_Email.Contains("'") Then 'apparently it's ok to have apostrophes in email addresses, but we need a legit character here! Usr_Email = AA.Server.HtmlDecode(Usr_Email) End If 'Check to make sure the Usr_Email exists and is a corporate account with a Company_API_ID value that is not null and greater than zero length. strSQL = "Select Emp_CompanyFK, Usr.*, CompanyPref.CompanyPref_Data, Company_Name, Redirect_URL.CompanyPref_Data As Redirect_URL, Usr_Disable From Company " _ & "Inner Join Emp On Emp_CompanyFK = Company_PK " _ & "Inner Join Usr On Usr_PK = Emp_UsrFK " _ & "Inner Join CompanyPref On CompanyPref_CompanyFK = Emp_CompanyFK " _ & "Left Join CompanyPref Redirect_URL On Redirect_URL.CompanyPref_CompanyFK = Emp_CompanyFK And Redirect_URL.CompanyPref_Key1 = 'SAMLSSOURL' " _ & "Where CompanyPref.CompanyPref_Key1 = 'X509Thumbprint' " _ & "And Usr_Email = '" & Replace(Usr_Email, "'", "''") & "' " Dim dtEmp As DataTable = AA.Database.Federated_GetDataTable(strSQL, 2) If dtEmp.Rows.Count = 0 Then FailLogin("No records found for " & Usr_Email, LogData, "An account does not exist in Certify for the email address: " & Usr_Email) : Exit Sub End If 'Set the Redirect URL If IsDBNull(dtEmp(0)("Redirect_URL")) = False AndAlso dtEmp(0)("Redirect_URL").Length > 0 Then SessionTimeOutPage = dtEmp(0)("Redirect_URL") End If 'Disabled user check If dtEmp(0)("Usr_Disable") = True Then FailLogin("Disabled user attemping to log in " & Usr_Email, LogData, "The account is disabled in Certify for the email address: " & Usr_Email) : Exit Sub End If If dtEmp(0)("CompanyPref_Data").Length > 0 Then Dim CompanyPrefThumbprint As String = dtEmp(0)("CompanyPref_Data") If CompanyPrefThumbprint = cerCustomerX509.Thumbprint Then boolLogUserIn = True Else Try Dim xmlThumbprintDoc As New XmlDocument xmlThumbprintDoc.LoadXml(CompanyPrefThumbprint) Dim xmlThumbprintNodeList As XmlNodeList = xmlThumbprintDoc.SelectNodes("/X509Thumbprints/X509Thumbprint") For Each ThumbprintNode As XmlElement In xmlThumbprintNodeList Dim StartDate As DateTime = Today.AddDays(-1) Dim EndDate As DateTime = Today.AddDays(1) If ThumbprintNode.HasAttribute("StartDate") = True Then StartDate = CDate(ThumbprintNode.GetAttribute("StartDate")) End If If ThumbprintNode.HasAttribute("EndDate") Then EndDate = CDate(ThumbprintNode.GetAttribute("EndDate") & " 23:59:59") End If If Today > StartDate AndAlso Today < EndDate AndAlso ThumbprintNode.InnerText = cerCustomerX509.Thumbprint Then boolLogUserIn = True Exit For End If Next If boolLogUserIn = False Then FailLogin("No valid thumbprint match for " & Usr_Email, LogData, "The X509 thumbprint (" & cerCustomerX509.Thumbprint & ") does not match the value Certify has on record.") : Exit Sub End If Catch ex As Exception FailLogin("No valid thumbprint match for " & Usr_Email, LogData, "The X509 thumbprint (" & cerCustomerX509.Thumbprint & ") does not match the value Certify has on record.") : Exit Sub End Try End If End If '''' 'Try ' Select Case samlResponseXml.OwnerDocument.DocumentElement.NamespaceURI ' Case SAML.NamespaceURIs.Assertion ' If SAMLAssertionSignature.IsSigned(samlResponseXml) Then ' Dim verified As Boolean = SAMLAssertionSignature.Verify(samlResponseXml, _CurrentX509Key) ' If verified = True Then ' ' AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Assertion Signed and Verified for " & dtEmp.Rows(0).Item("Company_Name")) ' Else ' ' AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Assertion Signed but NOT Verified for " & dtEmp.Rows(0).Item("Company_Name")) ' End If ' Else ' 'AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Assertion NOT signed for " & dtEmp.Rows(0).Item("Company_Name")) ' End If ' Case SAML.NamespaceURIs.Protocol ' If SAMLMessageSignature.IsSigned(samlResponseXml) Then ' Dim verified As Boolean = SAMLMessageSignature.Verify(samlResponseXml, _CurrentX509Key) ' If verified = True Then ' 'AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Message Signed and Verified for " & dtEmp.Rows(0).Item("Company_Name")) ' Else ' ' AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Message Signed but NOT Verified for " & dtEmp.Rows(0).Item("Company_Name")) ' End If ' Else ' 'AA.Log.Write(ArchiGenObj.CLog.LogLevel.LogLevelInfo, "SAML2.aspx", "SAML Message Not Signed for " & dtEmp.Rows(0).Item("Company_Name")) ' End If ' Case Else ' End Select 'Catch ex As Exception ' 'just an experiment to see what folks are using for signatures 'End Try '''' 'Check to make sure the DateTimeStamp is within the last one hour. Dim SSOLifetime As New TimeSpan(1, 0, 0) If Now.Subtract(SSOLifetime) > SAMLDateTime Then 'check operator on this...I may have it backwards FailLogin("Date/Time outside of window.", LogData, "The single sign on request timestamp was outside of a one hour window.") : Exit Sub End If 'Stash the SessionTimeoutPage parameter in a RAM cookie. 'We can't store it in the session because it would disappear at session timeout. 'We don't want to assign a date to this cookie because that would store it on the file system of the user's computer 'just store it in a cookie with a hard-coded name. AA.Response.Cookies.Item("SessionTimeoutPage").Value = AA.Crypto.EncryptString(SessionTimeOutPage) AA.Response.Cookies.Item("SessionTimeoutPage").Path = AA.Parser.VDirPlusFolderName() If AA.Session.Item("SecureCookies") = "1" Then AA.Response.Cookies.Item("SessionTimeoutPage").Secure = True End If If boolLogUserIn = True Then 'Log the user in. LogInUser(dtEmp.Rows(0)) End If 'Redirect to Home page. 'Response.Redirect("Home.aspx") CC.Nav.DoInitialPageRedirect() Else 'lets try to generate the metadata here ' Create an SP entity descriptor Dim spEntityDescriptor As EntityDescriptor = CreateSPEntityDescriptor() Dim XmlElement As XmlElement = spEntityDescriptor.ToXml XmlElement = spEntityDescriptor.ToXml() SAMLMetadataSignature.Generate(XmlElement, _CurrentX509Key.PrivateKey, _CurrentX509Key) ' Verify the SP entity descriptor signature If Not SAMLMetadataSignature.Verify(XmlElement) Then Throw New ArgumentException("The SP entity descriptor signature failed to verify") End If ' Read the IdP entity descriptor 'ReadMetadata(xmlElement) Response.Clear() AA.Response.ContentType = "octet-stream" AA.Response.AddHeader("Content-Disposition", "attachment;filename=" & Chr(34) & "federationmetadata.xml" & Chr(34)) AA.Response.Write(XmlElement.OuterXml) 'AA.Response.Flush() AA.Response.End() End If End Sub Private Sub FailLogin(ByVal LogMessage As String, ByVal LogData As String, ByVal UserMessage As String) Dim strScript As String Dim strContent As String SaveError(LogMessage & vbCrLf & LogData, Nothing) strScript = "var count = 15;" & vbCrLf _ & "var redirect = '" & SessionTimeOutPage & "';" & vbCrLf _ & "function countDown(){" & vbCrLf _ & " var timer = document.getElementById('timer');" & vbCrLf _ & " if(count > 0){" & vbCrLf _ & " count--;" & vbCrLf _ & " timer.innerHTML = 'This page will redirect in ' + count + ' seconds.';" & vbCrLf _ & " setTimeout('countDown()', 1000);" & vbCrLf _ & " }" & vbCrLf _ & " else {" & vbCrLf _ & " window.location.href = redirect;" & vbCrLf _ & " }" & vbCrLf _ & "}" MC.Controls.Add(AA.UI.ReturnScriptBlock(strScript)) strContent = "
" & UserMessage & "
" _ & "Please contact your system administrator for more information.
" _ & "" _ & "