Skip to content

Instantly share code, notes, and snippets.

@djui
Last active June 15, 2018 02:00
Show Gist options
  • Save djui/2b835648438dd354e051 to your computer and use it in GitHub Desktop.
Save djui/2b835648438dd354e051 to your computer and use it in GitHub Desktop.
OS X Keychain implementation in Python
class Keychain():
"""Reimplementation of Keyring for OS X.
Problem with Keyring is it doesn't allow to set a label with is required
for this use case.
"""
class KeychainError(Exception):
def __init__(self, code=None, msg=None, cause=None):
self.code = code
self.cause = cause
if msg:
self.msg = msg
elif code == 44:
self.msg = 'Keychain item not found'
elif code == 51:
self.msg = 'Keychain read permissions error'
elif code == 195:
self.msg='Keychain write permissions error'
else:
self.msg = 'Unknown Keychain error'
def __str__(self):
return repr(self.msg) + ' (' + repr(self.code) + '): ' + repr(self.cause)
Login = '~/Library/Keychains/login.keychain'
System = '/Library/Keychains/System.keychain'
Default = None
def __init__(self, keychain=None):
self.keychain = keychain
def get(self, **kwargs):
cmd = ['/usr/bin/security', 'find-generic-password', '-w']
if 'service' in kwargs:
cmd += ['-s', kwargs['service']]
if 'account' in kwargs:
cmd += ['-a', kwargs['account']]
if 'label' in kwargs:
cmd += ['-l', kwargs['label']]
if 'kind' in kwargs:
cmd += ['-D', kwargs['kind']]
if self.keychain:
cmd += [self.keychain]
logging.debug('$ %s', subprocess.list2cmdline(cmd))
try:
output = subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT)
return output.strip()
except subprocess.CalledProcessError as e:
raise Keychain.KeychainError(code=e.returncode, cause=e.output)
def set(self, secret, **kwargs):
cmd = ['/usr/bin/security', 'add-generic-password', '-w', secret, '-U']
if 'service' in kwargs:
cmd += ['-s', kwargs['service']]
else:
cmd += ['-s', self.lookup_service(**kwargs)]
if 'account' in kwargs:
cmd += ['-a', kwargs['account']]
if 'label' in kwargs:
cmd += ['-l', kwargs['label']]
if 'kind' in kwargs:
cmd += ['-D', kwargs['kind']]
if self.keychain:
cmd += [self.keychain]
logging.debug('$ %s', subprocess.list2cmdline(cmd))
try:
subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise Keychain.KeychainError(code=e.returncode, cause=e.output)
def lookup_service(self, **kwargs):
cmd = ['/usr/bin/security', 'find-generic-password']
if 'account' in kwargs:
cmd += ['-a', kwargs['account']]
if 'label' in kwargs:
cmd += ['-l', kwargs['label']]
if 'kind' in kwargs:
cmd += ['-D', kwargs['kind']]
if self.keychain:
cmd += [self.keychain]
logging.debug('$ %s', subprocess.list2cmdline(cmd))
try:
output = subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT)
for line in output.split():
if '"svce"<blob>=' in line:
return line[14:-1]
except subprocess.CalledProcessError as e:
raise Keychain.KeychainError(code=e.returncode, cause=e.output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment