在 Ubuntu 24.04.1 LTS (WSL) 中利用 openssl 天生 keybox.xml

打印 上一主题 下一主题

主题 988|帖子 988|积分 2966

看到“天生 keybox.xml”,大概率都会联想到 PIF 和 Tricky Store。这里就不多解释它们的用途了。最近在网上看到天生非 AOSP keybox 的教程,在这里做一些增补,并将代码打包成一个 Python 脚本。
参考自:

  • Idea 提供者:https://xdaforums.com/t/tee-hacking.4662185/page-21#post-89847987(如果打不开大概被重定向去另一个网页可能要刷新几遍才能正确打开这个网页) ,该原始 Idea 须要借助一个密码学工具网站;
  • RSA 私钥转换:https://stackoverflow.com/questions/17733536/how-to-convert-a-private-key-to-an-rsa-private-key。
做出以下调整:

  • 直接利用一站式脚本实行,主动利用 openssl 天生三个 PEM 文件,如果用于预检测的 openssl version 命令实行失败,主动尝试通过 sudo apt-get install libssl-dev 举行安装;
  • 实现对新版 openssl 天生的 RSA 私钥举行辨认,并从 PKCS8 转换为 PKCS1。
直接上 Python 代码,记得以 LF 形式保存换行符,并在 Ubuntu 24.04.1 LTS 中运行。
  1. import os
  2. try:
  3.         os.chdir(os.path.abspath(os.path.dirname(__file__)))
  4. except:
  5.         pass
  6. EXIT_SUCCESS = 0
  7. EXIT_FAILURE = 1
  8. EOF = (-1)
  9. keyboxFormatter = """<?xml version="1.0"?>
  10. <AndroidAttestation>
  11. <NumberOfKeyboxes>1</NumberOfKeyboxes>
  12. <Keybox DeviceID="YourDeviceID">
  13. <Key algorithm="ecdsa">
  14. <PrivateKey format="pem">
  15. {0}</PrivateKey>
  16. <CertificateChain>
  17. <NumberOfCertificates>1</NumberOfCertificates>
  18. <Certificate format="pem">
  19. {1}</Certificate>
  20. </CertificateChain>
  21. </Key>
  22. <Key algorithm="rsa">
  23. <PrivateKey format="pem">
  24. {2}</PrivateKey>
  25. </Key>
  26. </Keybox>
  27. </AndroidAttestation>"""
  28. def execute(commandline:str) -> int|None:
  29.         if isinstance(commandline, str):
  30.                 print("$ " + commandline)
  31.                 return os.system(commandline)
  32.         else:
  33.                 return None
  34. def handleOpenSSL(flag:bool = True) -> bool|None:
  35.         if isinstance(flag, bool):
  36.                 errorLevel = execute("openssl version")
  37.                 if EXIT_SUCCESS == errorLevel:
  38.                         return True
  39.                 elif flag: # can try again
  40.                         execute("sudo apt-get install openssl libssl-dev")
  41.                         return handleOpenSSL(False)
  42.                 else:
  43.                         return False
  44.         else:
  45.                 return None
  46. def pressTheEnterKeyToExit(errorLevel:int|None = None):
  47.         try:
  48.                 print("Please press the enter key to exit ({0}). ".format(errorLevel) if isinstance(errorLevel, int) else "Please press the enter key to exit. ")
  49.                 input()
  50.         except:
  51.                 pass
  52. def main() -> int:
  53.         # Parameters #
  54.         failureCount = 0
  55.         ecPrivateKeyFilePath = "ecPrivateKey.pem"
  56.         certificateFilePath = "certificate.pem"
  57.         rsaPrivateKeyFilePath = "rsaPrivateKey.pem"
  58.         oldRsaPrivateKeyFilePath = "oldRsaPrivateKey.pem"
  59.         keyboxFilePath = "keybox.xml"
  60.        
  61.         # First-phase Generation #
  62.         failureCount += execute("openssl ecparam -name prime256v1 -genkey -noout -out "{0}"".format(ecPrivateKeyFilePath)) != 0
  63.         failureCount += execute("openssl req -new -x509 -key "{0}" -out {1} -days 3650 -subj "/CN=Keybox"".format(ecPrivateKeyFilePath, certificateFilePath)) != 0
  64.         failureCount += execute("openssl genrsa -out "{0}" 2048".format(rsaPrivateKeyFilePath)) != 0
  65.         if failureCount > 0:
  66.                 print("Cannot generate a sample ``keybox.xml`` file since {0} PEM file{1} not generated successfully. ".format(failureCount, ("s were" if failureCount > 1 else " was")))
  67.                 pressTheEnterKeyToExit(EOF)
  68.                 return EOF
  69.        
  70.         # First-phase Reading #
  71.         try:
  72.                 with open(ecPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  73.                         ecPrivateKey = f.read()
  74.                 with open(certificateFilePath, "r", encoding = "utf-8") as f:
  75.                         certificate = f.read()
  76.                 with open(rsaPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  77.                         rsaPrivateKey = f.read()
  78.         except BaseException as e:
  79.                 print("Failed to read one or more of the PEM files. Details are as follows. \n{0}".format(e))
  80.                 pressTheEnterKeyToExit(EOF)
  81.                 return EOF
  82.        
  83.         # Second-phase Generation #
  84.         if rsaPrivateKey.startswith("-----BEGIN PRIVATE KEY-----"):
  85.                 print("A newer openssl version is used. The RSA private key in the PKCS8 format will be converted to that in the PKCS1 format soon. ")
  86.                 failureCount += execute("openssl rsa -in "{0}" -out "{1}" -traditional".format(rsaPrivateKeyFilePath, oldRsaPrivateKeyFilePath))
  87.                 if failureCount > 0:
  88.                         print("Cannot convert the RSA private key in the PKCS8 format to that in the PKCS1 format. ")
  89.                         pressTheEnterKeyToExit(EOF)
  90.                         return EOF
  91.                 else:
  92.                         print("Finished converting the RSA private key in the PKCS8 format to that in the PKCS1 format. ")
  93.                         try:
  94.                                 with open(oldRsaPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  95.                                         rsaPrivateKey = f.read()
  96.                         except BaseException as e:
  97.                                 print("Failed to update the RSA private key from "{0}". Details are as follows. \n{1}".format(oldRsaPrivateKeyFilePath, e))
  98.                                 pressTheEnterKeyToExit(EOF)
  99.                                 return EOF
  100.        
  101.         # Keybox Generation #
  102.         keybox = keyboxFormatter.format(ecPrivateKey, certificate, rsaPrivateKey)
  103.         print(keybox)
  104.         try:
  105.                 with open(keyboxFilePath, "w", encoding = "utf-8") as f:
  106.                         f.write(keybox)
  107.                 print("Successfully wrote the keybox to "{0}". ".format(keyboxFilePath))
  108.                 pressTheEnterKeyToExit(EXIT_SUCCESS)
  109.                 return EXIT_SUCCESS
  110.         except BaseException as e:
  111.                 print("Failed to write the keybox to "{0}". Details are as follows. \n{1}".format(keyboxFilePath, e))
  112.                 pressTheEnterKeyToExit(EXIT_FAILURE)
  113.                 return EXIT_FAILURE
  114. if "__main__" == __name__:
  115.         exit(main())
复制代码
更换 /data/adb/tricky_store/keybox.xml 之前,记得先将原来的 keybox.xml(刷入 tricky_store 时自带的谁人基于 AOSP 的 keybox.xml)备份为 keybox.xml.bak。

12月14日破晓做了一些更新:

  • 支持粗略检查三个子密钥文件内容,支持 OpenSSL 私钥转 RSA 私钥;
  • 如果文件存在,步调会提示是否覆盖;
  • 装备ID随机天生。
  1. import os
  2. from random import randint, choice
  3. from base64 import b64decode
  4. try:
  5.         os.chdir(os.path.abspath(os.path.dirname(__file__)))
  6. except:
  7.         pass
  8. EXIT_SUCCESS = 0
  9. EXIT_FAILURE = 1
  10. EOF = (-1)
  11. LB = 2 # the lower bound of the length of the device ID
  12. UB = 12 # the upper bound of the length of the device ID
  13. CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  14. keyboxFormatter = """<?xml version="1.0"?>
  15. <AndroidAttestation>
  16. <NumberOfKeyboxes>1</NumberOfKeyboxes>
  17. <Keybox DeviceID="{0}">
  18. <Key algorithm="ecdsa">
  19. <PrivateKey format="pem">
  20. {1}</PrivateKey>
  21. <CertificateChain>
  22. <NumberOfCertificates>1</NumberOfCertificates>
  23. <Certificate format="pem">
  24. {2}</Certificate>
  25. </CertificateChain>
  26. </Key>
  27. <Key algorithm="rsa">
  28. <PrivateKey format="pem">
  29. {3}</PrivateKey>
  30. </Key>
  31. </Keybox>
  32. </AndroidAttestation>
  33. """
  34. def canOverwrite(flags:list, idx:int, prompts:str|tuple|list|set) -> bool:
  35.         if isinstance(flags, list) and isinstance(idx, int) and -len(flags) <= idx < len(flags) and isinstance(prompts, (str, tuple, list, set)):
  36.                 try:
  37.                         if isinstance(prompts, str):
  38.                                 print(""{0}"".format(prompts))
  39.                                 choice = input("The file mentioned above exists. Overwrite or not [aYn]? ")
  40.                         else:
  41.                                 print(prompts)
  42.                                 choice = input("At least one of the files mentioned above exists. Overwrite or not [aYn]? ")
  43.                         if choice.upper() == "A":
  44.                                 for i in range((idx if idx >= 0 else len(flags) + idx), len(flags)): # overwirte the current file and all the following necessary files no matter whether they exist
  45.                                         flags[i] = True
  46.                                 return True
  47.                         elif choice.upper() == "N":
  48.                                 return False
  49.                         else:
  50.                                 flags[idx] = True
  51.                                 return True
  52.                 except BaseException as e:
  53.                         print(e)
  54.                         return False
  55.         else:
  56.                 input("#")
  57.                 return False
  58. def execute(commandline:str) -> int|None:
  59.         if isinstance(commandline, str):
  60.                 print("$ " + commandline)
  61.                 return os.system(commandline)
  62.         else:
  63.                 return None
  64. def handleOpenSSL(flag:bool = True) -> bool|None:
  65.         if isinstance(flag, bool):
  66.                 errorLevel = execute("openssl version")
  67.                 if EXIT_SUCCESS == errorLevel:
  68.                         return True
  69.                 elif flag: # can try again
  70.                         execute("sudo apt-get install openssl libssl-dev")
  71.                         return handleOpenSSL(False)
  72.                 else:
  73.                         return False
  74.         else:
  75.                 return None
  76. def pressTheEnterKeyToExit(errorLevel:int|None = None):
  77.         try:
  78.                 print("Please press the enter key to exit ({0}). ".format(errorLevel) if isinstance(errorLevel, int) else "Please press the enter key to exit. ")
  79.                 input()
  80.         except:
  81.                 pass
  82. def main() -> int:
  83.         # Parameters #
  84.         failureCount = 0
  85.         deviceID = "".join([choice(CHARSET) for _ in range(randint(LB, UB))]) # or specify the device ID manually like "YourDeviceID"
  86.         ecPrivateKeyFilePath = "ecPrivateKey.pem"
  87.         certificateFilePath = "certificate.pem"
  88.         rsaPrivateKeyFilePath = "rsaPrivateKey.pem"
  89.         keyboxFilePath = "keybox.xml" # None for no files written
  90.         flags = [not (os.path.isfile(ecPrivateKeyFilePath) or os.path.isfile(certificateFilePath)), not os.path.isfile(rsaPrivateKeyFilePath), not os.path.isfile(keyboxFilePath)]
  91.        
  92.         # First-phase Generation #
  93.         if flags[0] or canOverwrite(flags, 0, (ecPrivateKeyFilePath, certificateFilePath)):
  94.                 failureCount += execute("openssl ecparam -name prime256v1 -genkey -noout -out "{0}"".format(ecPrivateKeyFilePath)) != 0
  95.         if flags[0] or not os.path.isfile(certificateFilePath):
  96.                 failureCount += execute("openssl req -new -x509 -key "{0}" -out {1} -days 3650 -subj "/CN=Keybox"".format(ecPrivateKeyFilePath, certificateFilePath)) != 0
  97.         if flags[1] or canOverwrite(flags, 1, rsaPrivateKeyFilePath):
  98.                 failureCount += execute("openssl genrsa -out "{0}" 2048".format(rsaPrivateKeyFilePath)) != 0
  99.         if failureCount > 0:
  100.                 print("Cannot generate a sample ``keybox.xml`` file since {0} PEM file{1} not generated successfully. ".format(failureCount, ("s were" if failureCount > 1 else " was")))
  101.                 pressTheEnterKeyToExit(11)
  102.                 return 11
  103.        
  104.         # First-phase Reading #
  105.         try:
  106.                 with open(ecPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  107.                         ecPrivateKey = f.read()
  108.                 with open(certificateFilePath, "r", encoding = "utf-8") as f:
  109.                         certificate = f.read()
  110.                 with open(rsaPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  111.                         rsaPrivateKey = f.read()
  112.         except BaseException as e:
  113.                 print("Failed to read one or more of the PEM files. Details are as follows. \n{0}".format(e))
  114.                 pressTheEnterKeyToExit(12)
  115.                 return 12
  116.        
  117.         # Second-phase Generation #
  118.         if flags[1]: # only updates the key content when the original key is newly generated or updating is allowed
  119.                 if rsaPrivateKey.startswith("-----BEGIN PRIVATE KEY-----") and rsaPrivateKey.rstrip().endswith("-----END PRIVATE KEY-----"):
  120.                         print("A newer openssl version is used. The RSA private key in the PKCS8 format will be converted to that in the PKCS1 format soon. ")
  121.                         failureCount += execute("openssl rsa -in "{0}" -out "{0}" -traditional".format(rsaPrivateKeyFilePath))
  122.                         if failureCount > 0:
  123.                                 print("Cannot convert the RSA private key in the PKCS8 format to that in the PKCS1 format. ")
  124.                                 pressTheEnterKeyToExit(13)
  125.                                 return 13
  126.                         else:
  127.                                 print("Finished converting the RSA private key in the PKCS8 format to that in the PKCS1 format. ")
  128.                                 try:
  129.                                         with open(rsaPrivateKeyFilePath, "r", encoding = "utf-8") as f:
  130.                                                 rsaPrivateKey = f.read()
  131.                                 except BaseException as e:
  132.                                         print("Failed to update the RSA private key from "{0}". Details are as follows. \n{1}".format(rsaPrivateKeyFilePath, e))
  133.                                         pressTheEnterKeyToExit(14)
  134.                                         return 14
  135.                 elif rsaPrivateKey.startswith("-----BEGIN OPENSSH PRIVATE KEY-----") and rsaPrivateKey.rstrip().endswith("-----END OPENSSH PRIVATE KEY-----"):
  136.                         print("An OpenSSL private key is detected, which will be converted to the RSA private key soon. ")
  137.                         failureCount += execute("ssh-keygen -p -m PEM -f "{0}" -N """.format(rsaPrivateKeyFilePath))
  138.                         if failureCount > 0:
  139.                                 print("Cannot convert the OpenSSL private key to the RSA private key. ")
  140.                                 pressTheEnterKeyToExit(15)
  141.                                 return 15
  142.                         else:
  143.                                 print("Finished converting the OpenSSL private key to the RSA private key. ")
  144.                                 try:
  145.                                         with open(rsaPrivateKeyFilePath, "r", encoding = "utf-8") as f: # the ``ssh-keygen`` overwrites the file though no obvious output filepaths specified
  146.                                                 rsaPrivateKey = f.read()
  147.                                 except BaseException as e:
  148.                                         print("Failed to update the RSA private key from "{0}". Details are as follows. \n{1}".format(rsaPrivateKeyFilePath, e))
  149.                                         pressTheEnterKeyToExit(16)
  150.                                         return 16
  151.        
  152.         # Brief Checks #
  153.         if not (ecPrivateKey.startswith("-----BEGIN EC PRIVATE KEY-----") and ecPrivateKey.rstrip().endswith("-----END EC PRIVATE KEY-----")):
  154.                 print("An invalid EC private key is detected. Please try to use the latest key generation tools to solve this issue. ")
  155.                 pressTheEnterKeyToExit(17)
  156.                 return 17
  157.         if not (certificate.startswith("-----BEGIN CERTIFICATE-----") and certificate.rstrip().endswith("-----END CERTIFICATE-----")):
  158.                 print("An invalid certificate is detected. Please try to use the latest key generation tools to solve this issue. ")
  159.                 pressTheEnterKeyToExit(18)
  160.                 return 18
  161.         if not (rsaPrivateKey.startswith("-----BEGIN RSA PRIVATE KEY-----") and rsaPrivateKey.rstrip().endswith("-----END RSA PRIVATE KEY-----")):
  162.                 print("An invalid final RSA private key is detected. Please try to use the latest key generation tools to solve this issue. ")
  163.                 pressTheEnterKeyToExit(19)
  164.                 return 19
  165.        
  166.         # Keybox Generation #
  167.         keybox = keyboxFormatter.format(deviceID, ecPrivateKey, certificate, rsaPrivateKey)
  168.         print("Generated keybox with a length of {0}: ".format(len(keybox)))
  169.         print(keybox)
  170.         if keyboxFilePath is not None and (flags[2] or canOverwrite(flags, 2, keyboxFilePath)):
  171.                 try:
  172.                         with open(keyboxFilePath, "w", encoding = "utf-8") as f:
  173.                                 f.write(keybox)
  174.                         print("Successfully wrote the keybox to "{0}". ".format(keyboxFilePath))
  175.                         pressTheEnterKeyToExit(EXIT_SUCCESS)
  176.                         return EXIT_SUCCESS
  177.                 except BaseException as e:
  178.                         print("Failed to write the keybox to "{0}". Details are as follows. \n{1}".format(keyboxFilePath, e))
  179.                         pressTheEnterKeyToExit(20)
  180.                         return 20
  181.         else:
  182.                 print("The keybox has not been written to any files. Please refer to the text above. ")
  183.                 pressTheEnterKeyToExit(EXIT_FAILURE)
  184.                 return EXIT_FAILURE
  185. if "__main__" == __name__:
  186.         exit(main())
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表