GophKeeper представляет собой клиент-серверную систему, позволяющую пользователю надёжно и безопасно хранить логины, пароли, бинарные данные и прочую приватную информацию.
На сервере все записи хранятся в одной таблице:
CREATE TABLE entries
(
id uuid UNIQUE NOT NULL DEFAULT gen_random_uuid(),
public_key bytea NOT NULL,
payload bytea NOT NULL
);
- ID используем для обращения к конкретной записи в базе данных;
- PublicKey храним, чтобы пользователь мог получить все свои записи, а также удалить их;
- Payload - сами данные, зашифрованные ассиметричным алгоритмом RSA.
Получив доступ к базе данных, злоумышленник даже не сможет узнать, какого типа данные хранит пользователь.
Сервер поддерживает использование серверных TLS-сертификатов для безопасной передачи данных.
Клиент представляет собой консольное приложение, которое дает пользователю возможность сохранять и просматривать свои данные.
При запуске приложения пользователю будет предложено сгенерировать ключ, либо загрузить уже имеющийся.
Приватный ключ хранится в файле в PEM-формате.
Серверная и клиентская части приложения общаются между собой по протоколу GRPC следующими методами:
Чтобы получить конкретную запись, пользователю достаточно знать ID.
Здесь нет никакой аутентификации, потому что данные без приватного ключа все равно расшифровать не получится.
//rpc Get(GetRequest) returns (GetResponse);
message GetRequest {
string id = 1;
}
message GetResponse {
bytes data = 1;
}
Получаем записи пользователя по его публичному ключу.
//rpc GetAll(GetAllRequest) returns (GetAllResponse);
message GetAllRequest {
bytes public_key = 1;
}
message GetAllResponse {
message Entry {
string id = 1;
bytes data = 2;
}
repeated Entry entries = 1;
}
Сохраняем зашифрованные данные пользователя и возвращаем ID.
Подписать нужно хеш данных, которые сохраняем SHA256(data)
.
//rpc Create(CreateRequest) returns (CreateResponse);
message CreateRequest {
bytes public_key = 1;
bytes data = 2;
bytes sign = 3;
}
message CreateResponse {
string id = 1;
}
Удаляем пользовательские данные.
Подписать нужно дважды хешированные данные SHA256(SHA256(data))
,
чтобы даже в случае перехвата сообщения создания записи,
злоумышленник не мог ее удалить.
//rpc Delete(DeleteRequest) returns (google.protobuf.Empty);
message DeleteRequest {
string id = 1;
bytes sign = 2;
}
Принцип такой же, как и при создании, только дополнительно подписываем
дважды хешированные старые данные SHA256(SHA256(data))
, как при удалении.
//rpc Update(UpdateRequest) returns (google.protobuf.Empty);
message UpdateRequest {
string id = 1;
bytes data = 2;
bytes sign_old = 3;
bytes sign_new = 4;
}
Аутентификацию производим с помощью RSASSA-PKCS1-V1_5-SIGN подписи SHA256 хеша данных:
data := []byte("hello, world!")
// encrypt data
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, data)
// generate signature
hash := sha256.Sum256(ciphertext)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
// verify signature
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signature)