Go
криптография
Поиск…
Вступление
Узнайте, как шифровать и расшифровывать данные с помощью Go. Имейте в виду, что это не курс криптографии, а способ достижения этого с помощью Go.
Шифрование и дешифрование
предисловие
Это подробный пример того, как шифровать и расшифровывать данные с помощью Go. Код использования сокращается, например, обработка ошибок не упоминается. Полный рабочий проект с обработкой ошибок и пользовательским интерфейсом можно найти здесь в Github.
шифрование
Введение и данные
В этом примере описывается полное рабочее шифрование и дешифрование в Go. Для этого нам нужны данные. В этом примере мы используем собственный secret
структуры данных:
type secret struct {
DisplayName string
Notes string
Username string
EMail string
CopyMethod string
Password string
CustomField01Name string
CustomField01Data string
CustomField02Name string
CustomField02Data string
CustomField03Name string
CustomField03Data string
CustomField04Name string
CustomField04Data string
CustomField05Name string
CustomField05Data string
CustomField06Name string
CustomField06Data string
}
Затем мы хотим зашифровать такой secret
. Полный рабочий пример можно найти здесь (ссылка на Github) . Теперь шаг за шагом:
Шаг 1
Прежде всего, нам нужен некий мастер-пароль для защиты тайны: masterPassword := "PASS"
Шаг 2
Все криптовые методы работают с байтами вместо строк. Таким образом, мы строим массив байтов с данными из нашего секрета.
secretBytesDecrypted := []byte(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
artifact.DisplayName,
strings.Replace(artifact.Notes, "\n", string(65000), -1),
artifact.Username,
artifact.EMail,
artifact.CopyMethod,
artifact.Password,
artifact.CustomField01Name,
artifact.CustomField01Data,
artifact.CustomField02Name,
artifact.CustomField02Data,
artifact.CustomField03Name,
artifact.CustomField03Data,
artifact.CustomField04Name,
artifact.CustomField04Data,
artifact.CustomField05Name,
artifact.CustomField05Data,
artifact.CustomField06Name,
artifact.CustomField06Data,
))
Шаг 3
Мы создаем соль, чтобы предотвратить атаки радужного стола, ср. Википедия : saltBytes := uuid.NewV4().Bytes()
. Здесь мы используем UUID v4, который не предсказуем.
Шаг 4
Теперь мы можем получить ключ и вектор из основного пароля и случайной соли в отношении RFC 2898:
keyLength := 256
rfc2898Iterations := 6
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
Шаг 5
Желаемый режим CBC работает со всеми блоками. Таким образом, мы должны проверить, соответствуют ли наши данные полному блоку. Если нет, мы должны заполнить его:
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
Шаг 6
Теперь мы создаем AES-шифр: aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes)
Шаг 7
Мы сохраняем необходимую память для зашифрованных данных: encryptedData := make([]byte, len(secretBytesDecrypted))
. В случае AES-CBC зашифрованные данные имеют ту же длину, что и незашифрованные данные.
Шаг 8
Теперь мы должны создать encrypter и зашифровать данные:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
Теперь зашифрованные данные находятся внутри переменной encryptedData
.
Шаг 9
Зашифрованные данные должны быть сохранены. Но не только данные: без соли, зашифрованные данные не могут быть дешифрованы. Таким образом, мы должны использовать какой-то формат файла для управления этим. Здесь мы кодируем зашифрованные данные как base64, cf. Википедия :
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
Затем мы определяем наше содержимое файла и собственный формат файла. Формат выглядит так: salt[0x10]base64 content
. Сначала мы храним соль. Чтобы отметить начало содержимого base64, мы храним байт 10
. Это работает, потому что base64 не использует это значение. Поэтому мы могли бы найти начало base64 путем поиска первого вхождения 10
от конца к началу файла.
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
Шаг 10
Наконец, мы могли бы написать наш файл: writeErr := ioutil.WriteFile("my secret.data", fileContent, 0644)
.
Дешифрирование
Введение и данные
Что касается шифрования, нам нужны некоторые данные для работы. Таким образом, мы предполагаем, что у нас есть зашифрованный файл и secret
указанной структуры. Цель состоит в том, чтобы прочитать зашифрованные данные из файла, расшифровать его и создать экземпляр структуры.
Шаг 1
Первый шаг идентичен шифрованию: нам нужен некий главный пароль для дешифрования секретного masterPassword := "PASS"
.
Шаг 2
Теперь мы читаем зашифрованные данные из файла: encryptedFileData, bytesErr := ioutil.ReadFile(filename)
.
Шаг 3
Как упоминалось ранее, мы могли разделить соли и зашифрованные данные байт 10
ограничителя, искаженный назад от конца до начала:
for n := len(encryptedFileData) - 1; n > 0; n-- {
if encryptedFileData[n] == 10 {
saltBytes = encryptedFileData[:n]
encryptedBytesBase64 = encryptedFileData[n+1:]
break
}
}
Шаг 4
Затем мы должны декодировать байты с кодировкой base64:
decodedBytes := make([]byte, len(encryptedBytesBase64))
countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64)
encryptedBytes = decodedBytes[:countDecoded]
Шаг 5
Теперь мы можем получить ключ и вектор из основного пароля и случайной соли в отношении RFC 2898:
keyLength := 256
rfc2898Iterations := 6
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
Шаг 6
Создайте AES-шифр: aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes)
.
Шаг 7
Зарезервируйте необходимую память для дешифрованных данных: decryptedData := make([]byte, len(encryptedBytes))
. По определению он имеет ту же длину, что и зашифрованные данные.
Шаг 8
Теперь создайте decrypter и расшифруйте данные:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
Шаг 9
Преобразуйте прочитанные байты в строку: decryptedString := string(decryptedData)
. Поскольку нам нужны строки, разделите строку: lines := strings.Split(decryptedString, "\n")
.
Шаг 10
Создайте secret
из строк:
artifact := secret{}
artifact.DisplayName = lines[0]
artifact.Notes = lines[1]
artifact.Username = lines[2]
artifact.EMail = lines[3]
artifact.CopyMethod = lines[4]
artifact.Password = lines[5]
artifact.CustomField01Name = lines[6]
artifact.CustomField01Data = lines[7]
artifact.CustomField02Name = lines[8]
artifact.CustomField02Data = lines[9]
artifact.CustomField03Name = lines[10]
artifact.CustomField03Data = lines[11]
artifact.CustomField04Name = lines[12]
artifact.CustomField04Data = lines[13]
artifact.CustomField05Name = lines[14]
artifact.CustomField05Data = lines[15]
artifact.CustomField06Name = lines[16]
artifact.CustomField06Data = lines[17]
Наконец, заново создайте разрывы строк в поле примечаний: artifact.Notes = strings.Replace(artifact.Notes, string(65000), "\n", -1)
.