Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 905
DreTaX Avatar asked Sep 25 '22 20:09

DreTaX


2 Answers

There is a bug in Mono when using X509Certificate2 constructor to load PKCS#12 with empty password. A patch has been submitted to fix this issue but it is (probably) not released in your version of mono.

Try to save the bytes to a file and then use new X509Certificate2(filepath, String.Empty) or new X509Certificate2(filepath, null) or create pfx with some default non-empty password.

like image 167
pepo Avatar answered Dec 31 '22 22:12

pepo


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.

like image 20
DreTaX Avatar answered Dec 31 '22 22:12

DreTaX