Go
Kryptografia
Szukaj…
Wprowadzenie
Dowiedz się, jak szyfrować i deszyfrować dane za pomocą Go. Pamiętaj, że nie jest to kurs o kryptografii, ale raczej o tym, jak to osiągnąć za pomocą Go.
Szyfrowanie i deszyfrowanie
Przedmowa
To jest szczegółowy przykład na temat szyfrowania i deszyfrowania danych za pomocą Go. Kod użycia jest skrócony, np. Obsługa błędów nie jest wspomniana. W pełni działający projekt z obsługą błędów i interfejsem użytkownika można znaleźć na Github tutaj .
Szyfrowanie
Wprowadzenie i dane
Ten przykład opisuje w pełni działające szyfrowanie i deszyfrowanie w Go. W tym celu potrzebujemy danych. W tym przykładzie używamy naszej secret
struktury danych:
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
}
Następnie chcemy zaszyfrować taki secret
. Pełny działający przykład można znaleźć tutaj (link do Github) . Teraz proces krok po kroku:
Krok 1
Przede wszystkim potrzebujemy pewnego rodzaju hasła głównego, aby zabezpieczyć sekret: masterPassword := "PASS"
Krok 2
Wszystkie metody kryptograficzne pracujące z bajtami zamiast ciągów. Dlatego konstruujemy tablicę bajtów z danymi z naszego tajnego klucza.
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,
))
Krok 3
Tworzymy sól, aby zapobiec atakom na tęczowy stół, por. Wikipedia : saltBytes := uuid.NewV4().Bytes()
. W tym przypadku używamy UUID v4, którego nie można przewidzieć.
Krok 4
Teraz możemy uzyskać klucz i wektor z hasła głównego i losowej soli, w odniesieniu do 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:]
Krok 5
Pożądany tryb CBC działa z całymi blokami. Dlatego musimy sprawdzić, czy nasze dane są wyrównane do pełnego bloku. Jeśli nie, musimy go uzupełnić:
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
}
Krok 6
Teraz tworzymy szyfr AES: aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes)
Krok 7
Zastrzegamy niezbędną pamięć dla zaszyfrowanych danych: encryptedData := make([]byte, len(secretBytesDecrypted))
. W przypadku AES-CBC zaszyfrowane dane miały taką samą długość jak niezaszyfrowane dane.
Krok 8
Teraz powinniśmy utworzyć szyfrator i zaszyfrować dane:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
Teraz zaszyfrowane dane znajdują się w zmiennej encryptedData
.
Krok 9
Zaszyfrowane dane muszą być przechowywane. Ale nie tylko dane: bez soli zaszyfrowane dane nie mogłyby zostać odszyfrowane. Dlatego do zarządzania tym musimy użyć jakiegoś formatu pliku. Tutaj kodujemy zaszyfrowane dane jako base64, por. Wikipedia :
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
Następnie definiujemy naszą zawartość pliku i własny format pliku. Format wygląda następująco: salt[0x10]base64 content
. Najpierw przechowujemy sól. Aby zaznaczyć początek zawartości base64, przechowujemy bajt 10
. To działa, ponieważ base64 nie używa tej wartości. Dlatego możemy znaleźć początek base64, wyszukując pierwsze wystąpienie 10
od końca do początku pliku.
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
Krok 10
Wreszcie możemy napisać nasz plik: writeErr := ioutil.WriteFile("my secret.data", fileContent, 0644)
.
Deszyfrowanie
Wprowadzenie i dane
Jeśli chodzi o szyfrowanie, potrzebujemy danych do pracy. Dlatego zakładamy, że mamy zaszyfrowany plik i wspomnianą strukturę secret
. Celem jest odczytanie zaszyfrowanych danych z pliku, odszyfrowanie go i utworzenie instancji struktury.
Krok 1
Pierwszy krok jest identyczny z szyfrowaniem: Potrzebujemy pewnego rodzaju hasła głównego, aby odszyfrować sekret: masterPassword := "PASS"
.
Krok 2
Teraz czytamy zaszyfrowane dane z pliku: encryptedFileData, bytesErr := ioutil.ReadFile(filename)
.
Krok 3
Jak wspomniano wcześniej, możemy podzielić sól i zaszyfrowane dane według bajtu separatora 10
, przeszukując wstecz od końca do początku:
for n := len(encryptedFileData) - 1; n > 0; n-- {
if encryptedFileData[n] == 10 {
saltBytes = encryptedFileData[:n]
encryptedBytesBase64 = encryptedFileData[n+1:]
break
}
}
Krok 4
Następnie musimy zdekodować bajty zakodowane w base64:
decodedBytes := make([]byte, len(encryptedBytesBase64))
countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64)
encryptedBytes = decodedBytes[:countDecoded]
Krok 5
Teraz możemy uzyskać klucz i wektor z hasła głównego i losowej soli, w odniesieniu do 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:]
Krok 6
Utwórz szyfr AES: aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes)
.
Krok 7
Zarezerwuj niezbędną pamięć dla odszyfrowanych danych: decryptedData := make([]byte, len(encryptedBytes))
. Z definicji ma taką samą długość jak zaszyfrowane dane.
Krok 8
Teraz utwórz deszyfrator i odszyfruj dane:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
Krok 9
Konwertuj odczytane bajty na ciąg: decryptedString := string(decryptedData)
. Ponieważ potrzebujemy linii, podziel ciąg: lines := strings.Split(decryptedString, "\n")
.
Krok 10
Zbuduj secret
z linii:
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]
Na koniec utwórz ponownie podziały wiersza w polu notatek: artifact.Notes = strings.Replace(artifact.Notes, string(65000), "\n", -1)
.