Le script python est génial. Je l'ai un peu modifié pour créer un fichier csv pour l'importation dans Firefox. Sur MacOs, il faut un pip pinstall pandas avant
#!/usr/bin/env python
import sys
import os
import re
import csv
import pandas as pd
#de time
import datetime
# Regex pour faire correspondre les mots de passe génériques et Internet à partir d'une sauvegarde de trousseau
regex = re.compile(
r"""
keychain:\s"(?P[^"]+)"\n # chemin absolu et fichier du trousseau
version:\s(\d\d\d)\n # version
class:\s"(?P(genp|inet))"\n # mot de passe générique ou mot de passe Internet
attributes:\n
(\s*?0x00000007\s=(?P[^\n]+)\n)? # nom
(\s*?0x00000008\s=(?P[^\n]+)\n)? # ? utilisé uniquement pour les certificats
(\s*?"acct"=(?P[^\n]+)\n)? # compte
(\s*?"atyp"=(?P[^\n]+)\n)? # type de compte ("form"), parfois entier
(\s*?"cdat"=[^"]*(?P[^\n]+)\n)? # date et heure de création
(\s*?"crtr"=(?P[^\n]+)\n)? # clé du fournisseur avec quatre caractères comme "aapl"
(\s*?"cusi"=(?P[^\n]+)\n)? # ? toujours nulle
(\s*?"desc"=(?P[^\n]+)\n)? # description
(\s*?"gena"=(?P[^\n]+)\n)? # ? toujours nulle sauf dans certains cas rares
(\s*?"icmt"=(?P[^\n]+)\n)? # ? une sorte de description
(\s*?"invi"=(?P[^\n]+)\n)? # ? toujours nulle
(\s*?"mdat"=[^"]*(?P[^\n]+)\n)? # date et heure de dernière modification
(\s*?"nega"=(?P[^\n]+)\n)? # ? toujours nulle
(\s*?"path"=(?P[^\n]+)\n)? # chemin
(\s*?"port"=(?P[^\n]+)\n)? # numéro de port en hexadécimal
(\s*?"prot"=(?P[^\n]+)\n)? # ? toujours nulle
(\s*?"ptcl"=(?P[^\n]+)\n)? # protocole mais est un blob ("http", "https")
(\s*?"scrp"=(?P[^\n]+)\n)? # ? toujours nulle sauf dans certains cas rares
(\s*?"sdmn"=(?P[^\n]+)\n)? # utilisé pour AuthName de htaccess
(\s*?"srvr"=(?P[^\n]+)\n)? # serveur
(\s*?"svce"=(?P[^\n]+)\n)? # ? une sorte de description
(\s*?"type"=(?P[^\n]+)\n)? # un blob: "iprf", "note"
data:\n
"(?P.*)" # mot de passe
""", re.MULTILINE | re.VERBOSE)
# Dictionnaire utilisé par la fonction de nettoyage (Apple n'a pas toujours raison sur les
# types des champs)
field2type = {
"name": "blob",
"hex8": "blob",
"acct": "blob",
"atyp": "simple",
"cdat": "timedate",
"crtr": "uint32",
"cusi": "sint32",
"desc": "blob",
"gena": "blob",
"icmt": "blob",
"invi": "sint32",
"mdat": "timedate",
"nega": "sint32",
"path": "blob",
"port": "uint32",
"prot": "blob",
"ptcl": "blob",
"scrp": "sint32",
"sdmn": "blob",
"srvr": "blob",
"svce": "blob",
"type": "blob",
"data": "simple",
"kchn": "simple",
"clss": "simple"
}
def clean(field, match):
value = match.group(field)
if not value or value == "":
# affiche les valeurs nulles comme des chaînes vides
return ""
if field2type[field] == "blob":
# supprime le "au début et à la fin
return value[1:-1]
elif field2type[field] == "timedate":
# convertit une date et une heure en norme ISO
value = value[1:-1]
return value[0:4] + "-" + value[4:6] + "-" + value[6:8] + "T" + \
value[8:10] + ":" + value[10:12] + ":" + value[12:14] + "Z" + value[16:19]
elif field2type[field] == "uint32":
# si c'est vraiment un entier hexadécimal, le convertir en décimal
value = value.strip()
if re.match("^0x[0-9a-fA-F]+$", value):
return int(value, 16)
else:
return value
else:
# ne rien faire, simplement l'imprimer tel qu'il est
return value
def print_help():
print "Utilisation: python keychain4ff.py INPUTFILE OUTPUTFILE"
print "Exemple: python keychain.py keychain.txt keychain.xls"
print " où keychain.txt a été créé par `security dump-keychain -d > keychain.txt`"
print " Lors de la sauvegarde du trousseau, vous devez cliquer sur 'Autoriser' pour chaque entrée dans votre"
print " trousseau. Positionnez votre souris sur le bouton et cliquez frénétiquement."
print "Trousseau 0.1: convertir une sauvegarde de trousseau Apple en un fichier .csv pour l'importation dans Firefox."
# Vérifier les paramètres corrects
if len(sys.argv) != 3:
print_help()
sys.exit(1)
elif len(sys.argv) == 3:
if not os.path.isfile(sys.argv[1]):
print "Erreur: fichier '{0}' introuvable".format(sys.argv[1])
print_help()
exit(1)
# Lire le fichier de trousseau
buffer = open(sys.argv[1], "r").read()
print "Lire {0} octets depuis '{1}'".format(len(buffer), sys.argv[1])
df = pd.DataFrame( columns = ["url", "username", "password", "httpRealm", "formActionOrigin", "guid", "timeCreated", "timeLastUsed", "timePasswordChanged", ])
# Trouver les mots de passe et les ajouter au Data Frame
i = 0
for match in regex.finditer(buffer):
url = clean("ptcl", match)
if len(url):
if url == 'htps':
url = 'https'
elif url == 'http':
pass
else:
continue
datstr = clean("cdat", match)
mo = re.match(r"(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z000", datstr)
if mo:
dstr = mo.group(1)+" "+mo.group(2)+" "+mo.group(3)+" "+mo.group(4)+" "+mo.group(5)+" "+mo.group(6)
epoch = int( (datetime.datetime(int(mo.group(1)), int(mo.group(2)), int(mo.group(3)), int(mo.group(4)), int(mo.group(5)), int(mo.group(6))) - datetime.datetime(1970,1,1) ).total_seconds())
record = {"url": url + "://"+ clean("srvr", match),
"username" : clean("acct", match),
"password" : clean("data", match),
"httpRealm": "",
"formActionOrigin": url + "://"+ clean("srvr", match),
"guid": "",
"timeCreated": epoch,
"timeLastUsed": epoch,
"timePasswordChanged": epoch,
}
df = df.append(record, ignore_index=True)
i += 1
df.to_csv(sys.argv[2], index=False, quotechar='"', quoting=csv.QUOTE_ALL, escapechar="\\")
print "Enregistré {0} mots de passe dans '{1}'".format(i-1, sys.argv[2])
0 votes
Voir aussi cette question/réponse : apple.stackexchange.com/a/185980/129823
1 votes
Pour les éléments iCloud dans le trousseau de clés : apple.stackexchange.com/a/408952/151764