Keychain Services API的文档留下了一些不足之处。我找不到的一个事情是如何访问Keychain Access应用程序所允许添加和编辑的安全笔记的详细信息。
任何见解都将不胜感激。谢谢。
Keychain Services API的文档留下了一些不足之处。我找不到的一个事情是如何访问Keychain Access应用程序所允许添加和编辑的安全笔记的详细信息。
任何见解都将不胜感激。谢谢。
仅就已接受答案进行扩展:
确实,“安全笔记”仅作为格式化的通用密码存储。因此,如果您创建了一个安全笔记,可以使用Keychain Services API:SecKeychainFindGenericPassword()
或security
命令行工具进行编程访问。
以使用security
为例,如果你有一个名为“测试笔记”的安全笔记:
您将需要搜索“安全笔记”和笔记标题“Testing Note”。 "类型"(或desc
字段)将是“note”,而“服务”(或svce
字段,密钥链条目的名称)将是笔记的实际标题。似乎对于您指定的每个字段,它都必须是精确的,因此搜索“Testing *”或“Testing”将不会返回我们笔记的任何结果。
所以您可以使用这个命令来搜索类型为“安全笔记”且标题为“Testing Note”的笔记:
security find-generic-password -C note -s "Testing Note"
然后您将得到以下结果:
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"
要想输出密码,您还需要向security
命令传递-g
选项。除非您已将security
明确设置为允许访问该钥匙串项目的受信任/允许程序,否则它将询问您是否允许访问钥匙串项目:
如果只查看密码输出(您可以使用-w
选项仅输出“password”或我们备注的文本,但是您无法获得“解码”输出,只会得到十六进制),则可使用以下命令:
security find-generic-password -C note -s "Testing Note" -w
(已进行格式化以供清晰阅读)
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)
不是很有用!如果我们使用某些Python代码对其进行解码(或您选择的任何语言):
#!/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)
现在我们有了进展!对其进行解码的结果为:
<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>
显然,我们将明文密码作为“NOTE”键的值(因为这就是plist存储字典的方式),但“RTFD”键是什么呢?从二进制角度来看,它给人的印象是某种rtfd文件:
b'rtfd\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00TXT.rtf\x01 ......
但将其保存为.rtfd格式不起作用,但后来我意识到,例如从TextEdit保存的RTFD是一种束!那么这是如何工作的……您实际上无法将束序列化为字节,因为它是一个带有文件的文件夹,但在进一步搜索后,(我找到了Apple Type Code列表,其中有“com.apple.rtfd”,但也有“com.apple.flat-rtfd”,它说是“粘贴板”格式!
所以我使用了一个来自苹果的示例应用程序,该应用程序显示有关剪贴板/粘贴板的详细信息。然后,您可以在钥匙串访问中右键单击“复制安全笔记”:
然后,如果您查看ClipboardViewer中的字节,则会发现它与plist中未加密的字节匹配。
哇!这比我预期的要长得多....因此,简而言之,安全笔记只是通用密码,具有标题,密码部分是Apple XML Plist,并且明文数据以适合复制到剪贴板的粘贴板格式中的数据形式存在,“将安全笔记复制到剪贴板”。
希望这解释了安全笔记的存储方式,因为确实缺少访问安全笔记的API函数,并且官方钥匙串访问API中没有任何内容。
我发现您可以使用安全命令行工具来提取数据。安全笔记以以下特点存储为通用密码:
class: "genp"
- 这与通用密码相同
type<uint32>="note"
- 搜索时可以使用此标识符专门识别安全笔记(使用-C标志)。
desc<blob>="secure note"
- 我不知道是否可以根据此字段搜索,但它确实将项目标识为安全笔记
0x00000007 <blob>= "Note name"
- 我不知道是否可以通过API获取此信息,但是您可以从命令行工具中得到它
acct<blob>=<NULL>
- 这似乎是安全笔记的常见特征
使用命令security dump-keychain
查找有关钥匙串项目的所有有用信息。
您可以使用一长串从MacOS终端中的命令来获取keychain安全笔记的值。以下片段获取名为“foobar”的笔记的值,并将其保存到名为foobar.txt的文件中,位于用户桌面上。
security find-generic-password -C note -s 'foobar' -w | xxd -r -p |
xmllint --xpath "//dict/data/text()" - | base64 --decode |
textutil -stdin -convert txt -output ~/Desktop/foobar.txt
$h = security find-generic-password -a AAAAA -s BBBBB -w
$k = -join (($h | sls '(..)' -a).matches.value | %{[char][int]"0x$_" })
$k
非常简洁,但它正在使用 select-string (sls) 对所有 (-a) 正则表达式匹配组进行匹配,使用任意 2 个字符 ('(..)'),然后我们只获取匹配本身的值,这将是一个数组总和,并将其传递到循环中,通过使用一些 0x 快捷方式将十六进制转换为字符值,最后将所有内容连接在一起。由于我们只是将它们压缩在一起,因此可以将 -join 移动到开头,而不是在结尾处执行 -join ''。
我通常不会这么简洁,但这对我的需求来说是一种边缘情况,我宁愿它短小精悍。我只想将一个大值存储为我的 Mac 上的注释,并在需要时使用它,而不是使用环境变量。我的需求更多是一次性的,但在生产环境中,它可能只是一个环境变量或“从保险库获取”类型的情况,因此这只是在我的 Mac 上摆弄的结果。