The documentation for the Keychain Services API leaves a bit to be desired. One thing that I can't seem to locate are details on accessing the Secure Notes that the Keychain Access app lets you add and edit.
Any insight would be much appreciated. Thanks.
Everything stored in iCloud Keychain is secure—it's protected by industry-standard encryption. Your iCloud Keychain can't be set up on another Mac or iOS or iPadOS device unless you approve it.
When you want to access the note again, just go back into the Keychain Access app, select the Secure Notes tab, and click on the Secure Note that you want to view. If you check the Show note box, you'll be prompted to enter your password so you can see it.
The note and all of its attachments are encrypted using AES with Galois/Counter Mode (AES-GCM). New records are created in Core Data and CloudKit to store the encrypted note, attachments, tag, and initialization vector.
Just to expand on the accepted answer:
Indeed "secure notes" are stored as just specially formatted generic passwords. Thus, if you create a secure note, you can get programmatic access to it using the Keychain Services API: SecKeychainFindGenericPassword()
or the security
command line tool.
As an example of using security
, if you have a secure note named "Testing Note":
You will need to search for "secure notes" and for the title of the note, "Testing Note". The "type" (or desc
field) will be "note", and the "service" (or svce
field, the name of the keychain entry) will be the actual title of the note. It seems that for every field you specify, it has to be exact, so searching for "Testing *" or "Testing" will not turn up any results for our note.
So you can use this command to search for notes with the type "secure note" and title "Testing Note":
security find-generic-password -C note -s "Testing Note"
And you get as a result:
keychain: "/Users/USERNAME/Library/Keychains/login.keychain"
class: "genp"
attributes:
0x00000007 <blob>="Testing Note"
0x00000008 <blob>=<NULL>
"acct"<blob>=<NULL>
"cdat"<timedate>=0x32303134313231323137333130395A00 "20141212173109Z\000"
"crtr"<uint32>=<NULL>
"cusi"<sint32>=<NULL>
"desc"<blob>="secure note"
"gena"<blob>=<NULL>
"icmt"<blob>=<NULL>
"invi"<sint32>=<NULL>
"mdat"<timedate>=0x32303134313231323137333130395A00 "20141212173109Z\000"
"nega"<sint32>=<NULL>
"prot"<blob>=<NULL>
"scrp"<sint32>=<NULL>
"svce"<blob>="Testing Note"
"type"<uint32>="note"
To get the password to output as well, you will need to also pass the -g
option to the security
command, and unless you have explicitly set security
as a trusted/allowed program to access that keychain item, it will ask you if you want allow access to a keychain item:
Looking at just the password output (you can use the -w
option to only output the "password", or the text of our note, however you don't get the 'decoded' output, just the hex), you get:
security find-generic-password -C note -s "Testing Note" -w
(formatted for clarity)
3c3f786d 6c207665 7273696f 6e3d2231 2e302220 656e636f
64696e67 3d225554 462d3822 3f3e0a3c 21444f43 54595045
20706c69 73742050 55424c49 4320222d 2f2f4170 706c652f
2f445444 20504c49 53542031 2e302f2f 454e2220 22687474
703a2f2f 7777772e 6170706c 652e636f 6d2f4454 44732f50
726f7065 7274794c 6973742d 312e302e 64746422 3e0a3c70
6c697374 20766572 73696f6e 3d22312e 30223e0a 3c646963
....... (and so on)
Not very useful! If we use some python code to decode it: (or any language of your choice)
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import plistlib, pprint, binascii
# not full hex string for brevity!
hex_data = '''3c3f786d6c2076657273696f6e3d22312e3022206....'''
# decode hex into bytes
xml_bytes = binascii.unhexlify(hex_data)
# create ElementTree object since its an XML PList
ET.fromstring(xml_bytes)
# print out xml
print(ET.tostring(xml_bytes))
# or you can load it straight into a python object using plistlib
plist_dict = plistlib.loads(xml_bytes)
pprint.pprint(plist_dict)
Now we are getting somewhere! The result of decoding it is:
<plist version="1.0">
<dict>
<key>NOTE</key>
<string>12345
abcdefghijklmnopqrstuvwxyz
HELLO WORLD
=)
</string>
<key>RTFD</key>
<data>
cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC43AQAAKwAAAAEAAAAvAQAAe1xy
dGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxMzQzXGNvY29hc3VicnRmMTYwCntc
Zm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTt9CntcY29sb3J0Ymw7
XHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O30KXHBhcmRcdHg1NjBcdHgxMTIwXHR4MTY4
MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgwXHR4NTA0MFx0eDU2MDBc
dHg2MTYwXHR4NjcyMFxwYXJkaXJuYXR1cmFsCgpcZjBcZnMyNCBcY2YwIDEyMzQ1XAph
YmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5elwKSEVMTE8gV09STERcCj0pXAp9AQAAACMA
AAABAAAABwAAAFRYVC5ydGYQAAAAXSaLVLYBAAAAAAAAAAAAAA==
</data>
</dict>
</plist>
So we obviously have the plaintext password as the value to the key "NOTE" (as this is how plists store dictionaries), but what is the "RTFD" key? Looking at it in binary gives the impression that its some sort of rtfd file:
b'rtfd\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00TXT.rtf\x01 ......
But saving it as a .rtfd doesn't work, but then I realized that RTFDs saved from TextEdit for example, are bundles! So how does that work... you can't really serialize a bundle to bytes, as its a folder with files inside, but then upon more searching, (I found the Apple Type Code list , and there is "com.apple.rtfd", but also "com.apple.flat-rtfd", which it says is a "pasteboard" format!
So I used a sample application from Apple that shows detailed information about the clipboard/pasteboard. Then you can right click in Keychain access, "copy secure note":
And then if you look at the bytes in ClipboardViewer, you see it matches the un-hexed bytes in the tag in the plist.
Whew! That was a lot longer then I expected.... So in short, a Secure Note is just a generic password, with a title, and the password part being an Apple XML Plist, with the plaintext data, and the data in a pasteboard format suitable for copying to the clipboard with "Copy Secure note to clipboard".
I hope this clears up how Secure Notes are stored, as there is indeed a lack of API functions that access secure notes, and nothing in the official Keychain access API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With