Как зашифровать/расшифровать данные на golang/javascript передаваемые по websocket протоколу?

Пользователь

от judd , в категории: Golang , 5 месяцев назад

Как зашифровать/расшифровать данные на golang/javascript передаваемые по websocket протоколу?

Facebook Vk Ok Twitter LinkedIn Telegram Whatsapp

1 ответ

Пользователь

от marisa , 5 месяцев назад

@judd 

Для зашифрования и расшифрования данных, передаваемых по протоколу WebSocket в Go можно использовать пакет github.com/gorilla/websocket. Вот пример кода, показывающий, как это можно сделать:

  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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

func main() {
	http.HandleFunc("/", handleWebSocket)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
	// Обновление HTTP соединения до WebSocket
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()

	for {
		// Принимаем сообщение от клиента
		_, msg, err := conn.ReadMessage()
		if err != nil {
			log.Println(err)
			break
		}

		// Расшифровываем полученные данные
		decryptedMsg, err := decryptAES(msg, "encryption-key")
		if err != nil {
			log.Println(err)
			break
		}

		log.Printf("Received: %s
", decryptedMsg)

		// Шифруем и отправляем обратно клиенту
		encryptedMsg, err := encryptAES(decryptedMsg, "encryption-key")
		if err != nil {
			log.Println(err)
			break
		}

		err = conn.WriteMessage(websocket.TextMessage, encryptedMsg)
		if err != nil {
			log.Println(err)
			break
		}
	}
}

func encryptAES(text, key string) ([]byte, error) {
	// Генерация 32-байтного ключа шифрования из строки
	// (предположим, что key хранится в hex-или base64-формате)
	keyBytes, err := base64.StdEncoding.DecodeString(key)
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return nil, err
	}

	// Используем режим ECB для простоты; для более безопасного шифрования
	// рекомендуется использовать режим CBC или GCM
	ecb := cipher.NewECBEncrypter(block)

	// Дополняем входной текст до размера блока
	paddedText := padBlock(text, block.BlockSize())
	encrypted := make([]byte, len(paddedText))

	ecb.CryptBlocks(encrypted, []byte(paddedText))

	return encrypted, nil
}

func decryptAES(encrypted []byte, key string) (string, error) {
	keyBytes, err := base64.StdEncoding.DecodeString(key)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return "", err
	}

	ecb := cipher.NewECBDecrypter(block)

	decrypted := make([]byte, len(encrypted))
	ecb.CryptBlocks(decrypted, encrypted)

	// Удаляем заполнение
	unpaddedText := unpadBlock(string(decrypted), block.BlockSize())

	return unpaddedText, nil
}

func padBlock(text string, blockSize int) string {
	padSize := blockSize - len(text)%blockSize
	padding := byte(padSize)

	for i := 0; i < padSize; i++ {
		text += string(padding)
	}

	return text
}

func unpadBlock(paddedText string, blockSize int) string {
	padding := int(paddedText[len(paddedText)-1])
	// Проверяем, что заполнение является корректным
	if padding < 1 || padding > blockSize {
		return paddedText
	}

	for i := 0; i < padding; i++ {
		if paddedText[len(paddedText)-1-i] != byte(padding) {
			return paddedText
		}
	}

	return paddedText[:len(paddedText)-padding]
}


Обратите внимание, что данный пример использует режим ECB шифрования - это плохая практика для безопасности. Рекомендуется использовать режим CBC или GCM вместо него.


Чтобы зашифровать и расшифровать данные на клиентской стороне в JavaScript, вы можете использовать стандартные функции для работы с шифрованием, такие как crypto.subtle.encrypt и crypto.subtle.decrypt. Вот пример кода:

 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
// Зашифровываем и отправляем данные на сервер
function sendEncryptedData(socket, data, key) {
  const textEncoder = new TextEncoder();
  const encodedData = textEncoder.encode(data);

  crypto.subtle.encrypt(
    {
      name: "AES-ECB",
      length: 128,
    },
    key,
    encodedData
  ).then((encryptedData) => {
    socket.send(encryptedData);
  }).catch((error) => {
    console.error(error);
  });
}

// Расшифровываем полученные данные
function processEncryptedData(data, key) {
  crypto.subtle.decrypt(
    {
      name: "AES-ECB",
      length: 128,
    },
    key,
    data
  ).then((decryptedData) => {
    const textDecoder = new TextDecoder();
    const decodedData = textDecoder.decode(decryptedData);
    console.log(decodedData);
  }).catch((error) => {
    console.error(error);
  });
}

// Генерируем ключ шифрования
crypto.subtle.generateKey(
  {
    name: "AES-ECB",
    length: 128,
  },
  true,
  ["encrypt", "decrypt"]
).then((key) => {
  // Отправляем ключ серверу или используем его для расшифровки данных
  // ...

  // Пример отправки зашифрованных данных
  const socket = new WebSocket("ws://localhost:8080/");
  socket.onopen = () => {
    sendEncryptedData(socket, "Hello, server!", key);
  };

  // Пример обработки полученных зашифрованных данных
  socket.onmessage = (event) => {
    processEncryptedData(event.data, key);
  };
}).catch((error) => {
  console.error(error);
});


Обратите внимание, что примеры кода являются базовыми, их следует изменить в зависимости от конкретных требований вашего приложения.