feat(cryptocli): Go-клиент через PKCS#11 — КриптоПро CSP, Рутокен, etc.
Заменили stub-клиент на полноценный PKCS#11 wrapper через
github.com/miekg/pkcs11. Поддерживает любой PKCS#11-совместимый
провайдер: КриптоПро CSP (libcppkcs11.so), Рутокен ЭЦП 2.0
(librtpkcs11ecp.so), Валидата, ViPNet и др.
internal/cryptocli/client.go:
- cryptocli.Client с конфигом {Provider, ModulePath, PIN, SlotID}
- Health() — Initialize → GetInfo → GetSlotList(WithToken=true) →
GetTokenInfo для каждого слота. Возвращает HealthInfo с
Cryptoki/library версиями, manufacturer и списком подключённых
токенов (label, model, serial)
- DefaultModulePath() — путь до .so для каждого провайдера (CSP,
Рутокен, Валидата, ViPNet)
- Если провайдер=stub или модуль не найден — клиент возвращает
понятную ошибку, lk-gateway переходит в режим без криптографии
В admin/setup wizard:
- В карточке «Криптография» появилась кнопка «Проверить подключение СКЗИ»
→ POST /admin/setup/crypto/check → cryptocli.Health() → flash с
результатом сверху страницы (список токенов или диагностика)
- Поле "UDS-сокет" помечено как legacy (для старого Java crypto-service),
основное поле — «Путь к модулю PKCS#11» с дефолтами и подсказками
- Расширен список провайдеров: добавлен «rutoken»
internal/cryptocli/client_test.go:
- Тесты Stub, MissingModule, EmptyPath, DefaultModulePath
- Старые тесты на UDS-сокет удалены (теперь PKCS#11)
Зависимость: github.com/miekg/pkcs11 v1.1.2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,51 +2,61 @@ package cryptocli_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/cryptocli"
|
||||
)
|
||||
|
||||
func TestClientReturnsErrNotImplementedWhenSocketReachable(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
socketPath := filepath.Join(dir, "crypto.sock")
|
||||
|
||||
listener, err := net.Listen("unix", socketPath)
|
||||
func TestStubProviderHealthOK(t *testing.T) {
|
||||
cli := cryptocli.New(cryptocli.Config{Provider: cryptocli.ProviderStub})
|
||||
h, err := cli.Health(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("listen unix: %v", err)
|
||||
t.Fatalf("Health: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
defer os.Remove(socketPath)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
cli := cryptocli.NewClient(socketPath, cryptocli.WithTimeout(time.Second))
|
||||
_, err = cli.VerifyXMLDSig(context.Background(), []byte("<xml/>"))
|
||||
if !errors.Is(err, cryptocli.ErrNotImplemented) {
|
||||
t.Errorf("ожидалась ErrNotImplemented, получено %v", err)
|
||||
if h.Provider != string(cryptocli.ProviderStub) {
|
||||
t.Errorf("Provider = %q", h.Provider)
|
||||
}
|
||||
if !strings.Contains(h.Message, "stub") {
|
||||
t.Errorf("сообщение не содержит 'stub': %q", h.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientReturnsDialErrorWhenSocketMissing(t *testing.T) {
|
||||
cli := cryptocli.NewClient("/nonexistent/crypto.sock", cryptocli.WithTimeout(200*time.Millisecond))
|
||||
_, err := cli.VerifyXMLDSig(context.Background(), []byte("x"))
|
||||
func TestModulePathMissing(t *testing.T) {
|
||||
cli := cryptocli.New(cryptocli.Config{
|
||||
Provider: cryptocli.ProviderCryptoPro,
|
||||
ModulePath: "/nonexistent/libcppkcs11.so",
|
||||
})
|
||||
_, err := cli.Health(context.Background())
|
||||
if err == nil {
|
||||
t.Fatal("ожидалась ошибка диалинга на несуществующий сокет")
|
||||
t.Fatal("ожидалась ошибка о ненайденном модуле")
|
||||
}
|
||||
if errors.Is(err, cryptocli.ErrNotImplemented) {
|
||||
t.Errorf("при отсутствующем сокете не должно быть ErrNotImplemented")
|
||||
if !strings.Contains(err.Error(), "не найден") {
|
||||
t.Errorf("неинформативная ошибка: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyModulePath(t *testing.T) {
|
||||
cli := cryptocli.New(cryptocli.Config{Provider: cryptocli.ProviderCryptoPro})
|
||||
_, err := cli.Health(context.Background())
|
||||
if err == nil {
|
||||
t.Fatal("ожидалась ошибка о пустом ModulePath")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultModulePath(t *testing.T) {
|
||||
cases := []struct {
|
||||
p cryptocli.Provider
|
||||
want string
|
||||
}{
|
||||
{cryptocli.ProviderCryptoPro, "/opt/cprocsp/lib/amd64/libcppkcs11.so"},
|
||||
{cryptocli.ProviderRutoken, "/usr/lib64/librtpkcs11ecp.so"},
|
||||
{cryptocli.ProviderStub, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := cryptocli.DefaultModulePath(c.p)
|
||||
if got != c.want {
|
||||
t.Errorf("DefaultModulePath(%s) = %q, ожидалось %q", c.p, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user