Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/7: Рейтинг темы: голосов - 7, средняя оценка - 5.00
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32

Алгоритм ECDH в библиотеки BouncyCastle работает не так как в библиотеки .net 8

17.01.2024, 14:42. Показов 1819. Ответов 17

Студворк — интернет-сервис помощи студентам
Суть вопроса такая. Мне нужна очень простая вещь - реализация алгоритма ECDH с кривой secp256r1. Казалось бы эта кривая и этот алгоритм используется в TLS и куча других мест. Но вот найти нормальную реализацию в 1 файл, это просто неподсильная вещь. Да я нагуглил штук 30 полукорявых заброшенных исходников на githab. Но все эти проекты - каждый по сотни и более файлов в кривой структуре. И вообще создавать файл для одной строки, а большая часть в проектах как раз такие файлы - я вот вообще не понимаю такую моду. Это же вынос мозга. Проще самому написать реализацию чем разбираться в сотни этих фалов.

Всё это я написал потому как у меня есть реализация Curve25519 в виде одного фала с 400 строк кода. Всё прозрачно и понятно.

Ладно лирику отставили. Теперь к проблеме.

Готовая реализация криптографии на C# это просто исчезающий вид зверька. Самая известная библиотека это BouncyCastle. Я в своё время попытался вычленить от туда реализацию secp256r1, но в связи с стилем написания этой библиотеки, это оказалось практически невозможным. Парсить приходилось практически большую часть файлов из тысяч. Или сколько их там было? Да-да. Современный способ писания когда - максимальное наследование, интерфейсы, виртуальные и абстрактные методы. Внедрение зависимостей где только можно и не можно, и ещё куча всего что бы максимально склеить все файлы. Вместо 1 файла на 1000 строк, делаем 1000 файлов по 1 строке. И т.д.

В .net есть встроенная криптография. И там даже есть рабочая версия secp256r1.

Но как оказалось BouncyCastle и .net дают разный результат.

Я даже ухищерился сделать следующее. Создаю 2 экземпляра ecdh из BouncyCastle. Выдираю из них приватные и публичные ключи. Потом на основании этих ключей делаю 2 экземпляра ecdh из .net Не буду описывать отдельный квест как я искал способ в .net задать приватный и публичный ключ. Нормальным способом это не дано в этой кривой поделки.

Так вот секреты которые получаются в BouncyCastle и .net - РАЗНЫЕ. Но самое интересное что в .net секреты совпадают что говорит что в целом библиотека работает, но работает не так как надо.

Ну и вопрос.
Идеальным было бы найти нормальную нативную реализацию на С# ecdh на кривой secp256r1.
Второй вариант - может кто подскажет что я неправильно делаю с .net что она выдаёт не тот результат.

Эта же проблема с не правильным результатом обсуждалась здесь

Прикладываю тестовый проект для экспериментов - в консоль выводится секрет из BouncyCastle и .net на одних ключах. Там всё максимально понятно.

Но и тут код выложу для посмотреть:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
 
namespace Sevice
{
    class Program
    {
        static void Main(string[] _)
        {
            X9ECParameters ecParams = ECNamedCurveTable.GetByName("secp256r1");
            var curveparam = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
 
            ECKeyGenerationParameters keygenParams = new(curveparam, new SecureRandom());
 
            ECKeyPairGenerator generator = new();
            generator.Init(keygenParams);
            var keyPair = generator.GenerateKeyPair();
 
            var BobprivateKey = (ECPrivateKeyParameters)keyPair.Private;
            var BobpublicKey = (ECPublicKeyParameters)keyPair.Public;
 
 
            var keyPair2 = generator.GenerateKeyPair();
            var AliceprivateKey = (ECPrivateKeyParameters)keyPair2.Private;
            var AlicepublicKey = (ECPublicKeyParameters)keyPair2.Public;
 
 
            var ShareKeyAlice = BobpublicKey.Q.Multiply(AliceprivateKey.D);
            var ShareKeyBob = AlicepublicKey.Q.Multiply(BobprivateKey.D);
 
            /*
            Console.WriteLine("== Curve: {0} ", "secp256r1");
 
            Console.WriteLine("\n Bob Private key === ");
            Console.WriteLine("== D_b={0} ", BobprivateKey.D.ToString());
            Console.WriteLine("\n Bob Public key === ");
            Console.WriteLine("== Q_x={0} ", BobpublicKey.Q.AffineXCoord);
            Console.WriteLine("== Q_y={0} ", BobpublicKey.Q.AffineYCoord);
 
            Console.WriteLine("\n== Alice Private key === ");
            Console.WriteLine(" D_a={0} ", AliceprivateKey.D.ToString());
            Console.WriteLine("\n== Alice Public key === ");
            Console.WriteLine(" Q_x={0} ", AlicepublicKey.Q.AffineXCoord);
            Console.WriteLine(" Q_y={0} ", AlicepublicKey.Q.AffineYCoord);
 
            Console.WriteLine("\n\n== Shared key === ");
            Console.WriteLine(" Shared key (Bob) ==={0} ", ShareKeyBob.Normalize().AffineXCoord);
            Console.WriteLine(" Shared key (Alice) ==={0} ", ShareKeyAlice.Normalize().AffineXCoord);
            */
 
            byte[] q1_pub = AlicepublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] q1_priv = AliceprivateKey.D.ToByteArrayUnsigned();
            //===
            byte[] q2_pub = BobpublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] q2_priv = BobprivateKey.D.ToByteArrayUnsigned();
            //~~~
            byte[] sec1 = ShareKeyBob.Normalize().AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] sec2 = ShareKeyAlice.Normalize().AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
 
            ECPoint q1 = new()
            {
                X = AlicepublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
                Y = AlicepublicKey.Q.AffineYCoord.ToBigInteger().ToByteArrayUnsigned(),
            };
            ECPoint q2 = new()
            {
                X = BobpublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
                Y = BobpublicKey.Q.AffineYCoord.ToBigInteger().ToByteArrayUnsigned(),
            };
 
            //===
            ECParameters parameters1 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = q2,
                D = q2_priv
            };
            //===
            using ECDiffieHellman qqq1 = ECDiffieHellman.Create(parameters1);
 
            //***************************
            ECParameters parameters2 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = q1,
                D = q1_priv
            };
            //===
            using ECDiffieHellman qqq2 = ECDiffieHellman.Create(parameters2);
 
            //***************************
 
            byte[] sec3 = qqq1.DeriveKeyMaterial(qqq2.PublicKey);
            byte[] sec4 = qqq2.DeriveKeyMaterial(qqq1.PublicKey);
 
            byte[] sec5 = qqq1.DeriveKeyFromHash(qqq2.PublicKey, HashAlgorithmName.SHA256);
            byte[] sec6 = qqq2.DeriveKeyFromHash(qqq1.PublicKey, HashAlgorithmName.SHA256);
 
 
            Console.WriteLine("Bouncy sec1:" + BitConverter.ToString(sec1).Replace("-", ""));
            Console.WriteLine("Bouncy sec2:" + BitConverter.ToString(sec2).Replace("-", ""));
            Console.WriteLine("");
            Console.WriteLine(".net sec3:  " + BitConverter.ToString(sec3).Replace("-", ""));
            Console.WriteLine(".net sec4:  " + BitConverter.ToString(sec4).Replace("-", ""));
            Console.WriteLine("");
 
            Console.ReadKey();
        }
    }
}
Вложения
Тип файла: 7z Curv.7z (57.2 Кб, 3 просмотров)
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
17.01.2024, 14:42
Ответы с готовыми решениями:

Ошибка при использовании библиотеки в .NET Framework 4.7.2 и все работает в .NET 5.0
Есть сторонняя библиотека, которую необходимо использовать. При использовании в .NET 5.0 (поддержка прекращена) все работает. При...

Как посмотреть какие библиотеки тянут другие библиотеки процесса
Привет, есть процесс, у него подтянуты dll'ки список которых я могу просматривать прогой Process Explorer. НО вопрос как посмотреть...

Как включить библиотеки в исполняемый файл, чтобы не приходилось ставить библиотеки (Eclipse)
Как включить библиотеки в конечный EXE как это сделано в bilder, чтобы не приходилось ставить библиотеки.

17
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 16:50  [ТС]
Вчера целый день экспериментировал и у же к вечеру голова была никакая и не смог доделать один шаг.

C#
1
byte[] hash = SHA256.HashData(sec1);
И вуаля! Недо код .net секрет стал равен секрету из BouncyCastle. Собственно я вчера был близок к этому казалось бы очевидному шагу. Да и по ссылке приведённой мной это почти прямым текстом говорится.

Оставлю за кадром - НАХРЕНА это делает net. Теперь придётся лезть в сорцы и попытаться отучивать его от этого действия.
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 16:50
Цитата Сообщение от DeveloperD Посмотреть сообщение
Так вот секреты которые получаются в BouncyCastle и .net - РАЗНЫЕ.
Цитата Сообщение от DeveloperD Посмотреть сообщение
Второй вариант - может кто подскажет что я неправильно делаю с .net что она выдаёт не тот результат.
У меня результаты совпадают...
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
 
namespace Sevice
{
    class Program
    {
        static void Main(string[] _)
        {
            X9ECParameters ecParams = ECNamedCurveTable.GetByName("secp256r1");
            var curveparam = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
 
            ECKeyGenerationParameters keygenParams = new(curveparam, new SecureRandom());
 
            ECKeyPairGenerator generator = new();
            generator.Init(keygenParams);
            var keyPair = generator.GenerateKeyPair();
 
            var BobprivateKey = (ECPrivateKeyParameters)keyPair.Private;
            var BobpublicKey = (ECPublicKeyParameters)keyPair.Public;
 
 
            var keyPair2 = generator.GenerateKeyPair();
            var AliceprivateKey = (ECPrivateKeyParameters)keyPair2.Private;
            var AlicepublicKey = (ECPublicKeyParameters)keyPair2.Public;
 
 
            var ShareKeyAlice = BobpublicKey.Q.Multiply(AliceprivateKey.D);
            var ShareKeyBob = AlicepublicKey.Q.Multiply(BobprivateKey.D);
 
            /*
            Console.WriteLine("== Curve: {0} ", "secp256r1");
 
            Console.WriteLine("\n Bob Private key === ");
            Console.WriteLine("== D_b={0} ", BobprivateKey.D.ToString());
            Console.WriteLine("\n Bob Public key === ");
            Console.WriteLine("== Q_x={0} ", BobpublicKey.Q.AffineXCoord);
            Console.WriteLine("== Q_y={0} ", BobpublicKey.Q.AffineYCoord);
 
            Console.WriteLine("\n== Alice Private key === ");
            Console.WriteLine(" D_a={0} ", AliceprivateKey.D.ToString());
            Console.WriteLine("\n== Alice Public key === ");
            Console.WriteLine(" Q_x={0} ", AlicepublicKey.Q.AffineXCoord);
            Console.WriteLine(" Q_y={0} ", AlicepublicKey.Q.AffineYCoord);
 
            Console.WriteLine("\n\n== Shared key === ");
            Console.WriteLine(" Shared key (Bob) ==={0} ", ShareKeyBob.Normalize().AffineXCoord);
            Console.WriteLine(" Shared key (Alice) ==={0} ", ShareKeyAlice.Normalize().AffineXCoord);
            */
 
            byte[] q1_pub = AlicepublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] q1_priv = AliceprivateKey.D.ToByteArrayUnsigned();
            //===
            byte[] q2_pub = BobpublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] q2_priv = BobprivateKey.D.ToByteArrayUnsigned();
            //~~~
            byte[] sec1 = ShareKeyBob.Normalize().AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
            byte[] sec2 = ShareKeyAlice.Normalize().AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
 
            ECPoint q1 = new()
            {
                X = AlicepublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
                Y = AlicepublicKey.Q.AffineYCoord.ToBigInteger().ToByteArrayUnsigned(),
            };
            ECPoint q2 = new()
            {
                X = BobpublicKey.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned(),
                Y = BobpublicKey.Q.AffineYCoord.ToBigInteger().ToByteArrayUnsigned(),
            };
 
            //===
            ECParameters parameters1 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = q2,
                D = q2_priv
            };
            //===
            using ECDiffieHellman qqq1 = ECDiffieHellman.Create(parameters1);
 
            //***************************
            ECParameters parameters2 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = q1,
                D = q1_priv
            };
            //===
            using ECDiffieHellman qqq2 = ECDiffieHellman.Create(parameters2);
 
            //***************************
 
            byte[] sec3 = qqq1.DeriveKeyMaterial(qqq2.PublicKey);
            byte[] sec4 = qqq2.DeriveKeyMaterial(qqq1.PublicKey);
 
            byte[] sec5 = qqq1.DeriveKeyFromHash(qqq2.PublicKey, HashAlgorithmName.SHA256);
            byte[] sec6 = qqq2.DeriveKeyFromHash(qqq1.PublicKey, HashAlgorithmName.SHA256);
 
            byte[] sec7 = qqq1.DeriveRawSecretAgreement(qqq2.PublicKey);
            byte[] sec8 = qqq2.DeriveRawSecretAgreement(qqq1.PublicKey);
 
            Console.WriteLine("Bouncy sec1:" + BitConverter.ToString(sec1).Replace("-", ""));
            Console.WriteLine("Bouncy sec2:" + BitConverter.ToString(sec2).Replace("-", ""));
            Console.WriteLine("");
            Console.WriteLine(".net sec3:  " + BitConverter.ToString(sec3).Replace("-", ""));
            Console.WriteLine(".net sec4:  " + BitConverter.ToString(sec4).Replace("-", ""));
            Console.WriteLine("");
            Console.WriteLine(".net sec5:  " + BitConverter.ToString(sec5).Replace("-", ""));
            Console.WriteLine(".net sec6:  " + BitConverter.ToString(sec6).Replace("-", ""));
            Console.WriteLine("");
            Console.WriteLine(".net sec7:  " + BitConverter.ToString(sec7).Replace("-", ""));
            Console.WriteLine(".net sec8:  " + BitConverter.ToString(sec8).Replace("-", ""));
 
            Console.ReadKey();
        }
    }
}
1
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 17:13  [ТС]
У меня результаты совпадают...
Спасибо за ответ. Уже сам разобрался. Просто вчера от всей это криптографии крыша уже ехала. Сейчас пытаясь более менее на свежую голову понять что же меня вчера запутало и пришёл к выводу - что я решил что функция:

C#
1
DeriveKeyMaterial
У которой в описании только:
This method must be implemented by derived classes. In order to conform to the contract, it cannot be abstract.
Должна была возвращать чистый вариант раз еcть куча функций по типу:

C#
1
2
3
DeriveKeyTls
DeriveKeyFromHmac
DeriveKeyFromHash
Хотя в экспериментах я вчера видел что эта функция возвращает тоже самое что и DeriveKeyFromHash что меня вообще уводило в сторону о глючнутости сборки.
Опять же поиск как добавить сторонний публичный ключ в ECDiffieHellman тоже меня добил. Функции просто нету для этого. Нашёл только костыльный способ через создания нового экземпляра ECDiffieHellman с параметрами и выдёргивания уже объекта ECDiffieHellmanPublicKey. Потому как DeriveKeyMaterial больше ничего не жрёт. Хотя тупо передать туда открытый ключ в виде байтового массива - это самый очевидный способ работы.
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 17:22
Цитата Сообщение от DeveloperD Посмотреть сообщение
Нашёл только костыльный способ через создания нового экземпляра ECDiffieHellman с параметрами и выдёргивания уже объекта ECDiffieHellmanPublicKey.
Это как раз таки нормальный способ...
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 17:46  [ТС]
C#
1
Это как раз таки нормальный способ...
Т.е. инициализация нового объекта, под который выделяется память, и который ещё нужно потом диспозить ради только того что бы была возможность передать открытый ключ это по вашему норма?

У нас уже есть один первоначальный объект ECDiffieHellman у которого есть метод ExportSubjectPublicKeyInfo который сразу возвращает публичный ключ. Правда и тут нормально не возвращает - с мусором. Но хоть мусор легко отсеять - нормальные данные идут с 27 байта. Да да я знаю что это за мусор. Вот только он не участвует в протоколе обмена ключами.

Так вот нормально поведение - которое я встречал в других библиотеках и в том же BouncyCastle, это передать в тот же объект публичный ключ принятый от клиента, который является набором байт и сразу получит общий секрет.

Вместо этого я должен костылить код с созданием поинтеров кривой, где я должен вручную парсить байтовый ключ. А потом ещё и создавать новый объект ECDiffieHellman. Для всего этого выделяется память что явно не способствует скорости работы.

Я не считаю это нормальным. Просто криптография в .net сильно не доделанная. Ещё пару лет назад в ней вообще ничего по элептичесим кривым не было. И реализации secp256r1 тоже не было.
Но сейчас хоть с мёртвой точки сдвинулось
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 19:08
Цитата Сообщение от DeveloperD Посмотреть сообщение
У нас уже есть один первоначальный объект ECDiffieHellman у которого есть метод ExportSubjectPublicKeyInfo который сразу возвращает публичный ключ. Правда и тут нормально не возвращает - с мусором. Но хоть мусор легко отсеять - нормальные данные идут с 27 байта. Да да я знаю что это за мусор. Вот только он не участвует в протоколе обмена ключами.
Цитата Сообщение от DeveloperD Посмотреть сообщение
Вместо этого я должен костылить код с созданием поинтеров кривой, где я должен вручную парсить байтовый ключ.
Так не надо ничего в ручную парсить, есть методы ExportParameters/ExportExplicitParameters из которых можно получить все значения по отдельности...
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 19:26  [ТС]
ExportParameters/ExportExplicitParameters
ExportParameters делает экспорт из существующего уже созданного и настроенного объекта ECDiffieHellman.

С экспортом проблемы нету - ExportSubjectPublicKeyInfo возвращает только публичный ключ в открытом виде. Байтовый массив. Лишние 27 байтов на заголовок - так, лёгкая неприятность. ExportParameters же возвращает много лишнего, под которое выделяется память. И тормозит работу алгоритма на пустом месте.

Проблема в импорте. Который принимает только ECParameters. И который опять же я должен создавать путём парса ключа клиента. От клиента приходит ключ - байтовый массив. И который нельзя напрямую передать в импорт. Просто не создана ещё функция.

И вообще в пределах одного кода весьма удобно бросаться друг в друга объектами. Но это же алгоритм обмена ключами между 2 сторонами. А обмен может быть только в виде байтовых массивах. Следовательно и код должен напрямую, без геморроя и оверхеда работать с байтовыми массивами.

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

В целом работать с этим можно. Сейчас как раз провожу тесты разных библиотек на скорость работы.
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 19:43
Цитата Сообщение от DeveloperD Посмотреть сообщение
ExportParameters делает экспорт из существующего уже созданного и настроенного объекта ECDiffieHellman.
Для вызова ExportSubjectPublicKeyInfo тоже требуется уже созданный и настроенный объект ECDiffieHellman...

Цитата Сообщение от DeveloperD Посмотреть сообщение
Проблема в импорте. Который принимает только ECParameters.
Там есть куча методов, принимающих ключи в разных форматах...

Цитата Сообщение от DeveloperD Посмотреть сообщение
ExportParameters же возвращает много лишнего, под которое выделяется память.
Да то же самое он возвращает что и ExportSubjectPublicKeyInfo, только по отдельности...

Цитата Сообщение от DeveloperD Посмотреть сообщение
И тормозит работу алгоритма на пустом месте.
Есть какие-то пруфы что что-то тормозит, сравнение с другими библиотеками?

Цитата Сообщение от DeveloperD Посмотреть сообщение
Мне не нравится что для нормальной работы эклиптики в .net нужно создавать кучу паразитных объектов, которые будут жрать память. Это как раз тот случай - где нужна скорость и оптимальность.
Там все типы (ECPoint, ECParameters) кроме самого ECDiffieHellman являются структурами и с ними нет никаких проблем в этом плане...

Цитата Сообщение от DeveloperD Посмотреть сообщение
Сейчас как раз провожу тесты разных библиотек на скорость работы.
Хотелось бы увидеть код тестов и результаты...
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 20:16  [ТС]
Для вызова ExportSubjectPublicKeyInfo тоже требуется уже созданный и настроенный объект ECDiffieHellman
Ну так он уже есть. Для обмена ключами каждая из сторон создаёт 1 этот объект. Далее ExportSubjectPublicKeyInfo даёт нам публичный ключ который сгенерирован в этом объекте. Потом каждая сторона кидает свой публичный ключ другой стороне. И далее мне хотелось бы просто загрузиьт этот публичный ключ в уже созданый мной объект и сразу полчить секрет. Просто вызвать функцию что то вроде Get_secret(byte[] publc_key).

Вместо этого мне нужно создать ещё один ECDiffieHellman. Создать ECParameters и загрузить в этот новый ECDiffieHellman. И всё для того что бы я мог получить объект ECDiffieHellmanPublicKey который я уже могу загрузить в первый ECDiffieHellman.

Там есть куча методов, принимающих ключи в разных форматах...
У самой структуры вообще один бесполезный метод Validate. Но повторяю - создать структуру это вообще не проблема. Распарсить один ключ на 2 массива не проблема. Напрягает что вообще это делать надо. Но проблема не в создании структуры, а в инициализации на ровном месте бесполезного нового объекта ECDiffieHellman только для того что бы из него выдрать публичный ключ.

Да то же самое он возвращает что и ExportSubjectPublicKeyInfo, только по отдельности...
Вот это по отдельности мне и не нравится. Зачем мне нужен приватный ключ например? Под него же тоже выделяется буфер, потом этот буфер надо утилизировать. Другая инфа мне тоже не нужна. Для работы всего протокола нужен только публичный ключ. И всё.

Есть какие-то пруфы что что-то тормозит, сравнение с другими библиотеками?
Ну вообще я говорил что не тормозит сам по себе, а то что код без лишних выделений памяти будет работать быстрее.
И да я тут провёл маленький тест. Сравнил BouncyCastle и .net. В тесте производится обмен ключами между 2 сторонами.

Результат показал что .net реализация быстрее BouncyCastle на 22%

1000 итераций
net : 3.8 секунды
boun: 5сек

В принципе меня результат радует. Код приведу. Тест набросан на коленке поэтому на объективность не претендует. но общую картину показывает.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
 
namespace Crypt
{
    class Secp256r1
    {
        static readonly ECDomainParameters ecSpec;
        static readonly X9ECParameters ecP;
        //~~~
        AsymmetricCipherKeyPair aKeyPair;
        //~~~
        static Secp256r1()
        {
            ecP = X962NamedCurves.GetByName("prime256v1");
            ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
        }
        public byte[] CreateKey()
        {
            IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("ECDH");
            g.Init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
            //~~~
            aKeyPair = g.GenerateKeyPair();
            //>>>>>>
            return ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded(false);
        }
        public byte[] GetSecret(byte[] pub)
        {
            ECPoint q = ecP.Curve.DecodePoint(pub);
            ECPublicKeyParameters peerPub = new(q, ecSpec);
 
            //===
            IBasicAgreement aKeyAgree = AgreementUtilities.GetBasicAgreement("ECDHC");
            aKeyAgree.Init(aKeyPair.Private);
            //===
            BigInteger k1 = aKeyAgree.CalculateAgreement(peerPub);
            //>>>>>>
            return k1.ToByteArray();
        }
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using Crypt;
using System.Diagnostics;
using System.Security.Cryptography;
 
namespace Sevice
{
    class Program
    {
        static void net_test()
        {
            using var ecd1 = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
            //~~~
            byte[] pub1 = ecd1.ExportSubjectPublicKeyInfo();
            byte[] pub1_1_32 = new byte[32];
            byte[] pub1_2_32 = new byte[32];
            //~~~
            Buffer.BlockCopy(pub1, 27, pub1_1_32, 0, 32);
            Buffer.BlockCopy(pub1, 27 + 32, pub1_2_32, 0, 32);
            //~~~
            ECParameters par1 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = {
                    X = pub1_1_32,
                    Y = pub1_2_32
                }
            };
            //~~~
            using var ecd1_key = ECDiffieHellman.Create(par1);
 
            //**********
            using var ecd2 = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
            //~~~
            byte[] pub2 = ecd2.ExportSubjectPublicKeyInfo();
            byte[] pub2_1_32 = new byte[32];
            byte[] pub2_2_32 = new byte[32];
            //~~~
            Buffer.BlockCopy(pub2, 27, pub2_1_32, 0, 32);
            Buffer.BlockCopy(pub2, 27 + 32, pub2_2_32, 0, 32);
            //~~~
            ECParameters par2 = new()
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = {
                    X = pub2_1_32,
                    Y = pub2_2_32
                }
            };
            //~~~
            using var ecd2_key = ECDiffieHellman.Create(par2);
 
            //************
            byte[] sec1 = ecd1.DeriveRawSecretAgreement(ecd2_key.PublicKey);
            byte[] sec2 = ecd2.DeriveRawSecretAgreement(ecd1_key.PublicKey);
        }
        static void bounc_test()
        {
            var secp1 = new Secp256r1();
            byte[] clt_pub_key1 = secp1.CreateKey();
            //===
            var secp2 = new Secp256r1();
            byte[] clt_pub_key2 = secp2.CreateKey();
            //===
            byte[] pre_master_secret1 = secp1.GetSecret(clt_pub_key2);
            byte[] pre_master_secret2 = secp2.GetSecret(clt_pub_key1);
        }
 
        static void Main(string[] _)
        {
            var timer = new Stopwatch();
            timer.Start();
 
            for (int i = 0; i < 1000; i++) net_test();
            //for (int i = 0; i < 1000; i++) bounc_test();
 
            Console.WriteLine(timer.Elapsed);
 
            Console.ReadKey(); 
        }
    }
}
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 21:09
Я так и не понял зачем вы в тесте .NET ECDiffieHellman.Create 4 раза вызываете, если можно упростить до 2 и результат не меняется...

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System.Diagnostics;
using System.Security.Cryptography;
 
namespace CurvTest
{
    internal class Program
    {
        static void net_test()
        {
            using var ecd1 = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
            //~~~
            //byte[] pub1 = ecd1.ExportSubjectPublicKeyInfo();
            //~~~
            //using var ecd1_key = ECDiffieHellman.Create();
            //ecd1_key.ImportSubjectPublicKeyInfo(pub1, out _);
 
            //**********
            using var ecd2 = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
            //~~~
            //byte[] pub2 = ecd2.ExportSubjectPublicKeyInfo();
            //~~~
            //using var ecd2_key = ECDiffieHellman.Create();
            //ecd2_key.ImportSubjectPublicKeyInfo(pub2, out _);
 
            //************
            //byte[] sec1 = ecd1.DeriveRawSecretAgreement(ecd2_key.PublicKey);
            //byte[] sec2 = ecd2.DeriveRawSecretAgreement(ecd1_key.PublicKey);
            byte[] sec1 = ecd1.DeriveRawSecretAgreement(ecd2.PublicKey);
            byte[] sec2 = ecd2.DeriveRawSecretAgreement(ecd1.PublicKey);
 
            //Console.WriteLine($".net sec1 {Convert.ToHexString(sec1)}");
            //Console.WriteLine($".net sec2 {Convert.ToHexString(sec2)}");
        }
        static void bounc_test()
        {
            var secp1 = new Secp256r1();
            byte[] clt_pub_key1 = secp1.CreateKey();
            //===
            var secp2 = new Secp256r1();
            byte[] clt_pub_key2 = secp2.CreateKey();
            //===
            byte[] pre_master_secret1 = secp1.GetSecret(clt_pub_key2);
            byte[] pre_master_secret2 = secp2.GetSecret(clt_pub_key1);
 
            //Console.WriteLine($"bounc sec1 {Convert.ToHexString(pre_master_secret1)}");
            //Console.WriteLine($"bounc sec2 {Convert.ToHexString(pre_master_secret2)}");
        }
 
        static void Main(string[] _)
        {
            var timer = new Stopwatch();
            timer.Start();
 
            for (int i = 0; i < 1000; i++)
                bounc_test();
 
            Console.WriteLine(timer.Elapsed);
 
            timer.Restart();
 
            for (int i = 0; i < 1000; i++)
                net_test();
 
            Console.WriteLine(timer.Elapsed);
 
            Console.ReadKey();
        }
    }
}
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 21:24  [ТС]
Протестировал нативную реализацию Curve25519. 1 файл в 400 строк когда. Без бесконечного выделения объектов и прочего мусора.

1000 итераций

0.4 секунды

Теперь вы понимаете почему я гоню на не оптимальную реализацию?

Хотя тут мои придирки не совсем верны. Curve25519 была создана как раз для максимально быстрой работы и она быстрее тех же nistP256.

Но прирост скорости в сотни процентов впечатляет.

Хотя в криптографии скорость функции может сыграть злую шутку. Для защиты важна скорость прямого перебора. Чем медленнее алгоритм тем менее подвержен брутфорсу.

Добавлено через 6 минут
Я так и не понял зачем вы в тесте .NET ECDiffieHellman.Create 4 раза
Ну как же не понятно. 2 раза вызывается только на 1 стороне. И на другой стороне то же 2 раза. Это 2 разные машины. Которые по сети передают публичный ключ в виде байтового массива друг другу.

Я лишь объединил эти 2 машины в 1 месте.

Вот вы передаёте ecd2.PublicKey сразу. Потому как 2 сторона существует в коде вот тут. Просто бери объект.

Но в реальности вторая сторона существует только в виде байтового массива пришедшего по сети. Вот что бы этот байтовый массив превратить в ECDiffieHellmanPublicKey и приходится костыли делать в виде мусорного создания ECDiffieHellman.
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,574
17.01.2024, 22:30
Цитата Сообщение от DeveloperD Посмотреть сообщение
Вот это по отдельности мне и не нравится. Зачем мне нужен приватный ключ например? Под него же тоже выделяется буфер, потом этот буфер надо утилизировать. Другая инфа мне тоже не нужна. Для работы всего протокола нужен только публичный ключ. И всё.
Я тут посмотрел исходники, и обнаружил что ExportSubjectPublicKeyInfo вызывает внутри себя ExportParameters. Поэтому нет никакого смысла использовать ExportSubjectPublicKeyInfo если нужно получить любой из ключей... Проще вызвать ExportParameters и взять то что нужно из полученного объекта ECParameters...
1
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 23:00  [ТС]
Спасибо. За наводку. Я вот тут тестика написал чисто для проверки что быстрее будет для использования ExportSubjectPublicKeyInfo или ECParameters

Для копирования буферов применил Buffer.MemoryCopy - это чуть ли не одна инструкция процессора (хотя я не смотрел что там компилится). Как то собирал статистику для копирования буферов - это самая быстрая. Вобщем взял её для нивелирования воздействия самого копирования.

Короче в среднем на 1000 итераций

00:00:00.2591873
00:00:00.2862960
Получается что верхняя часть кода побыстрее будет

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using var ecd1 = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
 
//Прогрев===
for (int i = 0; i < 1000; i++)
{
    byte[] pub_raw = ecd1.ExportSubjectPublicKeyInfo();
    //~~~
    byte[] pub_key = new byte[64];
    //~~~
    unsafe
    {
        fixed (byte* p_pub_raw = pub_raw, p_pub_key = pub_key)
        {
            Buffer.MemoryCopy(p_pub_key + 27, p_pub_key, 64, 64);
        }
    }
}
for (int i = 0; i < 1000; i++)
{
    ECParameters par3 = ecd1.ExportParameters(false);
    //~~~
    byte[] pub_key = new byte[64];
    //~~~
    unsafe
    {
        fixed (byte* p_pub_key = pub_key, p_k1 = par3.Q.X, p_k2 = par3.Q.X)
        {
            Buffer.MemoryCopy(p_k1, p_pub_key, 32, 32);
            Buffer.MemoryCopy(p_k2, p_pub_key + 32, 32, 32);
        }
    }
}
 
 
//Test############
var timer = new Stopwatch();
timer.Start();
//===
for (int i = 0; i < 1000; i++)
{
    byte[] pub_raw = ecd1.ExportSubjectPublicKeyInfo();
    //~~~
    byte[] pub_key = new byte[64];
    //~~~
    unsafe
    {
        fixed (byte* p_pub_raw = pub_raw, p_pub_key = pub_key)
        {
            Buffer.MemoryCopy(p_pub_key + 27, p_pub_key, 64, 64);
        }
    }
}
//===
Console.WriteLine(timer.Elapsed);
timer.Restart();
//===
for (int i = 0; i < 1000; i++)
{
    ECParameters par3 = ecd1.ExportParameters(false);
    //~~~
    byte[] pub_key = new byte[64];
    //~~~
    unsafe
    {
        fixed (byte* p_pub_key = pub_key, p_k1 = par3.Q.X, p_k2 = par3.Q.X)
        {
            Buffer.MemoryCopy(p_k1, p_pub_key, 32, 32);
            Buffer.MemoryCopy(p_k2, p_pub_key + 32, 32, 32);
        }
    }
}
//===
Console.WriteLine(timer.Elapsed);
Console.ReadKey();
0
HF
 Аватар для HF
1316 / 895 / 200
Регистрация: 09.09.2011
Сообщений: 2,689
Записей в блоге: 2
17.01.2024, 23:27
Цитата Сообщение от DeveloperD Посмотреть сообщение
00:00:00.2591873
00:00:00.2862960
И где это будет работать? Сетевые пакеты "заворачивать" 24/7? Или же пару раз по клику что-то выполнить?
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 23:29  [ТС]
И где это будет работать?
Высоко нагруженный сервер с шифро-соединениями.
0
HF
 Аватар для HF
1316 / 895 / 200
Регистрация: 09.09.2011
Сообщений: 2,689
Записей в блоге: 2
17.01.2024, 23:35
Цитата Сообщение от DeveloperD Посмотреть сообщение
Высоко нагруженный сервер с шифро-соединениями.
Ну то есть первый вариант? "Сетевые пакеты "заворачивать" 24/7?" Типа Telegram сервер?

P.S. Любопытствую просто, для опыта.
P.S.2. Цитировать не страшно. Не бойтесь оставлять никнейм.
0
2 / 2 / 0
Регистрация: 07.08.2023
Сообщений: 32
17.01.2024, 23:55  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
Ну то есть первый вариант? "Сетевые пакеты "заворачивать" 24/7?" Типа Telegram сервер?
Ну скажем так. Сейчас в сети практически всё ходит по tls. Либо SSH. Либо ещё что шифрованное. И там и там есть процедуры обмена ключами. Вообще все сайты, даже самые бессмысленные. Кстати никто не догадывается сколько на это шифромощностей уходит? Биткоин нервно курит в сторонке.

Так вот для обмена ключами раньше был RSA, но он умер по причине врождённого порока. Стоилло с сервера стянуть приватный ключ - и всю переписку можно расшифровать. Конечно для этого её надо было бы заранее сохранять. С элиптикой такой вариант не проходит. У неё просто нету постоянного приватного ключа. Сейчас все сидят на элиптеке.

И да - с letsencrypt - сейчас это удовольствие не только весёлое но и бесплатное.

P.S. на вопрос так и не ответил. Для сетевого сервера. А игровой ли (где важны микросекунды) или сайт какой где одна страница открывает по 5 tls соединений сразу и 100 пользователей устраивают DDOS атаку, не столь важно.

OFFTOP
Вчера при отладке заметил у мозилы интересное нововведение. Она запоминает прошлые запросы. И стоит в окне адреса ввести одну букву и если эта буква будет как у вашего сайта - то будет сразу же установлено соединение к сайту, да же если вы в результате введёт совершенно другой адрес. Короче DDOS смотрю будет только увеличиваться со временем )
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
17.01.2024, 23:55
Помогаю со студенческими работами здесь

Как с подгруженой библиотеки сделать вызов функции которая находится в файле подгрузки библиотеки?
Вообщем есть файл main.c, внём обявленно несколько функций, переменных и идёт подгрузка библиотеки, как-то так: lib = dlopen(library,...

Библиотеки C++ : Как использовать в C# .Net?
Здравствуйте! Я студент-вечерник, сейчас перехожу на 2-й курс (в среди .net вообще не шарю, умею только с виндоус формочками...

Подключение файла библиотеки ( как то так)
вот мой изначальный код: &lt;? class lab { public $h=array(10.01,9.95,9.82,10.1,10.15); public...

как вытащить JS-файл из библиотеки asp.net
Народ подскажите, как вытащить JS-файл из библиотеки asp.net. Т.Е. при загрузке страницы, V.Studio создает динамические JavaScript файлы...

Можно ли упаковать в exe'шник используемые в проекте библиотеки так, чтобы можно было запустить программу на машине без .NET
Я погуглил, но нашел только очень дорогие программы. А хотелось бы узнать насчет предложенного мной способа)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Новые блоги и статьи
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru