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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With