問題描述
在Azure Key Vault中集歇,我們可以從Azure門戶中下載證書PEM文件到本地。 可以通過OpenSSL把PFX文件轉換到PEM文件。然后用TXT方式查看內容,操作步驟如下圖:
OpenSSL轉換命令為:
openssl pkcs12 -in "C:\Users\Downloads\mykeyvault01-cscert01-20220316.pfx" -nokeys -out "C:\tool\xd12.pem"
當然前方,Azure也提供了通過PowerShell或CLI命令來下載PEM文件狈醉,操作為:
**az keyvault certificate download** --vault-name vault -n cert-name -f cert.pem
(Source : https://docs.microsoft.com/en-us/cli/azure/keyvault/certificate?view=azure-cli-latest#az-keyvault-certificate-download)
那么,如何可以通過Python SDK的代碼獲取到PEM文件呢惠险?
問題解答
查看 Python SDK Certificate中公布出來的接口舔糖,并沒有 Export, Download 的方法莺匠。** Python Azure Key Vault SDK 中并沒有可以直接下載PEM文件的方法金吗。**
Azure SDK For Python KeyVault -- CertificateClient Class : https://docs.microsoft.com/en-us/python/api/azure-keyvault-certificates/azure.keyvault.certificates.aio.certificateclient?view=azure-python#methods
所以使用原始的SDK Methods方法不可行。
尋找解決方案
通過對CLI (az keyvault certificate download)指令的研究趣竣,發(fā)現(xiàn)CLI使用的是python代碼執(zhí)行的Get Certificates 操作摇庙,實質上是調用的Key Vault的**REST API: **
**Get Certificate: **https://docs.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate#getcertificate
DEBUG az 指令:
az keyvault certificate download --vault-name mykeyvault01 -n cscert01 -f cert2.pem --debug
C:\Users>az keyvault certificate download --vault-name mykeyvault01 -n cscert01 -f cert2.pem --debug
cli.knack.cli: Command arguments: ['keyvault', 'certificate', 'download', '--vault-name', 'mykeyvault01', '-n', 'cscert01', '-f', 'cert2.pem', '--debug']
cli.knack.cli: __init__ debug log:
Enable color in terminal.
Init colorama.
cli.knack.cli: Event: Cli.PreExecute []
cli.knack.cli: Event: CommandParser.OnGlobalArgumentsCreate [<function CLILogging.on_global_arguments at 0x033452F8>, <function OutputProducer.on_global_arguments at 0x034F0190>, <function CLIQuery.on_global_arguments at 0x03607D60>]
cli.knack.cli: Event: CommandInvoker.OnPreCommandTableCreate []
cli.azure.cli.core: Modules found from index for 'keyvault': ['azure.cli.command_modules.keyvault']
cli.azure.cli.core: Loading command modules:
cli.azure.cli.core: Name Load Time Groups Commands
cli.azure.cli.core: keyvault 0.038 19 117 cli.azure.cli.core: Total (1) 0.038 19 117 cli.azure.cli.core: These extensions are not installed and will be skipped: ['azext_ai_examples', 'azext_next']
cli.azure.cli.core: Loading extensions:
cli.azure.cli.core: Name Load Time Groups Commands Directory
cli.azure.cli.core: Total (0) 0.000 0 0 cli.azure.cli.core: Loaded 19 groups, 117 commands.
cli.azure.cli.core: Found a match in the command table.
cli.azure.cli.core: Raw command : keyvault certificate download
cli.azure.cli.core: Command table: keyvault certificate download
cli.knack.cli: Event: CommandInvoker.OnPreCommandTableTruncate [<function AzCliLogging.init_command_file_logging at 0x039701D8>]
cli.azure.cli.core.azlogging: metadata file logging enabled - writing logs to 'C:\Users\.azure\commands\2022-03-16.14-46-58.keyvault_certificate_download.21860.log'.
az_command_data_logger: command args: keyvault certificate download --vault-name {} -n {} -f {} --debug
cli.knack.cli: Event: CommandInvoker.OnPreArgumentLoad [<function register_global_subscription_argument.<locals>.add_subscription_parameter at 0x039B6268>, <function register_global_query_examples_argument.<locals>.register_query_examples at 0x039D8928>]
cli.knack.cli: Event: CommandInvoker.OnPostArgumentLoad []
cli.knack.cli: Event: CommandInvoker.OnPostCommandTableCreate [<function register_ids_argument.<locals>.add_ids_arguments at 0x039D8970>, <function register_cache_arguments.<locals>.add_cache_arguments at 0x039D8A00>]
cli.knack.cli: Event: CommandInvoker.OnCommandTableLoaded []
cli.knack.cli: Event: CommandInvoker.OnPreParseArgs []
cli.knack.cli: Event: CommandInvoker.OnPostParseArgs [<function OutputProducer.handle_output_argument at 0x034F01D8>, <function CLIQuery.handle_query_parameter at 0x03607DA8>, <function register_global_query_examples_argument.<locals>.handle_example_parameter at 0x039D88E0>, <function register_ids_argument.<locals>.parse_ids_arguments at 0x039D89B8>]
msrest.universal_http.requests: Configuring retry: max_retries=4, backoff_factor=0.8, max_backoff=90 msrest.service_client: Accept header absent and forced to application/json
msrest.universal_http: Configuring redirects: allow=True, max=30 msrest.universal_http: Configuring request: timeout=100, verify=True, cert=None
msrest.universal_http: Configuring proxies: ''
msrest.universal_http: Evaluate proxies against ENV settings: True urllib3.connectionpool: Starting new HTTPS connection (1): mykeyvault01.vault.azure.cn:443 urllib3.connectionpool: https://mykeyvault01.vault.azure.cn:443 "GET /certificates/cscert01/?api-version=7.0 HTTP/1.1" 401 97 cli.azure.cli.core._profile: Profile.get_raw_token invoked with resource='https://vault.azure.cn', subscription=None, tenant=None
cli.azure.cli.core.util: attempting to read file C:\Users\.azure\accessTokens.json as utf-8-sig
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - Authority:Performing instance discovery: ...
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - Authority:Performing static instance discovery
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - Authority:Authority validated via static instance discovery
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - TokenRequest:Getting token from cache with refresh if necessary.
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - CacheDriver:finding with query keys: {'_clientId': '...', 'userId': '...'}
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - CacheDriver:Looking for potential cache entries: {'_clientId': '...', 'userId': '...'}
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - CacheDriver:Found 9 potential entries.
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - CacheDriver:Resource specific token found.
adal-python: 3f4f9351-cdc1-4891-b125-1f02ac02741b - CacheDriver:Returning token from cache lookup, AccessTokenId: b'wR5KoJYE=', RefreshTokenId: b'5VNpWgDCg4ydvuf2PeWxGaph/r7KMelGLXVY+jLV89s='
urllib3.connectionpool: https://mykeyvault01.vault.azure.cn:443 "GET /certificates/cscert01/?api-version=7.0 HTTP/1.1" 200 2114 cli.knack.cli: Event: CommandInvoker.OnTransformResult [<function _resource_group_transform at 0x039A5C88>, <function _x509_from_base64_to_hex_transform at 0x039A5CD0>]
cli.knack.cli: Event: CommandInvoker.OnFilterResult []
cli.knack.cli: Event: Cli.SuccessfulExecute []
cli.knack.cli: Event: Cli.PostExecute [<function AzCliLogging.deinit_cmd_metadata_logging at 0x039702F8>]
az_command_data_logger: exit code: 0 cli.__main__: Command ran in 3.206 seconds (init: 0.394, invoke: 2.812)
telemetry.save: Save telemetry record of length 3005 in cache
telemetry.check: Negative: The C:\Users\.azure\telemetry.txt was modified at 2022-03-16 14:43:00.945931, which in less than 600.000000 s
如以上黃色高亮內容, download指令調用了 "GET /certificates/cscert01/?api-version=7.0 HTTP/1.1" 200 2114遥缕,且返回的HTTP Code為200 成功卫袒。所以當我們單獨對get certificates接口請求時,在返回結果中单匣,發(fā)現(xiàn)cer屬性值就是證書PEM格式內容夕凝。通過Postman發(fā)送請求并驗證結果:
那么,通過Python Azure SDK的 certificate_client.get_certificate方法户秤,獲取到certificate對象后码秉,其中包含的cer值,是否也是PEM的內容呢鸡号?
我們通過下面的代碼來進行驗證:
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient
import ssl
credential = DefaultAzureCredential()
certificate_client = CertificateClient(vault_url=https://yourkeyvaultname.vault.azure.cn/, credential=credential)
certificate = certificate_client.get_certificate("your certificate name") print(certificate.name) print(certificate.properties.version) print(certificate.policy.issuer_name) print(str(certificate.cer)) # Convert the certificate to PEM format
cert_PEM = ssl.DER_cert_to_PEM_cert(certificate.cer); print("Certificate in PEM format:"); print(cert_PEM);
是的转砖,在certificate對象中,cer就是我們證書的內容鲸伴,但是由于格式為DER府蔗,所以使用了SSL包中的 DER_cert_to_PEM_cert 方法完成轉換,最終得到PEM文件內容。
在 python代碼中獲取到PEM內容后,剩下的部分就是把內容輸出到 .pem 文件即可礁遣。
##接上一段代碼
filename = 'mypem1.pem'
# Open the file in append mode and append the new content in file_object
with open(filename, 'a') as file_object:
file_object.write(cert_PEM) print("output the PEM file End!")
注意:在創(chuàng)建 certificate_client對象時,需要使用credential薛匪,而以上代碼使用的是默認的DefaultAzureCredential(),在執(zhí)行代碼的本機中,已經(jīng)配置了如下環(huán)境變量:
- AZURE_TENANT_ID
- AZURE_CLIENT_ID
- AZURE_CLIENT_SECRET
使用 ClientSecretCredential 認證方式后,代碼修改如下:
import os
from azure.keyvault.certificates import CertificateClient
import ssl
from azure.identity import ClientSecretCredential
from msrestazure.azure_cloud import AZURE_CHINA_CLOUD
print('AZURE_TENANT_ID:' +os.environ["AZURE_TENANT_ID"])
print('AZURE_CLIENT_ID:' +os.environ["AZURE_CLIENT_ID"])
print('AZURE_CLIENT_SECRET:' +os.environ["AZURE_CLIENT_SECRET"])
credential = ClientSecretCredential (client_id=os.environ["AZURE_CLIENT_ID"],client_secret=os.environ["AZURE_CLIENT_SECRET"],tenant_id=os.environ["AZURE_TENANT_ID"],cloud_environment=AZURE_CHINA_CLOUD,china=True)
certificate_client = CertificateClient(vault_url="https://yourkeyvault.vault.azure.cn/", credential=credential)
certificate = certificate_client.get_certificate("your certificate name") print(certificate.name) print(certificate.properties.version) print(certificate.policy.issuer_name) print(str(certificate.cer)) # Convert the certificate to PEM format
cert_PEM = ssl.DER_cert_to_PEM_cert(certificate.cer); print("Certificate in PEM format:"); print(cert_PEM);
filename = 'mypem2.pem'
# Open the file in append mode and append the new content in file_object
with open(filename, 'a') as file_object:
file_object.write(cert_PEM) print("output the PEM file End!")
參考文檔
Retrieve a Certificate:https://docs.microsoft.com/en-us/python/api/overview/azure/keyvault-certificates-readme?view=azure-python#retrieve-a-certificate
Der_cert_to_pem_cert Function Of Ssl Module In Python: https://pythontic.com/ssl/ssl-module/der_cert_to_pem_cert
A certificate bundle consists of a certificate (X509) plus its attributes: https://docs.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate#certificatebundle
當在復雜的環(huán)境中面臨問題狂男,格物之道需:濁而靜之徐清综看,安以動之徐生品腹。 云中,恰是如此!
標簽: Azure Developer, Azure Key Vault, 下載證書PEM文件, az keyvault certificate download, certificate_client.get_certificate, ClientSecretCredential