Passing a Certificate with SSLstream

  • (2 Pages)
  • +
  • 1
  • 2

25 Replies - 922 Views - Last Post: 28 September 2018 - 09:40 AM Rate Topic: -----

#16 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 26 September 2018 - 08:21 PM

Hey @Sheepings - Thanks for taking the time to go through my long post above and respond! As you can see from that previous post, my code has changed a bit. I now basically have 2 different sets of code now. The first one gives me the "remote certificate is invalid according to the validation procedure" while the second one gives no certificate error, but says it was "aborted by the software in your host machine." At this point I feel like I don't know which of the two sets of code is actually "correct" and if the issue is actually with the code itself or my certificate.

Here is the first set of code I was trying. Whether I run this code with your "3.cer" file or the "FIcert.cer" file which I had previous converted online, I always receive the "remote certificate is invalid according to the validation procedure" exception. The exception I receive when running this code leads me to believe this might be the "correct" way. However, sslStream.AuthenticateAsClient should always contain the hostname, shouldn't it?
        Dim sslCertificate As X509Certificate2 = New X509Certificate2("C:\\Test\\3.cer")
        Dim requestResponse As String = String.Empty
        Dim client As New TcpClient(DRShost, 700)
        Dim sslStream As New Security.SslStream(client.GetStream(), True)
        sslStream.AuthenticateAsClient(sslCertificate.ToString)
	''''''IT NEVER MAKES IT PAST THIS POINT''''''
        Dim helloElement As XElement = <epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
                                           <hello/>
                                       </epp>

        SendRequest(helloElement.ToString, sslStream, System.Text.Encoding.UTF8)
        requestResponse = GetResponse(sslStream, System.Text.Encoding.UTF8)
        MsgBox(requestResponse)



Here is the second set of code I've tried most recently. Whether I run this code with your "3.cer" file or the "FIcert.cer" file which I had previously converted online, I always receive that "aborted by the software in your host machine" exception. The fact that this code doesn't give any debug errors in the client/SSLstream area makes me think it actually gets connected. However, I'm not sure how it would let me connect when using your "3.cer" file?
        Dim collection = New X509Certificate2Collection()
        collection.Import("C:\\Users\\Drew\\Desktop\\3.cer")
        Dim store = New X509Store(StoreName.My, StoreLocation.CurrentUser)
        store.Open(OpenFlags.ReadWrite)

        Try
            For Each certificate As X509Certificate2 In collection
                store.Add(certificate)
            Next
        Finally
            store.Close()
        End Try

        Dim requestResponse As String = String.Empty
        Dim client As New TcpClient(DRShost, 700)
        Dim sslStream As New Security.SslStream(client.GetStream(), True)
        sslStream.AuthenticateAsClient(DRShost, collection, True, True)

        Dim helloElement As XElement = <epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
                                           <hello/>
                                       </epp>

        SendRequest(helloElement.ToString, sslStream, System.Text.Encoding.UTF8)
	''''''IT NEVER MAKES IT PAST THIS POINT''''''
        requestResponse = GetResponse(sslStream, System.Text.Encoding.UTF8)
        MsgBox(requestResponse)



SendRequest Sub
    Private Sub SendRequest(ByVal msg As String, ByVal strm As System.Net.Security.SslStream, ByVal encoding As System.Text.Encoding)
        Dim ret As String = Nothing
        ' get encoded message bytes
        Dim msgBytes As Byte() = encoding.GetBytes(msg)

        ' get encoded message length in Big Endian, Windows is Little Endian
        ' this array will be 4 bytes long
        Dim totalMessageLength As Int32 = msgBytes.Length + 4 ' add 4 for the length preface
        Dim msgLengthBytes As Byte() = Int32AsBigEndianBytes(totalMessageLength)

        ' write message length
        strm.Write(msgLengthBytes, 0, msgLengthBytes.Length)

        ' write message
        strm.Write(msgBytes, 0, msgBytes.Length)
        strm.Flush()
    End Sub



GetResponse Function
    Private Function GetResponse(ByVal strm As System.Net.Security.SslStream, ByVal encoding As System.Text.Encoding) As String
        Dim ret As String = Nothing
        Try
            ' get encoded response length in Big Endian, Windows is Little Endian
            ' this array will be 4 bytes long
            Dim msgLengthBytes(0 To 3) As Byte
            Dim readBytes As Int32 = strm.Read(msgLengthBytes, 0, 4)
            If readBytes = 4 Then
                ' should get back 4 bytes that are the BigEndian length
                Dim totalResponseLength As Int32 = BigEndianInt32BytesToInt32(msgLengthBytes)
                ' subtract the bytes that comprise the length
                Dim responseLength As Int32 = totalResponseLength - 4

                ' get encoded message bytes
                Dim msgBytes(0 To (responseLength - 1)) As Byte
                readBytes = strm.Read(msgBytes, 0, responseLength)
                ret = encoding.GetString(msgBytes)
            End If
            Return ret
        Catch ex As Exception
            Return ret
        End Try
    End Function



Int32AsBigEndianBytes Function
    Private Function Int32AsBigEndianBytes(ByVal val As Int32) As Byte()
        Dim bytes As Byte() = BitConverter.GetBytes(val)
        Array.Reverse(bytes)
        Return bytes
    End Function



BigEndianInt32BytesToInt32 Function
    Private Function BigEndianInt32BytesToInt32(ByVal bytes As Byte()) As Int32
        Dim tmpBytes(0 To bytes.Length - 1) As Byte
        Array.Copy(bytes, tmpBytes, bytes.Length)
        Array.Reverse(tmpBytes)
        Return BitConverter.ToInt32(tmpBytes, 0)
    End Function


Was This Post Helpful? 0
  • +
  • -

#17 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 26 September 2018 - 08:33 PM

Just to add some details about the certificates.. The service doesn't allow self-signed certs, so I set this one up inside of cPanel. When I did so it gave me 2 files - a CRT file and a KEY file. I went and uploaded the CRT file inside my user account at the services website. I then proceeded to open the CRT and KEY files in a text editor and place both of them into one file (one on top of the other) and used that to make the FIcert file I've been testing with.

I decided to go out and try making another certificate somewhere else, and that one actually gave me some additional files (a ca-bundle & CARoot.crt). I haven't done any testing with this certificate at all yet because I thought the initial one I described above would be enough. Do you think it would be better for me to test with this new certificate instead, and try uploading something like the CARoot certificate into my user account where the other one is now?
Was This Post Helpful? 0
  • +
  • -

#18 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 120
  • View blog
  • Posts: 843
  • Joined: 05-December 13

Re: Passing a Certificate with SSLstream

Posted 26 September 2018 - 08:34 PM

Ok let me dig in to this tomorrow as its 4:30AM again, and I'm still up and I need to get some sleep. We will get it working eventually. :)

I also created a self-signed cert without the encryption on the plain text of the .crt file. So its actually readable with any editor. I've yet to test it too, but see no reason for it not to work. I created it inside VS.

Drop the CARoot.crt file. It's not needed as you need only need DER encoded certificates otherwise you will be getting the initial error you opened the topic with.

This post has been edited by Sheepings: 26 September 2018 - 08:49 PM

Was This Post Helpful? 0
  • +
  • -

#19 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 26 September 2018 - 09:02 PM

Thanks a bunch for all your help on this Sheepings! Sorry I couldn't get my reply in earlier. I'm going to continue messing around with this tonight some more and will keep you posted if I make any progress.
Was This Post Helpful? 0
  • +
  • -

#20 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 27 September 2018 - 01:50 AM

After speaking to one of their support guys and doing a bit more research online, I think I've made some more progress. However, the response I get is still blank..

I previously mentioned the CRT and KEY files needed to be together. I was taking these and trying to make a CER file with them, but apparently they need to be combined into a PFX file. At least, that's about the only way I've found to "combine" these two together. So basically, the CRT file goes in my account on their website, and then I comebine the CRT and KEY into one PFX file and pass that with the password. Since I don't have OpenSSL installed I went out and used the same online converter I was using before, only to make the PFX this time. I was able to successfully select my CRT and KEY files and combine them with a password to make one PFX file.

Below is my most up-to-date code. Maybe @Sheepings or someone has an idea as to why I would still be getting a blank response? At this point nothing breaks between making a connection, calling SendRequest and then calling GetResponse. It's not until I try to read the response with an "If" statement that I now get an exception..

        Dim collection = New X509Certificate2Collection()
        collection.Import("C:\\Test\\FIcert.pfx", "password", X509KeyStorageFlags.DefaultKeySet)
        Dim store = New X509Store(StoreName.My, StoreLocation.CurrentUser)
        store.Open(OpenFlags.ReadWrite)

        Try
            For Each certificate As X509Certificate2 In collection
                store.Add(certificate)
            Next
        Finally
            store.Close()
        End Try

        Dim requestResponse As String = String.Empty
        Dim client As New TcpClient(DRShost, 700)
        Dim sslStream As New Security.SslStream(client.GetStream(), True)
        sslStream.AuthenticateAsClient(DRShost, collection, System.Security.Authentication.SslProtocols.[Default], False)

        If client.Connected Then
            Dim helloElement As XElement = <epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
                                               <hello/>
                                           </epp>
            SendRequest(helloElement.ToString, sslStream, System.Text.Encoding.UTF8)
            requestResponse = GetResponse(sslStream, System.Text.Encoding.UTF8)

            If requestResponse.ToLower.Contains("<greeting>") = True Then
	''''''IT NEVER MAKES IT PAST THIS POINT''''''
                MsgBox("Success!")
            Else
                MsgBox("Failed!")
            End If
        Else
            MsgBox("Client not connected!")
            e.Cancel = True
            Exit Sub
        End If



The specific line I am receiving an exception on now is here:
If requestResponse.ToLower.Contains("<greeting>") = True Then



and here is the exception

Quote

'Object reference not set to an instance of an object.'


Could it be something related to X509KeyStorageFlags.DefaultKeySet? Or maybe System.Security.Authentication.SslProtocols.[Default] in my sslStream.AuthenticateAsClient? After speaking with their support guy I can now confirm I definitely have the right certificate on the server, and I appear to be combining the CRT and KEY files properly (it won't let you merge these two files if the keys don't match up). Just can't figure out why I'm still not getting the response!!
Was This Post Helpful? 0
  • +
  • -

#21 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 120
  • View blog
  • Posts: 843
  • Joined: 05-December 13

Re: Passing a Certificate with SSLstream

Posted 27 September 2018 - 08:11 AM

I just sent you a direct message, please check your personal message.

I'm wondering what message you are on about, because I don't see anywhere you've declared a network stream to write your messages.

Attached Image

Attached Image

Mine is sending and receiving fine, but you've yet to declare your net stream or have I overlooked something?

This post has been edited by Sheepings: 27 September 2018 - 09:07 AM

Was This Post Helpful? 0
  • +
  • -

#22 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 27 September 2018 - 01:12 PM

Thanks for your reply Sheepings! I've just responded to your private message, and am looking into the NetworkStream now. I don't think I have a TCPListener in there because I thought the server would be doing the listening, but this could be where my issue is. I'm going to look more into that and will update this thread soon. In the meantime, feel free to reply to my private message. Thanks again!!
Was This Post Helpful? 1
  • +
  • -

#23 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 120
  • View blog
  • Posts: 843
  • Joined: 05-December 13

Re: Passing a Certificate with SSLstream

Posted 27 September 2018 - 04:21 PM

How is the server going to listen without a listener? Where are your send requests going to if not to a TCP server with listening capabilities? Well I'm in and can see what you are doing...

You've to much going on here and you've over complicated most of it. You've to much code in Thread1 and not enough functions doing one single assignment. Instead, you have a send request sub which should be a function, and it should be returning only the bytes from the message and then It would be best to pass that to another function to execute the stream writing. But its your app, and that's just my opinion, and how I like to do things.
strm.Write(msgLengthBytes, 0, msgLengthBytes.Length)

        ' write message
        strm.Write(msgBytes, 0, msgBytes.Length)
        strm.Flush()

By sectioning your code into functions, its easier to see when a function misbehaves instead of having it in one big subroutine, thus making it easier to isolate problems more efficiently.

In my example I created, I wrote mine with the network stream, and you never included it in your SendRequest. Also in your code throughout the app, you should be including using blocks especially when working with TCP clients because failing to expose of the connection/closing it will result in a memory leak. Using blocks will release the resources after they're done using.

Start using a time-out. A period of receiving no bytes means there is no more data for the message. I don't see that Implemented, and its something you should add. And you will also want the network stream for communicating back and forward.

I'm currently troubleshooting a problem with the X509Certificate, so I'll post back once I make some headway with this.
Was This Post Helpful? 1
  • +
  • -

#24 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 27 September 2018 - 07:54 PM

Hey Sheepings, thanks for the reply! As we spoke about in our private message, the listening is being handled by the services website which I'm connecting to, so the TCPListener is remote. I'll definitely be looking to clean up the code as well. Some of it's gotten messy since I've been trying to make this work. It's caused me to just throw a few things into the BackgroundWorker so I can quickly test and see if the changes I made work. Once I can at least get connected and receive a response (so I know it's working) I was going to go in and move some of that stuff around and focus on cleaning things up. Thanks again and I'll keep you posted if I make anymore progress tonight.
Was This Post Helpful? 0
  • +
  • -

#25 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 120
  • View blog
  • Posts: 843
  • Joined: 05-December 13

Re: Passing a Certificate with SSLstream

Posted 28 September 2018 - 07:07 AM

For give my ignorance, forums get a lot of script kids and commonly they want app to app communication, which is why I assumed asked about the listener. Didn't notice it was for a (web server).

I'e been having trouble with this ::

requestResponse = GetResponse(sslStream, Encoding.UTF8)            
If requestResponse.ToLower.Contains("<greeting>") = True Then

It gave a null ref on the if statement, so the response isn't returning anything. And I'm 100% certain its not X509 at cause. Your code would not have executed to this point if it was. Sorry I haven't had much time to debug the rest of it as time isn't on my side with personal work. I think your server is rejecting my/our connection. But commonly an exception should have been thrown telling us that the server refused the connection, which it's not doing. Maybe its something else server side?

I recommend if you are on some kind of VPS, or dedicated server, to use something like Netstat to monitor your TCP connections and see what is actually connecting. You can use Netstat -an in command console.

Even though I've concluded your ssl cert problems are fixed, but your message not receiving is an entirely new problem.

Have said that Andrew, are you sure you want to continue working with this X509 library? I already hate it. Even when Google was building Android, they had this library as an option but opted for the bouncy castle library instead. I'm just thinking further down the road.
Was This Post Helpful? 0
  • +
  • -

#26 digitaldrew   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 21
  • Joined: 29-December 14

Re: Passing a Certificate with SSLstream

Posted 28 September 2018 - 09:40 AM

Thanks for taking the time to look some more and respond Sheepings. Would WireShark work instead of Netstat? I'll bring that up here in just a minute to see if it's actually getting connected, although it seems to me like it is. As mentioned in my private message, that's exactly where I'm having problems as well. Specifically during this IF statement in GetResponse
            If readBytes = 4 Then
                ' should get back 4 bytes that are the BigEndian length
                Dim totalResponseLength As Int32 = BigEndianInt32BytesToInt32(msgLengthBytes)
                ' subtract the bytes that comprise the length
                Dim responseLength As Int32 = totalResponseLength - 4

                ' get encoded message bytes
                Dim msgBytes(0 To (responseLength - 1)) As Byte
                readBytes = strm.Read(msgBytes, 0, responseLength)
                ret = encoding.GetString(msgBytes)
            End If



Because for some reason "readBytes" is coming back as 1 and not 4 like it should be. Here is the whole GetResponse function again:
    Private Function GetResponse(ByVal strm As System.Net.Security.SslStream, ByVal encoding As System.Text.Encoding) As String
        Dim ret As String = Nothing
        Try
            ' get encoded response length in Big Endian, Windows is Little Endian
            ' this array will be 4 bytes long
            Dim msgLengthBytes(0 To 3) As Byte
            Dim readBytes As Int32 = strm.Read(msgLengthBytes, 0, 4)

            If readBytes = 4 Then
                ' should get back 4 bytes that are the BigEndian length
                Dim totalResponseLength As Int32 = BigEndianInt32BytesToInt32(msgLengthBytes)
                ' subtract the bytes that comprise the length
                Dim responseLength As Int32 = totalResponseLength - 4

                ' get encoded message bytes
                Dim msgBytes(0 To (responseLength - 1)) As Byte
                readBytes = strm.Read(msgBytes, 0, responseLength)
                ret = encoding.GetString(msgBytes)
            End If
            Return ret
        Catch ex As Exception
            Return ret
        End Try
    End Function



They've assured me everything is working server-side, but I am going to try WireShark and see if I can verify a connection (maybe even try to see if the request packet is at least being sent as well).. I've verified they do recommend encoding in UTF-8, but could there be something else I'm missing here related to Big Endian and Little Endian? Should I not being doing any converting since a certificate is being used and the data should't really be touched when using a certificate?

I am starting to consider Bouncy Castle more and more, but also feel like this is really close. Almost like the request is not being sent or the response is not being received just because of something with SendRequest or GetResponse. Like you, from what I can tell it seems like the initial issue (the cert) has been resolved. Arg it's very frustrating. Just like you said - this library absolutely sucks!! But, a big thanks again for everything you've done to help me so far. It's way more than I would have been able to figure out on my own.
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2