Search code examples
c#sslx509certificate2

Unable to decode certificate at client new X509Certificate2()


I'm using this little class which returns me a pfx file in byte array.

Server side:

byte[] serverCertificatebyte;
var date = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day);
serverCertificatebyte = Certificate.CreateSelfSignCertificatePfx("CN=RustBuster" + RandomString(5),
    date,
    date.AddDays(7));

Then I send It to the client (length: 1654):

tcpClient.GetStream().Write(serverCertificatebyte , 0, serverCertificatebyte .Length);

Once the client read It, I would like to convert It to a certificate class: (Length is also 1654 here)

I try to do a new X509Certificate2(data); and I get the error down below. This works on server side. What's the matter?

I also tried It with new X509Certificate2(data, string.Empty); and got the same error

Error System.Security.Cryptography.CryptographicException: Unable to decode certificate. ---> System.Security.Cryptography.CryptographicException: Input data cannot be coded as a valid certificate. ---> System.ArgumentOutOfRangeException: Cannot be negative.

Parameter name: length

at System.String.Substring (Int32 startIndex, Int32 length) [0x00000] in :0

at Mono.Security.X509.X509Certificate.PEM (System.String type, System.Byte[] data) [0x00000] in :0

at Mono.Security.X509.X509Certificate..ctor (System.Byte[] data) [0x00000] in :0


Solution

  • Long thoughts and help requests finally lead me to a solution.

    The following way or examples to a persistent connections that I had DID NOT WORK AT ALL.

    At the server side you first must get the length of the byte you would like to send, and write It to the stream. This most likely going to be a byte having the length of 4.

    byte[] intBytes = BitConverter.GetBytes(serverCertificatebyte.Length);
    Array.Reverse(intBytes);
    

    On the client side, read that byte, and convert It to an int:

    byte[] length = new byte[4];
    int red = 0;
    while (true)
    {
        red = stream.Read(length, 0, length.Length);
        if (red != 0)
        {
            break;
        }
    }
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(length);
    }
    int i = (BitConverter.ToInt32(length, 0)); // The length of the byte that the server sent
    // By this time your server already sent the byte (Right after you sent the length) tcpClient.GetStream().Write(byte, 0, byte.Length);
    byte[] data = ByteReader(i);
    

    This method is going to read the byte that you send from the server until It's possible

    internal byte[] ByteReader(int length)
    {
        using (NetworkStream stream = client.GetStream())
        {
            byte[] data = new byte[length];
            using (MemoryStream ms = new MemoryStream())
            {
                int numBytesRead;
                int numBytesReadsofar = 0;
                while (true)
                {
                    numBytesRead = stream.Read(data, 0, data.Length);
                    numBytesReadsofar += numBytesRead;
                    ms.Write(data, 0, numBytesRead);
                    if (numBytesReadsofar == length)
                    {
                        break;
                    }
                }
                return ms.ToArray();
            }
        }
    }
    

    This solution seems to be working pretty well unlike the other examples that were provided on the microsoft documentation page. I hope this will help others too.