Не удалось декодировать сертификат на клиенте new X509Certificate2()

Я использую этот маленький класс, который возвращает мне файл pfx в массиве байтов.

Сторона сервера:

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));

Затем я отправляю его клиенту (длина: 1654):

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

Как только клиент прочитал это, я хотел бы преобразовать его в класс сертификата: (длина здесь также 1654)

Я пытаюсь сделать новый сертификат X509Certificate2 (данные); и я получаю ошибку внизу. Это работает на стороне сервера. В чем дело?

Я также попробовал это с новым X509Certificate2 (data, string.Empty); и получил ту же ошибку

Ошибка System.Security.Cryptography.CryptographicException: невозможно декодировать сертификат. ---> System.Security.Cryptography.CryptographicException: Входные данные не могут быть закодированы как действительный сертификат. ---> System.ArgumentOutOfRangeException: не может быть отрицательным.

Имя параметра: длина

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

at Mono.Security.X509.X509Certificate.PEM (тип System.String, данные System.Byte[]) [0x00000] в: 0

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


person DreTaX    schedule 29.12.2015    source источник
comment
Вы используете Моно. Это не черный ящик, поэтому попробуйте вмешаться. Я помню, что в классе X509Certificate2 была ошибка. Не совсем уверен, что это было, но я думаю, что это было связано с чтением pfx из файла по сравнению с байтом []. Попробуйте сохранить байты в файл, а затем используйте new X509Certificate2(filepath, String.Empty) или new X509Certificate2(filepath, null).   -  person pepo    schedule 29.12.2015
comment
Ждать. Вы говорите, что я должен сделать байт файлом, а затем прочитать его?   -  person DreTaX    schedule 29.12.2015
comment
Да, точно. Запишите byte[] в файл, а затем используйте файл в X509Certificate2.   -  person pepo    schedule 29.12.2015
comment
Хорошо. Я надеюсь, что это работает. Скоро скажу результаты. Каким должно быть имя файла? .pfx?   -  person DreTaX    schedule 29.12.2015
comment
Для класса X509Certificate2 не имеет значения, какое расширение имеет файл. Но обычно расширение .p12 или .pfx.   -  person pepo    schedule 29.12.2015
comment
Хорошо, попробую через несколько минут.   -  person DreTaX    schedule 29.12.2015
comment
Итак, я сделал то, что вы предложили, записал полученный файл, используя File.WriteAllBytes(string path, byte[] bytes) и вывод: pastie.org/private/krps21snoe0dj0lrbhmgw | Затем я инициализировал X509Certificate2, используя путь и готовый пароль, я также пробовал это с импортом, оба в try catch. Я получил Входные данные не могут быть закодированы как действительный сертификат. что отличается от ошибки, которую я получал.   -  person DreTaX    schedule 29.12.2015
comment
Полный: pastie.org/private/dkmokyltaezfy50hnr4zxq   -  person DreTaX    schedule 29.12.2015
comment
Попробуйте загрузить pfx на стороне сервера. Либо вы создаете pfx, который не является pfx, либо при отправке pfx клиенту возникает ошибка кодирования. Кроме того, если бы вы могли предоставить дамп byte[] в кодировке base64, это было бы полезно. Я также попытался бы открыть pfx, дважды щелкнув файл .pfx в Windows (если Windows может его открыть, значит, он имеет правильную структуру, и проблема в другом).   -  person pepo    schedule 30.12.2015
comment
Загрузка самого байта на стороне сервера работает нормально. Как я могу предоставить этот дамп? Кстати: db.tt/a19g5vEc ‹- Думаю, это неверно.   -  person DreTaX    schedule 30.12.2015
comment
Да вы совершенно правы. pastie.org/private/0acswikxfyxkm7yakbpftg Байт сертификата, отправляемый клиенту, полностью отличается от байта сервера. В чем может быть проблема?   -  person DreTaX    schedule 30.12.2015
comment
Я не знаю, вы не написали ту часть кода :) Вы используете Encoding.ACSII или Encoding.UTF8 где-то в процессе?   -  person pepo    schedule 30.12.2015
comment
Честно говоря, я знаю, в чем проблема. В настоящее время я использую цикл for для чтения количества байтов. Сначала я использовал некоторое время, он читал байты до длины байта 1654, но после этого клиент просто завис, и цикл не прекращался, пока я проверял количество прочитанных данных. Я попробую это: stackoverflow.com/questions/26058594/ Если это не сработает, я вставлю код и сообщу вам об этом.   -  person DreTaX    schedule 30.12.2015
comment
stackoverflow .com/questions/6958255/ Вот дерьмо....   -  person DreTaX    schedule 31.12.2015


Ответы (2)


В Mono возникает ошибка при использовании конструктора X509Certificate2 для загрузки PKCS#12 с пустым паролем. исправление было отправлено для решения этой проблемы, но оно (вероятно) не выпущено в вашей версия моно.

Попробуйте сохранить байты в файл, а затем используйте new X509Certificate2(filepath, String.Empty) или new X509Certificate2(filepath, null) или создайте pfx с каким-нибудь непустым паролем по умолчанию.

person pepo    schedule 29.12.2015
comment
Я бы пошел с непустым паролем по умолчанию. - person jariq; 29.12.2015
comment
Неа. Я уже знаю об этом, и, как я уже упоминал выше. Я также пробовал это с новым X509Certificate2 (data, string.Empty); и получил ту же ошибку. Это байт PFX, а не pkcs - person DreTaX; 29.12.2015
comment
@DreTaX pfx - это ИМХО PKCS # 12. Вы пытались загрузить byte[] в конструктор, а не в имя файла. Его по-разному реализуют. Если вы хотите использовать byte[] в конструкторе X509Certificate2, создайте PFX с непустым паролем, и он должен работать. - person pepo; 29.12.2015
comment
Ммм. Но предоставление пароля зашифрует данные. Разве пароль не даст доступ к закрытому ключу? Кстати: db.tt/KzjxiQ4t - person DreTaX; 29.12.2015
comment
Что ж, я попытался указать пароль на стороне сервера, а затем использовать его на стороне клиента для доступа к нему, но все равно получаю ту же ошибку. Эта штука у меня на уме. - person DreTaX; 29.12.2015

Долгие размышления и просьбы о помощи наконец привели меня к решению.

Следующий способ или примеры постоянных подключений, которые у меня были, ВООБЩЕ НЕ РАБОТАЛИ.

На стороне сервера вы сначала должны получить длину байта, который вы хотите отправить, и записать его в поток. Скорее всего, это будет байт длиной 4.

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

На стороне клиента прочитайте этот байт и преобразуйте его в 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);

Этот метод будет читать байт, который вы отправляете с сервера, пока это не станет возможным.

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();
        }
    }
}

Это решение, похоже, работает довольно хорошо, в отличие от других примеров, представленных на странице документации Microsoft. Я надеюсь, что это поможет и другим.

person DreTaX    schedule 01.01.2016