Go Cryptage et décryptage


Exemple

Avant-propos

Ceci est un exemple détaillé sur la façon de chiffrer et déchiffrer les données avec Go. Le code d'utilisation est raccourci, par exemple la gestion des erreurs n'est pas mentionnée. Le projet de travail complet avec gestion des erreurs et interface utilisateur se trouve sur Github ici .


Cryptage

Introduction et données

Cet exemple décrit un chiffrement et un déchiffrement complets dans Go. Pour ce faire, nous avons besoin de données. Dans cet exemple, nous utilisons notre propre secret structure de données:

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
}

Ensuite, nous voulons chiffrer un tel secret . L'exemple de travail complet peut être trouvé ici (lien vers Github) . Maintenant, le processus pas à pas:

Étape 1

Tout d'abord, nous avons besoin d'une sorte de mot de passe maître pour protéger le secret: masterPassword := "PASS"

Étape 2

Toutes les méthodes de cryptage utilisant des octets au lieu de chaînes. Ainsi, nous construisons un tableau d'octets avec les données de notre secret.

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,
))

Étape 3

Nous créons du sel afin de prévenir les attaques à l'arc-en-ciel, cf. Wikipedia : saltBytes := uuid.NewV4().Bytes() . Ici, nous utilisons un UUID v4 qui n'est pas prévisible.

Étape 4

Maintenant, nous sommes en mesure de dériver une clé et un vecteur du mot de passe maître et du sel aléatoire, en ce qui concerne 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:]

Étape 5

Le mode CBC souhaité fonctionne avec des blocs entiers. Nous devons donc vérifier si nos données sont alignées sur un bloc complet. Sinon, nous devons le remplir:

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
}

Étape 6

Maintenant, nous créons un chiffrement AES: aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes)

Étape 7

Nous réservons la mémoire nécessaire aux données chiffrées: encryptedData := make([]byte, len(secretBytesDecrypted)) . Dans le cas d'AES-CBC, les données chiffrées avaient la même longueur que les données non chiffrées.

Étape 8

Maintenant, nous devrions créer le crypteur et crypter les données:

aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)

Maintenant, les données chiffrées se trouvent dans la variable encryptedData .

Étape 9

Les données cryptées doivent être stockées. Mais pas seulement les données: sans le sel, les données chiffrées n'ont pas pu être déchiffrées. Nous devons donc utiliser un format de fichier pour gérer cela. Ici, nous encodons les données chiffrées en base64, cf. Wikipedia :

encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)

Ensuite, nous définissons notre contenu de fichier et notre propre format de fichier. Le format ressemble à ceci: salt[0x10]base64 content . Tout d'abord, nous stockons le sel. Afin de marquer le début du contenu de base64, nous stockons l’octet 10 . Cela fonctionne, car base64 n'utilise pas cette valeur. Par conséquent, nous pourrions trouver le début de base64 en recherchant la première occurrence de 10 de la fin au début du fichier.

fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)

Étape 10

Enfin, nous pourrions écrire notre fichier: writeErr := ioutil.WriteFile("my secret.data", fileContent, 0644) .


Décryptage

Introduction et données

En ce qui concerne le chiffrement, nous avons besoin de certaines données pour travailler. Ainsi, nous supposons que nous avons un fichier crypté et la structure mentionnée secret . L'objectif est de lire les données chiffrées à partir du fichier, de les déchiffrer et de créer une instance de la structure.

Étape 1

La première étape est identique au chiffrement: nous avons besoin d'une sorte de mot de passe principal pour déchiffrer le secret: masterPassword := "PASS" .

Étape 2

Maintenant, nous lisons les données chiffrées à partir du fichier: encryptedFileData, bytesErr := ioutil.ReadFile(filename) .

Étape 3

Comme mentionné précédemment, nous pourrions diviser les données de sel et les données cryptées par l’octet de délimiteur 10 , recherché en arrière de la fin au début:

for n := len(encryptedFileData) - 1; n > 0; n-- {
    if encryptedFileData[n] == 10 {
        saltBytes = encryptedFileData[:n]
        encryptedBytesBase64 = encryptedFileData[n+1:]
        break
    }
} 

Étape 4

Ensuite, nous devons décoder les octets encodés en base64:

decodedBytes := make([]byte, len(encryptedBytesBase64))
countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64)
encryptedBytes = decodedBytes[:countDecoded]

Étape 5

Maintenant, nous sommes en mesure de dériver une clé et un vecteur du mot de passe maître et du sel aléatoire, en ce qui concerne 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:]

Étape 6

Créez un chiffrement AES: aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes) .

Étape 7

Réservez la mémoire nécessaire pour les données déchiffrées: decryptedData := make([]byte, len(encryptedBytes)) . Par définition, il a la même longueur que les données cryptées.

Étape 8

Maintenant, créez le décrypteur et décryptez les données:

aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)

Étape 9

Convertissez les octets de lecture en chaîne: decryptedString := string(decryptedData) . Comme nous avons besoin de lignes, séparez la chaîne: lines := strings.Split(decryptedString, "\n") .

Étape 10

Construis un secret sur les lignes:

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]

Enfin, recréez les sauts de ligne dans le champ notes: artifact.Notes = strings.Replace(artifact.Notes, string(65000), "\n", -1) .