28

in order to migrate to an Ubutun, I would like to export all my password, to for example a CSV file.

In Keychain Access, I found export menu, but it's alway disabled, even when the access is unlocked.

What should I do?

2

9 Answers 9

20

This is just about how I did it a many years ago, this is that script update for Yosemite 10.11.5 - but I've not tested it.

  1. A script that saves each item in the Keychain to text:

    security dump-keychain -d login.keychain > keychain.txt
    
  2. A second AppleScript item that clicks on the "Allow" button that the 1st script triggers when reading the item out of the KeyChain.

[Edit: July 2016] This has been updated to 10.11.5 note as some have reported locking up their Mac with the 0.2 delay, I've limited the script to only process 200 results at a time, thus if you have 1050 keychain items, you'll need to run this script 6 time in the ScriptEditor, you'll also have to allow the ScriptEditor to be enabled in the Accessibility section in the security preferences in :

tell application "System Events"
    set maxAttemptsToClick to 200
    repeat while exists (processes where name is "SecurityAgent")
        if maxAttemptsToClick = 0 then exit repeat
        set maxAttemptsToClick to maxAttemptsToClick - 1
        tell process "SecurityAgent"
            try
                click button 2 of window 1
            on error
                keystroke " "
            end try
        end tell
        delay 0.2
    end repeat
end tell

Then the link/yosemite update above also has a ruby conversion step from the text file to CSV, Good luck!

ShreevatsaR points out in the comments that this ruby conversion only covers "internet passwords" and not "application passwords". This is due to the aim of the script is to export the "internet passwords" into the application 1Password.

And here's a stack overflow question and answer along the same lines

The System.keychain is here:

security dump-keychain -d /Library/Keychains/System.keychain > systemkeychain.txt

To enable AppleScript to interact with dialogue box the System Preferences -> Security & Privacy Preferences -> Privacy Tab, Accessibility Option must have "Script Editor.app" enabled System Preferences -> Security & Privacy Preferences -> Privacy Tab, Accessibility Option highlighted

12
  • thanks you very much! It's work fine. I just don't know why I can't export my System.keychain. But most keychain there are also on login.keychain.
    – Maïeul
    Jul 8, 2014 at 20:44
  • See update for that keychain item path, but it might ask for username and password for each item, which may be the Allow script can be modified to provide. Jul 8, 2014 at 20:56
  • Thinks. I have no competence in AppleScript. I have juste few password in this keychain, so I will type my main password many time.
    – Maïeul
    Jul 8, 2014 at 21:05
  • If you get no output, try running the command within the same directory as the keychain. Oct 2, 2014 at 11:13
  • 6
    The AppleScript did not work for me on OS X 10.10.3 Yosemite, claiming "System Events got an error: Can’t get group 1 of window 1 of process \"SecurityAgent\". Invalid index". May 29, 2015 at 11:58
9

I wrote a python script that converts the keychain dump to an Excel file and thought I share it with you. I choose Excel over CSV or TSV because a lot of people have it installed and it just works by double clicking on the file. You may of course modify the script to print any other format. I did this on OS X 10.11 El Capitan, but should work on older OS' as well.

  1. Since I do not like storing my passwords plaintext on my hard drive, I created an encrypted container using the Disk Utility app. Simply open Disk Utility (press cmd+Space, type "disk"). In the app, press cmd+N for new image, change the name to SEC, change encryption to 256-Bit AES and save it under SEC in a directory of you choice. Then mount the volume by doubleclicking on the file (or using Disk Utility).

  2. Create a new file named keychain.py in the secure container and paste the code below.

  3. Now open Terminal.app and change directory to the mounted encrypted volume: cd /Volumes/SEC

  4. We need the python package manager for installing the Excel module (you will be prompted for your password): sudo easy_install pip

  5. We need to install the Python Excel module: sudo pip install xlwt

  6. Now export the passwords using one of the other answers to this question. I just did security dump-keychain -d > keychain.txt and spam clicked on the Allow button while holding the mouse with my other hand.

  7. The last step is to convert the txt file to a readable Excel sheet using the python script: python keychain.py keychain.txt keychain.xls

.

#!/usr/bin/env python

import sys
import os
import re
import xlwt

# Regex to match both generic and internet passwords from a keychain dump
regex = re.compile(
    r"""
    keychain:\s"(?P<kchn>[^"]+)"\n                  # absolute path and file of keychain
    version:\s(\d\d\d)\n                            # version
    class:\s"(?P<clss>(genp|inet))"\n               # generic password or internet password
    attributes:\n
    (\s*?0x00000007\s<blob>=(?P<name>[^\n]+)\n)?    # name
    (\s*?0x00000008\s<blob>=(?P<hex8>[^\n]+)\n)?    # ? only used at certificates
    (\s*?"acct"<blob>=(?P<acct>[^\n]+)\n)?          # account
    (\s*?"atyp"<blob>=(?P<atyp>[^\n]+)\n)?          # account type ("form"), sometimes int
    (\s*?"cdat"<timedate>=[^"]*(?P<cdat>[^\n]+)\n)? # datetime created
    (\s*?"crtr"<uint32>=(?P<crtr>[^\n]+)\n)?        # vendor key with four chars like "aapl"
    (\s*?"cusi"<sint32>=(?P<cusi>[^\n]+)\n)?        # ? always null
    (\s*?"desc"<blob>=(?P<desc>[^\n]+)\n)?          # description
    (\s*?"gena"<blob>=(?P<gena>[^\n]+)\n)?          # ? always null except one rare cases
    (\s*?"icmt"<blob>=(?P<icmt>[^\n]+)\n)?          # ? some sort of description
    (\s*?"invi"<sint32>=(?P<invi>[^\n]+)\n)?        # ? always null
    (\s*?"mdat"<timedate>=[^"]*(?P<mdat>[^\n]+)\n)? # datetime last modified
    (\s*?"nega"<sint32>=(?P<nega>[^\n]+)\n)?        # ? always null
    (\s*?"path"<blob>=(?P<path>[^\n]+)\n)?          # path
    (\s*?"port"<uint32>=(?P<port>[^\n]+)\n)?        # port number in hex
    (\s*?"prot"<blob>=(?P<prot>[^\n]+)\n)?          # ? always null
    (\s*?"ptcl"<uint32>=(?P<ptcl>[^\n]+)\n)?        # protocol but is blob ("http", "https")
    (\s*?"scrp"<sint32>=(?P<scrp>[^\n]+)\n)?        # ? always null except one rare cases
    (\s*?"sdmn"<blob>=(?P<sdmn>[^\n]+)\n)?          # used for htaccess AuthName
    (\s*?"srvr"<blob>=(?P<srvr>[^\n]+)\n)?          # server
    (\s*?"svce"<blob>=(?P<svce>[^\n]+)\n)?          # ? some sort of description
    (\s*?"type"<uint32>=(?P<type>[^\n]+)\n)?        # some blob: "iprf", "note"
    data:\n
    "(?P<data>[^"]*)"                               # password
    """, re.MULTILINE | re.VERBOSE)

# Dictionary used by the clean function (Apple is not always right about the
# types of the field)
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 == "<NULL>":
        # print null values as empty strings
        return ""
    if field2type[field] == "blob":
        # strip " at beginning and end
        return value[1:-1]
    elif field2type[field] == "timedate":
        # convert timedate to the iso standard
        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":
        # if it really is a hex int, convert it to decimal
        value = value.strip()
        if re.match("^0x[0-9a-fA-F]+$", value):
            return int(value, 16)
        else:
            return value
    else:
        # do nothing, just print it as it is
        return value

def print_help():
    print "Usage: python keychain.py INPUTFILE OUTPUTFILE"
    print "Example: python keychain.py keychain.txt keychain.xls"
    print "  where keychain.txt was created by `security dump-keychain -d > keychain.txt`"
    print "  When dumping the keychain, you have to click 'Allow' for each entry in your"
    print "  keychain. Position you mouse over the button and go clicking like crazy."




print "Keychain 0.1: convert an Apple Keychain dump to an Excel (XLS) spreadsheet."

# Check for correct parameters
if len(sys.argv) != 3:
    print_help()
    sys.exit(1)
elif len(sys.argv) == 3:
    if not os.path.isfile(sys.argv[1]):
        print "Error: no such file '{0}'".format(sys.argv[1])
        print_help()
        exit(1)

# Read keychain file
buffer = open(sys.argv[1], "r").read()
print "Read {0} bytes from '{1}'".format(len(buffer), sys.argv[1])

# Create excel workbook and header
wb = xlwt.Workbook()
ws = wb.add_sheet("Keychain")
ws.write(0, 0, "Name")
ws.write(0, 1, "Account")
ws.write(0, 2, "Password")
ws.write(0, 3, "Protocol")
ws.write(0, 4, "Server")
ws.write(0, 5, "Port")
ws.write(0, 6, "Path")
ws.write(0, 7, "Description")
ws.write(0, 8, "Created")
ws.write(0, 9, "Modified")
ws.write(0, 10, "AuthName")
ws.write(0, 11, "AccountType")
ws.write(0, 12, "Type")
ws.write(0, 13, "Keychain")

# Find passwords and add them to the excel spreadsheet
i = 1
for match in regex.finditer(buffer):
    ws.write(i, 0, clean("name", match))
    ws.write(i, 1, clean("acct", match))
    ws.write(i, 2, clean("data", match))
    ws.write(i, 3, clean("ptcl", match))
    ws.write(i, 4, clean("srvr", match))
    ws.write(i, 5, clean("port", match))
    ws.write(i, 6, clean("path", match))
    ws.write(i, 7, clean("desc", match))
    ws.write(i, 8, clean("cdat", match))
    ws.write(i, 9, clean("mdat", match))
    ws.write(i, 10, clean("sdmn", match))
    ws.write(i, 11, clean("atyp", match))
    ws.write(i, 12, clean("clss", match))
    ws.write(i, 13, clean("kchn", match))
    i += 1
wb.save(sys.argv[2])

print "Saved {0} passwords to '{1}'".format(i-1, sys.argv[2])
4
  • Wow, this looks really thorough. I'm going to give it a shot, though I still need a solution for the secure notes and app passwords... :( This should get me a long way though.
    – hepcat72
    Jul 1, 2016 at 14:24
  • Found this excellent break-down of secure notes: stackoverflow.com/questions/22370552/…
    – hepcat72
    Jul 1, 2016 at 15:02
  • Now I just need to figure out how to create an export of an rtfd that includes an embedded image. I have a few notes with passwords in screen-caps.
    – hepcat72
    Jul 1, 2016 at 15:03
  • Awesome addition to this, plus one and thx
    – ehime
    Jul 18, 2017 at 18:05
5

As of OSX 10.10.3 there is a new way do auto-accept (I ran into issues during an upgrade path)

Bash functions (added to either .profile or .bash_rc files)

## At the terminal when you start getting the prompts, type `Accepts` and press enter
function Accepts () {
osascript <<EOF
  tell application "System Events"
    repeat while exists (processes where name is "SecurityAgent")
      tell process "SecurityAgent" to click button "Allow" of window 1
      delay 0.2
    end repeat
  end tell
EOF
}

## At the terminal when you start getting the prompts, type `Accepts YourUsername YourPassword` and press enter
function AcceptWithCreds () {
username="$1"
password="$2"

[ -z "${password}" ] && return 1

osascript 2>/dev/null <<EOF
    set appName to "${username}"
    set appPass to "${password}"
    tell application "System Events"
        repeat while exists (processes where name is "SecurityAgent")
            tell process "SecurityAgent"
                if exists (text field 1 of window 1) then
                    set value of text field 1 of window 1 to appName
                    set value of text field 2 of window 1 to appPass
                end if
            end tell
      tell process "SecurityAgent" to click button "Allow" of window 1
            delay 0.2
        end repeat
    end tell
EOF
echo 'Finished...'
}

And use this script to dump your keyring (sudo ./dump.sh)

#!/bin/bash
# Run above script in another window

security dump-keychain -d login.keychain > keychain-login.txt
security dump-keychain -d /Library/Keychains/System.keychain > keychain-system.txt
4
  • This is great! When I ran it I got execution error: System Events got an error: osascript is not allowed assistive access. at the command line. The easiest way I found to deal with this was to paste the AppleScript code into the Script Editor app and run it from there.
    – Evan
    Jul 1, 2016 at 19:03
  • AppleScripts not working on 10.10.5
    – oarfish
    Jul 8, 2016 at 12:51
  • 1
    This worked for me under OS X El Capitan 10.11.6. Make sure not to copy the last character (backtick) from the script. The osascript is not allowed assistive access error can be avoided by allowing your Terminal app in System Preferences => Security & Privacy => Accessibility.
    – neon1
    Sep 17, 2016 at 18:40
  • This doesn't work on Mojave. AcceptWithCreds simply finishes, without doing anything.
    – oarfish
    Aug 31, 2019 at 11:50
4

@MichaelStoner's answer is a good start, but it fails on OS X 10.10.3 Yosemite, with his AppleScript code reporting System Events got an error: Can’t get group 1 of window 1 of process "SecurityAgent". Invalid index.

After playing around a little bit, the following solution worked for me:

tell application "System Events"
    repeat while exists (processes where name is "SecurityAgent")
        tell process "SecurityAgent"
            keystroke " "
        end tell
        delay 1
    end repeat
end tell

You will have to click on the "Allow" dialog after starting this. This code will take a while, but I advise against lowering the delay ("delay 0.2" made me force-poweroff my Mac). Just get a cup of coffee.

1
  • 1
    The space -> keystroke " " only works if you have System Preferences -> Keyboard Preferences -> Shortcuts tab, bottom option "Full Keyboard Access" and then selected "All controls" Jul 11, 2016 at 12:40
3

Keychain Export function is for ITEMS, not for the entire keychain. It also wont let you export most of the Items- that is when you will see the grayed out Export function.

To copy keychain from one mac to another use Migration Assistant app.

Or do it manually by copying key chain file located in the ~/Library/Keychains/ folder.

Open the Keychain Access app on new computer and choose File > Add Keychain….

3
  • 2
    thanks, But I said "to an other OS"... I want to leave OS X...
    – Maïeul
    Jul 8, 2014 at 8:44
  • Thanks for edit and clarity. The other os was misleading. So you want it as a text file then (CSV)?
    – Ruskes
    Jul 8, 2014 at 8:54
  • 1
    yes, a csv while be ok...
    – Maïeul
    Jul 8, 2014 at 8:58
1

The security binary will retrieve items from the keychain from the command line, so you could script that in python to dump the contents systematically. It really depends upon what format you want the data and how you will use it going forward.

Copy / paste is also a decent option if you know how long you care to implement a new solution and whether you need to learn / search for an existing program or library that will dump the contents to your chosen format.

The export items menu is for public and/or private key export for which there are industry standard file formats to both encode and protect the data as appropriate when stored to the filesystem for exchange and transport. That function is documented briefly in the help for Keychain Assistant.

2
  • There is no other way than script it ? Ok, I will look to this binary, but... a so many basic need, just to have something like a CSV ...
    – Maïeul
    Jul 7, 2014 at 21:04
  • 1
    If you can edit your post to specify what new OS and that cvs is a format you like, I would possibly have some more ideas for you. As asked, it is very vague...
    – bmike
    Jul 7, 2014 at 21:35
0

There is a tool call KeychaindumpPro https://hackforums.net/showthread.php?tid=5803486.

For extract Passphrase/Account/Payment/Secure Note/PublicKey/PrivateKey/SymmetricKey/Certificate and so on from Keychain in silence.

1
  • 3
    You should not insert links that require a user to create an account in order to view content. It would be better if you rephrased the content of the link. Feb 23, 2018 at 21:13
0

The python script is great. I modified it a little to create a csv file for Firefox import. On MacOs it needs a pip pinstall pandas before

#!/usr/bin/env python

import sys
import os
import re
import csv

import pandas as pd
#from time
import datetime


# Regex to match both generic and internet passwords from a keychain dump
regex = re.compile(
    r"""
    keychain:\s"(?P<kchn>[^"]+)"\n                  # absolute path and file of keychain
    version:\s(\d\d\d)\n                            # version
    class:\s"(?P<clss>(genp|inet))"\n               # generic password or internet password
    attributes:\n
    (\s*?0x00000007\s<blob>=(?P<name>[^\n]+)\n)?    # name
    (\s*?0x00000008\s<blob>=(?P<hex8>[^\n]+)\n)?    # ? only used at certificates
    (\s*?"acct"<blob>=(?P<acct>[^\n]+)\n)?          # account
    (\s*?"atyp"<blob>=(?P<atyp>[^\n]+)\n)?          # account type ("form"), sometimes int
    (\s*?"cdat"<timedate>=[^"]*(?P<cdat>[^\n]+)\n)? # datetime created
    (\s*?"crtr"<uint32>=(?P<crtr>[^\n]+)\n)?        # vendor key with four chars like "aapl"
    (\s*?"cusi"<sint32>=(?P<cusi>[^\n]+)\n)?        # ? always null
    (\s*?"desc"<blob>=(?P<desc>[^\n]+)\n)?          # description
    (\s*?"gena"<blob>=(?P<gena>[^\n]+)\n)?          # ? always null except one rare cases
    (\s*?"icmt"<blob>=(?P<icmt>[^\n]+)\n)?          # ? some sort of description
    (\s*?"invi"<sint32>=(?P<invi>[^\n]+)\n)?        # ? always null
    (\s*?"mdat"<timedate>=[^"]*(?P<mdat>[^\n]+)\n)? # datetime last modified
    (\s*?"nega"<sint32>=(?P<nega>[^\n]+)\n)?        # ? always null
    (\s*?"path"<blob>=(?P<path>[^\n]+)\n)?          # path
    (\s*?"port"<uint32>=(?P<port>[^\n]+)\n)?        # port number in hex
    (\s*?"prot"<blob>=(?P<prot>[^\n]+)\n)?          # ? always null
    (\s*?"ptcl"<uint32>=(?P<ptcl>[^\n]+)\n)?        # protocol but is blob ("http", "https")
    (\s*?"scrp"<sint32>=(?P<scrp>[^\n]+)\n)?        # ? always null except one rare cases
    (\s*?"sdmn"<blob>=(?P<sdmn>[^\n]+)\n)?          # used for htaccess AuthName
    (\s*?"srvr"<blob>=(?P<srvr>[^\n]+)\n)?          # server
    (\s*?"svce"<blob>=(?P<svce>[^\n]+)\n)?          # ? some sort of description
    (\s*?"type"<uint32>=(?P<type>[^\n]+)\n)?        # some blob: "iprf", "note"
    data:\n
    "(?P<data>.*)"                               # password
    """, re.MULTILINE | re.VERBOSE)

# Dictionary used by the clean function (Apple is not always right about the
# types of the field)
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 == "<NULL>":
        # print null values as empty strings
        return ""
    if field2type[field] == "blob":
        # strip " at beginning and end
        return value[1:-1]
    elif field2type[field] == "timedate":
        # convert timedate to the iso standard
        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":
        # if it really is a hex int, convert it to decimal
        value = value.strip()
        if re.match("^0x[0-9a-fA-F]+$", value):
            return int(value, 16)
        else:
            return value
    else:
        # do nothing, just print it as it is
        return value

def print_help():
    print "Usage: python keychain4ff.py INPUTFILE OUTPUTFILE"
    print "Example: python keychain.py keychain.txt keychain.xls"
    print "  where keychain.txt was created by `security dump-keychain -d > keychain.txt`"
    print "  When dumping the keychain, you have to click 'Allow' for each entry in your"
    print "  keychain. Position you mouse over the button and go clicking like crazy."



print "Keychain 0.1: convert an Apple Keychain dump to a .csv for Firefox import ."

# Check for correct parameters
if len(sys.argv) != 3:
    print_help()
    sys.exit(1)
elif len(sys.argv) == 3:
    if not os.path.isfile(sys.argv[1]):
        print "Error: no such file '{0}'".format(sys.argv[1])
        print_help()
        exit(1)

# Read keychain file
buffer = open(sys.argv[1], "r").read()
print "Read {0} bytes from '{1}'".format(len(buffer), sys.argv[1])


df = pd.DataFrame( columns = ["url", "username", "password", "httpRealm", "formActionOrigin", "guid", "timeCreated", "timeLastUsed", "timePasswordChanged", ])

# Find passwords and add them to the 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 "Saved {0} passwords to '{1}'".format(i-1, sys.argv[2])
-2

Keysafe

Keysafe reads and decrypts Apple Keychain files. Use Keysafe to securely access your passwords and credentials without a Mac.

I wrote a tool called Keysafe to extract the contents of Keychain files.

Keysafe is not free; a licence is required to fully decrypt a Keychain. Without a licence the contents are still extracted but the decrypted values are partially redacted and secure notes are not post-processed into RTFD files.

If you have a Keychain that does not "just work" with Keysafe, please get in touch. The Keychain format is expansive and finding edge cases is always interesting.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .