Go
geheimschrift
Zoeken…
Invoering
Ontdek hoe u gegevens kunt coderen en decoderen met Go. Houd er rekening mee dat dit geen cursus over cryptografie is, maar hoe je dit kunt bereiken met Go.
Versleuteling en ontsleuteling
Voorwoord
Dit is een gedetailleerd voorbeeld van het coderen en decoderen van gegevens met Go. De gebruikscode is korter, bijvoorbeeld de foutafhandeling wordt niet vermeld. De volledig werkende project met error handling en de user interface kan worden gevonden op Github hier .
Encryption
Introductie en gegevens
Dit voorbeeld beschrijft een volledig werkende codering en decodering in Go. Hiervoor hebben we gegevens nodig. In dit voorbeeld gebruiken we onze eigen secret
datastructuur:
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
}
Vervolgens willen we zo'n secret
coderen. Het volledige werkende voorbeeld is hier te vinden (link naar Github) . Nu het stapsgewijze proces:
Stap 1
Allereerst hebben we een soort hoofdwachtwoord nodig om het geheim te beschermen: masterPassword := "PASS"
Stap 2
Alle cryptomethoden die met bytes werken in plaats van tekenreeksen. We construeren dus een byte-array met de gegevens van ons geheim.
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,
))
Stap 3
We maken wat zout om regenboogtabelaanvallen te voorkomen, cf. Wikipedia : saltBytes := uuid.NewV4().Bytes()
. Hier gebruiken we een UUID v4 die niet voorspelbaar is.
Stap 4
Nu kunnen we een sleutel en een vector afleiden uit het hoofdwachtwoord en het willekeurige zout, met betrekking tot 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:]
Stap 5
De gewenste CBC-modus werkt met hele blokken. We moeten dus controleren of onze gegevens op een volledig blok zijn afgestemd. Zo niet, dan moeten we het opvullen:
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
}
Stap 6
Nu maken we een AES-codering: aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes)
Stap 7
We reserveren het nodige geheugen voor de gecodeerde gegevens: encryptedData := make([]byte, len(secretBytesDecrypted))
. In het geval van AES-CBC hadden de gecodeerde gegevens dezelfde lengte als de niet-gecodeerde gegevens.
Stap 8
Nu moeten we de codeerder maken en de gegevens coderen:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
Nu bevinden de gecodeerde gegevens zich in de variabele encryptedData
.
Stap 9
De gecodeerde gegevens moeten worden opgeslagen. Maar niet alleen de gegevens: zonder het zout konden de gecodeerde gegevens niet worden gedecodeerd. Daarom moeten we een soort bestandsindeling gebruiken om dit te beheren. Hier coderen we de gecodeerde gegevens als base64, cf. Wikipedia :
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
Vervolgens definiëren we onze bestandsinhoud en onze eigen bestandsindeling. De indeling ziet er als volgt uit: salt[0x10]base64 content
. Eerst slaan we het zout op. Om het begin van de base64-inhoud te markeren, slaan we de byte 10
. Dit werkt omdat base64 deze waarde niet gebruikt. Daarom konden we het begin van base64 vinden door te zoeken naar het eerste exemplaar van 10
vanaf het einde tot het begin van het bestand.
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
Stap 10
Eindelijk kunnen we ons bestand schrijven: writeErr := ioutil.WriteFile("my secret.data", fileContent, 0644)
.
decryptie
Introductie en gegevens
Wat codering betreft, hebben we wat gegevens nodig om mee te werken. We gaan er dus van uit dat we een gecodeerd bestand hebben en dat de genoemde structuur secret
. Het doel is om de gecodeerde gegevens uit het bestand te lezen, te decoderen en een instantie van de structuur te maken.
Stap 1
De eerste stap is identiek aan de codering: we hebben een soort hoofdwachtwoord nodig om het geheim te ontsleutelen: masterPassword := "PASS"
.
Stap 2
Nu lezen we de gecodeerde gegevens uit het bestand: encryptedFileData, bytesErr := ioutil.ReadFile(filename)
.
Stap 3
Zoals eerder vermeld, konden we zout en gecodeerde gegevens splitsen door de scheiding byte 10
, achteruit gezocht vanaf het einde tot het begin:
for n := len(encryptedFileData) - 1; n > 0; n-- {
if encryptedFileData[n] == 10 {
saltBytes = encryptedFileData[:n]
encryptedBytesBase64 = encryptedFileData[n+1:]
break
}
}
Stap 4
Vervolgens moeten we de base64-gecodeerde bytes decoderen:
decodedBytes := make([]byte, len(encryptedBytesBase64))
countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64)
encryptedBytes = decodedBytes[:countDecoded]
Stap 5
Nu kunnen we een sleutel en een vector afleiden uit het hoofdwachtwoord en het willekeurige zout, met betrekking tot 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:]
Stap 6
Maak een AES-codering: aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes)
.
Stap 7
Reserveer het benodigde geheugen voor de gedecodeerde gegevens: decryptedData := make([]byte, len(encryptedBytes))
. Per definitie heeft het dezelfde lengte als de gecodeerde gegevens.
Stap 8
Maak nu de decrypter en decodeer de gegevens:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
Stap 9
Converteer de gelezen bytes naar string: decryptedString := string(decryptedData)
. Omdat we lijnen nodig hebben, splitst u de string: lines := strings.Split(decryptedString, "\n")
.
Stap 10
Construeer een secret
uit de lijnen:
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]
Maak ten slotte de regeleinden in het artifact.Notes = strings.Replace(artifact.Notes, string(65000), "\n", -1)
: artifact.Notes = strings.Replace(artifact.Notes, string(65000), "\n", -1)
.