Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates
Imports System.Security.Cryptography.Xml
Imports System.IO.Compression

Namespace SAML
    Public Class SamlHelper

        Public Shared Function CreateAuthRequest(issuer As String, assertionConsumerServiceUrl As String) As String
            Dim id = "_" & Guid.NewGuid().ToString()
            Dim issueInstant = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

            Dim requestXml As String = String.Format( _
                "<samlp:AuthnRequest xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol"" ID=""{0}"" Version=""2.0"" IssueInstant=""{1}"" ProtocolBinding=""urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"" AssertionConsumerServiceURL=""{2}"">" & vbCrLf & _
                "  <saml:Issuer xmlns:saml=""urn:oasis:names:tc:SAML:2.0:assertion"">{3}</saml:Issuer>" & vbCrLf & _
                "</samlp:AuthnRequest>", _
                id, issueInstant, assertionConsumerServiceUrl, issuer _
            )
            
            Dim bytes = Encoding.UTF8.GetBytes(requestXml)

            Using ms = New MemoryStream()
                Using zip = New DeflateStream(ms, CompressionMode.Compress, True)
                    zip.Write(bytes, 0, bytes.Length)
                End Using
                Dim compressedBytes = ms.ToArray()
                Return Convert.ToBase64String(compressedBytes)
            End Using
        End Function

        Public Class Response
            Private ReadOnly _xml As XmlDocument
            Private ReadOnly _certificate As X509Certificate2

            Public Sub New(certString As String, base64Response As String)
                Dim decoded = Convert.FromBase64String(base64Response)
                Dim xmlString = Encoding.UTF8.GetString(decoded)

                _xml = New XmlDocument()
                _xml.PreserveWhitespace = True
                _xml.LoadXml(xmlString)

                _certificate = New X509Certificate2(Encoding.UTF8.GetBytes(certString))
            End Sub

            Public Function IsValid() As Boolean
                Dim ns = New XmlNamespaceManager(_xml.NameTable)
                ns.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#")

                Dim signatureNode = _xml.SelectSingleNode("//ds:Signature", ns)
                If signatureNode Is Nothing Then Return False

                Dim signedXml = New SignedXml(_xml)
                signedXml.LoadXml(CType(signatureNode, XmlElement))

                Return signedXml.CheckSignature(_certificate, True)
            End Function

            Public Function GetEmail() As String
                Return GetAttributeValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")
            End Function

            Public Function GetFirstName() As String
                Return GetAttributeValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname")
            End Function

            Public Function GetLastName() As String
                Return GetAttributeValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname")
            End Function

            Private Function GetAttributeValue(attributeName As String) As String
                Dim nsmgr = New XmlNamespaceManager(_xml.NameTable)
                nsmgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion")

                Dim attrNode = _xml.SelectSingleNode(String.Format("//saml:Attribute[@Name='{0}']/saml:AttributeValue", attributeName), nsmgr)
                Return If(attrNode IsNot Nothing, attrNode.InnerText, String.Empty)
            End Function
        End Class
    End Class
End Namespace
