Azure Graph API - 添加自定义签名密钥 - 证书无效:密钥值是无效证书
Azure Graph API - Add a custom signing key - Invalid certificate: Key value is invalid certificate
当我想按照教程进行操作时:
https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/application-saml-sso-configure-api#add-a-custom-signing-key.
我一直在更新服务以添加我的私钥和证书。
我不想点击 azure 界面,我想通过 Graph API 做所有事情(它用于自动测试)。我不使用 Windows,因此我的脚本不使用 Powershell,我在 Linux.
上使用 Python 和 OpenSSL
'Request_BadRequest','Invalid certificate: Key value is invalid certificate',状态:400
发送的数据:
"keyCredentials": [
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "e4ba4cbd-8bfc-4c3a-a6a7-b693c52dc807",
"type": "AsymmetricX509Cert",
"usage": "Sign",
"key": "MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIlPjaWgWswX4CAggAMAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEPFi6NFq0hMBIIEyGzmtT3UF9WDM5bGIMrOgXqf6XBtTwEZhnLrbMGk2GupjpS49M26uS2QEJq6ZGRitf8+7UH4g3dhBxk+35/18+20Z4Nvu661Xzo/Kod9rWPhoHBLg5hjL5s/gfuMu3p2bDswhijCqDAmNaXOCBifhba3ECM3NRmr4mOC/u+WNa+CuK0ihU9VlaU0Jx/LsgIN1PLZnrPtUTxvNXG7oGftEjDy/wu5RnLuLuQr5R5FSoa1TCHI8Q81loSkPSpRMQtC06jp63IjqIc1K81qrW7VE9nRGgeM0xlSHIQTJ1mpWfQm97RJbCvdORy8+MgceZXd5/Yr4Lb+bL2zT3lpr83XQgFwC8l8uGK1lcScaXWn6Smquy9q3uBHgedS3g4sB4aF4l5MD4WwOFXOcLhkZIV/t7M5aRlHyMoWMo5ZHbPqEMQdc3Dj82WvYHW6WV7XxEFs+a25lgVWUh42dnanEGu0ViKa39oKEVXUHa8Q42Jv3q9eO4NmnD9CKiuZA1/k9edwGHIxeXUcqW1oFcxjOw23XJmQ9F6G38Ei4u2ECFpvqlZzHBcGEjKcUyjO8LCP+NZxJfjgMgOR4hMfpQuzaamV9CErSVEckCQG6yAMeJPmQmwtkNIXnMR/uw3hm72nGnZUCxRhtvcdbfrP0DWG7Vs6tyjEGjBm4r3pc7bXWMZ1KBrB6OMLSvYh2Ltyp/CZ+I2bzUmp2+bFQmvEVXfPdetz8X9YEn9/GbjgGiof4ZMrCOeDX06DRu9CKq7HHYP6mnhjWPGXEXR7h887v49LjM0A4NpPIcRNjta1l89spA3gVa9ffylY5sqrQM8+4YR0IsxvaztYUcZQoFXaMwffFn1I1ef9NJHYTsbX8ZDBTGOW+gcg7hIU/a+7Fa4lBtkxi1zRHnzzMeLyHOS5Lv4gmVg6Z3TJnHsaGS/oT4/QZJIsAtvaleCNPKMeFByMFCRHMk6rzqe+x47XH34UP6qVg+af8eioCkM/H20VddidIKKkkTmVZbc/FfkdnzXpksKh+foFvQVFSmXLj7zQMPW+lNt/B4eq81mJfV2uWRBI6+NUjmRTX028hPBOvpx3EMgyENXjrN01yCBVWcVH1lLW+W6laLdVqIilsgPur+86aIvlIqGCxSHw4AfeRyyiF1Qh9C+7v8NjC9hGSJfIgQjQ790CK2Iro7ukNHC0OWbkHtkurZvyclWd8r7DaKqeJFaSik6MibuJM2mW7Vr4SCOUSkfLhFFUGZG8QU9L4h3FPlKp8o5eo2sLY7ybNIgs5FSyShv2v2OWD2GkatGwcqPD1yJO0WZ+Pgp9iaVH+AfA08B3S0R0CfQAJE+onYiH/glpEyxLKanCwnmkCqrebkdHBfCWNsoNlIUr7D11puu4DWaJR0wmLUgjwCKy6by5ZqyGR5hXzk4WdnouhwJrFwJciSdDyT3osi0XDl0oYXb20aFvMRdBpn6W+7e2DOe+xA4S8LPM+3vSmO3u/i0beojzj27g3tvnJdEyxMpyCKPJskwqXurt9J1POf1JLQ/nOQOqfM00fZinjIQlIl2+nOppzcWav5yzS3TLi+AGdNYs0bxbRbC5pzXNGssLwi4d+Hdey4nxtduZDu6rRQbmzlurp40u6/MO5bolja4krA=="
},
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "5680e704-01d8-4a1a-bf6f-79e6794bc894",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"key": "MIIDrzCCApegAwIBAgIUdVKPcuuIZEmXiyWvKNRE3j9aj/UwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lERjEOMAwGA1UEBwwFUGFyaXMxDjAMBgNVBAoMBU9sZmVvMRcwFQYDVQQLDA5SJkQgRGVwYXJ0bWVudDERMA8GA1UEAwwIdGVzdC5jb20wHhcNMjAwNzIwMDY1MjE0WhcNMjAwODA0MDY1MjE0WjBnMQswCQYDVQQGEwJGUjEMMAoGA1UECAwDSURGMQ4wDAYDVQQHDAVQYXJpczEOMAwGA1UECgwFT2xmZW8xFzAVBgNVBAsMDlImRCBEZXBhcnRtZW50MREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkCMYK54Z/LiccB6U5szncQJFWhfc4d3jRqeySpngGJ56UR8Jxn5uwwyfx09eanauwNY4yCR82YvMAnfh7z6+cY2B6MiqfAQ9ghETCEy+TzXJIpHaMO95rTZGJJhiLcJsjmWCQjko26Bfr0kuMykks2vIHcANxpkufNqQ7ZHHAtaXZHtnjQY0DHJGz39BVjElqzkUBs+rmRDIojpDM1aclU2BTWLyYysVg43S6Loa20jETj9Z5INwDPK9ah7dVJZQ2zhyJ+KiShdGw+FjTF3sAXcoGRHZWqZalGrQx6UmegJxRGEZwAYat60jM9FTPs69ETjkkeQoxZG6gEZNbsFosCAwEAAaNTMFEwHQYDVR0OBBYEFDC9PDYIGVTgNEWTwRsBtRLCYL43MB8GA1UdIwQYMBaAFDC9PDYIGVTgNEWTwRsBtRLCYL43MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH6yOYj1J22g7+TtgUuY/OaKzOCz4aJ4BE46FCSd5YaMQjk1RpnnKCnmz/QVo7sM2AaHPJpg3H4L77ZeeknsqWvTTmIwXDvyVpxBEp96qyamzTcxYOzrx8hRHrHzxkCvqj6I6EEAwnsYkJv62qhPDQ/uVE/oNXloqQQ7IE7Xvb7ZUtO+fkzClby5dimk/UbMgvkvOnHFcDrtc+xndwINc5PB0DF0oMKozImiTXfuPij/7Q6WKCFUeURKgnSX6WM/OuLQhBCljnaABpSYl6VFZMQbpH2im8s2iQTNEfwvcbHx/jCeVxaslBje6/tqIhrPK/GIt8io9Rr69a6qzDA6nuM="
}
],
"passwordCredentials": [
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "e4ba4cbd-8bfc-4c3a-a6a7-b693c52dc807",
"endDateTime": "2020-07-24T08:52:14.869829",
"startDateTime": "2020-07-16T08:52:14.869854",
"secretText": "vypgoyylxxortmcc"
}
],
"preferredTokenSigningKeyThumbprint": "63AA740E6D5E070CEC6BB3F5C4839BAAC2D48FA0",
"preferredSingleSignOnMode": "saml",
"notificationEmailAddresses": [
"fake@test.com"
]
}
完成的测试:
- 当我只输入 public 键时它起作用了。
- 当我想在没有密码的情况下放置私钥时,它不起作用,我收到以下消息:
您的一个凭据中 属性 'usage' 的值无效。可接受的值为签名、验证。
- 当我在 Azure 中手动上传从证书生成的 pfx 和私钥时,它有效。
生成数据的代码 (WIP)
import json
import os
import subprocess
import uuid
from datetime import datetime, timedelta
from typing import Optional
def random_string(string_length: int = 8) -> str:
letters = string.ascii_lowercase
return "".join(random.choice(letters) for i in range(string_length))
def gen_cert_and_private_key(
dirname: str, encrypted: bool, password: Optional[str] = None
):
if encrypted:
if not password:
password = random_string(16)
password_option = f"-passout pass:{password}"
else:
password_option = "-nodes"
certificate = dirname + "/certificate.crt"
private_key = dirname + "/privateKey.key"
command = (
f"openssl req {password_option} -x509 -sha256 -days 15 -newkey rsa:2048 -keyout {private_key} "
f"-out {certificate} -subj '/C=EN/ST=IDF/L=London/O=Enterprise/OU=R&D Department/CN=test.com'"
)
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0 and encrypted:
return certificate, private_key, password
elif exitcode == 0:
return certificate, private_key
def get_base64_thumbprint(filename: str):
command = f"openssl x509 -outform der -in {filename} | openssl dgst -binary -sha1 | openssl base64"
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0:
return output
def get_thumbprint(filename):
command = (
f"openssl x509 -fingerprint -noout -in {filename} | tr -d : | cut -d '=' -f2-"
)
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0:
return output
def get_base64_inline(filename):
with open(filename, "r") as f:
content = f.read()
return (
content.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "")
.replace("-----END ENCRYPTED PRIVATE KEY-----", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("-----BEGIN RSA PRIVATE KEY-----", "")
.replace("-----END RSA PRIVATE KEY-----", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.replace("\r", "")
)
def get_password(password: str, custom_key_identifier: str, key_id: str):
return {
"customKeyIdentifier": custom_key_identifier,
"keyId": key_id,
"endDateTime": (datetime.now() + timedelta(days=4)).isoformat(),
"startDateTime": (datetime.now() - timedelta(days=4)).isoformat(),
"secretText": password,
}
def gen_key_credentials_from_crt(filename):
return {
"customKeyIdentifier": get_base64_thumbprint(filename),
"keyId": str(uuid.uuid4()),
"type": "AsymmetricX509Cert",
"usage": "Verify",
"key": get_base64_inline(filename),
}
def gen_key_credentials_from_private_key(filename: str, custom_key_identifier: str):
# X509CertAndPassword
return {
"customKeyIdentifier": custom_key_identifier,
"keyId": str(uuid.uuid4()),
"type": "AsymmetricX509Cert",
"usage": "Sign",
"key": get_base64_inline(filename),
}
def gen_all(path: Optional[str] = None):
dir_path = os.path.dirname(os.path.realpath(__file__))
if not path:
path = ""
certificate_filename, private_key_filename, password = gen_cert_and_private_key(
dir_path + path, True
)
public_key = gen_key_credentials_from_crt(certificate_filename)
private_key = gen_key_credentials_from_private_key(
private_key_filename, public_key["customKeyIdentifier"]
)
password_cred = get_password(
password, public_key["customKeyIdentifier"], private_key["keyId"]
)
thumbprint = get_thumbprint(certificate_filename)
return {
"keyCredentials": [private_key, public_key],
"passwordCredentials": [password_cred],
"preferredTokenSigningKeyThumbprint": thumbprint,
}
if __name__ == "__main__":
data = {
"preferredSingleSignOnMode": "saml",
"notificationEmailAddresses": ["fake@test.com"],
}
cred = gen_all()
z = {**cred, **data}
print(json.dumps(z, indent=2))
您发布的数据
“用法”:“签名”,
“键”:“MIIFHDBOBgkqhkiG9w0BBQ0wQTApB……”
是 PKCS5。 API 期望这是 PKCS12(或 PFX)数据库 base64 编码。
获取“用法”的密钥:“签名,我的解决方案:
我将 pem 转换为 pfx :
def convert_pem_to_pfx(public_key: str, private_key: str, password: str) -> str:
dir_path = os.path.dirname(os.path.realpath(__file__))
command = f"openssl pkcs12 -export -out {dir_path}/data/certificate.pfx -inkey {private_key} -in {public_key} -passout pass:{password} -passin pass:{password}"
exitcode, output = subprocess.getstatusoutput(command)
if exitcode != 0:
assert False
return f"{dir_path}/data/certificate.pfx"
我从 pfx 文件中提取 base64 :
def get_base64_from_file(filename) -> str:
with open(filename, "rb") as f:
content = f.read()
return base64.b64encode(content).decode('ascii')
当我想按照教程进行操作时: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/application-saml-sso-configure-api#add-a-custom-signing-key.
我一直在更新服务以添加我的私钥和证书。 我不想点击 azure 界面,我想通过 Graph API 做所有事情(它用于自动测试)。我不使用 Windows,因此我的脚本不使用 Powershell,我在 Linux.
上使用 Python 和 OpenSSL'Request_BadRequest','Invalid certificate: Key value is invalid certificate',状态:400
发送的数据:
"keyCredentials": [
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "e4ba4cbd-8bfc-4c3a-a6a7-b693c52dc807",
"type": "AsymmetricX509Cert",
"usage": "Sign",
"key": "MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIlPjaWgWswX4CAggAMAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEPFi6NFq0hMBIIEyGzmtT3UF9WDM5bGIMrOgXqf6XBtTwEZhnLrbMGk2GupjpS49M26uS2QEJq6ZGRitf8+7UH4g3dhBxk+35/18+20Z4Nvu661Xzo/Kod9rWPhoHBLg5hjL5s/gfuMu3p2bDswhijCqDAmNaXOCBifhba3ECM3NRmr4mOC/u+WNa+CuK0ihU9VlaU0Jx/LsgIN1PLZnrPtUTxvNXG7oGftEjDy/wu5RnLuLuQr5R5FSoa1TCHI8Q81loSkPSpRMQtC06jp63IjqIc1K81qrW7VE9nRGgeM0xlSHIQTJ1mpWfQm97RJbCvdORy8+MgceZXd5/Yr4Lb+bL2zT3lpr83XQgFwC8l8uGK1lcScaXWn6Smquy9q3uBHgedS3g4sB4aF4l5MD4WwOFXOcLhkZIV/t7M5aRlHyMoWMo5ZHbPqEMQdc3Dj82WvYHW6WV7XxEFs+a25lgVWUh42dnanEGu0ViKa39oKEVXUHa8Q42Jv3q9eO4NmnD9CKiuZA1/k9edwGHIxeXUcqW1oFcxjOw23XJmQ9F6G38Ei4u2ECFpvqlZzHBcGEjKcUyjO8LCP+NZxJfjgMgOR4hMfpQuzaamV9CErSVEckCQG6yAMeJPmQmwtkNIXnMR/uw3hm72nGnZUCxRhtvcdbfrP0DWG7Vs6tyjEGjBm4r3pc7bXWMZ1KBrB6OMLSvYh2Ltyp/CZ+I2bzUmp2+bFQmvEVXfPdetz8X9YEn9/GbjgGiof4ZMrCOeDX06DRu9CKq7HHYP6mnhjWPGXEXR7h887v49LjM0A4NpPIcRNjta1l89spA3gVa9ffylY5sqrQM8+4YR0IsxvaztYUcZQoFXaMwffFn1I1ef9NJHYTsbX8ZDBTGOW+gcg7hIU/a+7Fa4lBtkxi1zRHnzzMeLyHOS5Lv4gmVg6Z3TJnHsaGS/oT4/QZJIsAtvaleCNPKMeFByMFCRHMk6rzqe+x47XH34UP6qVg+af8eioCkM/H20VddidIKKkkTmVZbc/FfkdnzXpksKh+foFvQVFSmXLj7zQMPW+lNt/B4eq81mJfV2uWRBI6+NUjmRTX028hPBOvpx3EMgyENXjrN01yCBVWcVH1lLW+W6laLdVqIilsgPur+86aIvlIqGCxSHw4AfeRyyiF1Qh9C+7v8NjC9hGSJfIgQjQ790CK2Iro7ukNHC0OWbkHtkurZvyclWd8r7DaKqeJFaSik6MibuJM2mW7Vr4SCOUSkfLhFFUGZG8QU9L4h3FPlKp8o5eo2sLY7ybNIgs5FSyShv2v2OWD2GkatGwcqPD1yJO0WZ+Pgp9iaVH+AfA08B3S0R0CfQAJE+onYiH/glpEyxLKanCwnmkCqrebkdHBfCWNsoNlIUr7D11puu4DWaJR0wmLUgjwCKy6by5ZqyGR5hXzk4WdnouhwJrFwJciSdDyT3osi0XDl0oYXb20aFvMRdBpn6W+7e2DOe+xA4S8LPM+3vSmO3u/i0beojzj27g3tvnJdEyxMpyCKPJskwqXurt9J1POf1JLQ/nOQOqfM00fZinjIQlIl2+nOppzcWav5yzS3TLi+AGdNYs0bxbRbC5pzXNGssLwi4d+Hdey4nxtduZDu6rRQbmzlurp40u6/MO5bolja4krA=="
},
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "5680e704-01d8-4a1a-bf6f-79e6794bc894",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"key": "MIIDrzCCApegAwIBAgIUdVKPcuuIZEmXiyWvKNRE3j9aj/UwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lERjEOMAwGA1UEBwwFUGFyaXMxDjAMBgNVBAoMBU9sZmVvMRcwFQYDVQQLDA5SJkQgRGVwYXJ0bWVudDERMA8GA1UEAwwIdGVzdC5jb20wHhcNMjAwNzIwMDY1MjE0WhcNMjAwODA0MDY1MjE0WjBnMQswCQYDVQQGEwJGUjEMMAoGA1UECAwDSURGMQ4wDAYDVQQHDAVQYXJpczEOMAwGA1UECgwFT2xmZW8xFzAVBgNVBAsMDlImRCBEZXBhcnRtZW50MREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkCMYK54Z/LiccB6U5szncQJFWhfc4d3jRqeySpngGJ56UR8Jxn5uwwyfx09eanauwNY4yCR82YvMAnfh7z6+cY2B6MiqfAQ9ghETCEy+TzXJIpHaMO95rTZGJJhiLcJsjmWCQjko26Bfr0kuMykks2vIHcANxpkufNqQ7ZHHAtaXZHtnjQY0DHJGz39BVjElqzkUBs+rmRDIojpDM1aclU2BTWLyYysVg43S6Loa20jETj9Z5INwDPK9ah7dVJZQ2zhyJ+KiShdGw+FjTF3sAXcoGRHZWqZalGrQx6UmegJxRGEZwAYat60jM9FTPs69ETjkkeQoxZG6gEZNbsFosCAwEAAaNTMFEwHQYDVR0OBBYEFDC9PDYIGVTgNEWTwRsBtRLCYL43MB8GA1UdIwQYMBaAFDC9PDYIGVTgNEWTwRsBtRLCYL43MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH6yOYj1J22g7+TtgUuY/OaKzOCz4aJ4BE46FCSd5YaMQjk1RpnnKCnmz/QVo7sM2AaHPJpg3H4L77ZeeknsqWvTTmIwXDvyVpxBEp96qyamzTcxYOzrx8hRHrHzxkCvqj6I6EEAwnsYkJv62qhPDQ/uVE/oNXloqQQ7IE7Xvb7ZUtO+fkzClby5dimk/UbMgvkvOnHFcDrtc+xndwINc5PB0DF0oMKozImiTXfuPij/7Q6WKCFUeURKgnSX6WM/OuLQhBCljnaABpSYl6VFZMQbpH2im8s2iQTNEfwvcbHx/jCeVxaslBje6/tqIhrPK/GIt8io9Rr69a6qzDA6nuM="
}
],
"passwordCredentials": [
{
"customKeyIdentifier": "Y6p0Dm1eBwzsa7P1xIObqsLUj6A=",
"keyId": "e4ba4cbd-8bfc-4c3a-a6a7-b693c52dc807",
"endDateTime": "2020-07-24T08:52:14.869829",
"startDateTime": "2020-07-16T08:52:14.869854",
"secretText": "vypgoyylxxortmcc"
}
],
"preferredTokenSigningKeyThumbprint": "63AA740E6D5E070CEC6BB3F5C4839BAAC2D48FA0",
"preferredSingleSignOnMode": "saml",
"notificationEmailAddresses": [
"fake@test.com"
]
}
完成的测试:
- 当我只输入 public 键时它起作用了。
- 当我想在没有密码的情况下放置私钥时,它不起作用,我收到以下消息: 您的一个凭据中 属性 'usage' 的值无效。可接受的值为签名、验证。
- 当我在 Azure 中手动上传从证书生成的 pfx 和私钥时,它有效。
生成数据的代码 (WIP)
import json
import os
import subprocess
import uuid
from datetime import datetime, timedelta
from typing import Optional
def random_string(string_length: int = 8) -> str:
letters = string.ascii_lowercase
return "".join(random.choice(letters) for i in range(string_length))
def gen_cert_and_private_key(
dirname: str, encrypted: bool, password: Optional[str] = None
):
if encrypted:
if not password:
password = random_string(16)
password_option = f"-passout pass:{password}"
else:
password_option = "-nodes"
certificate = dirname + "/certificate.crt"
private_key = dirname + "/privateKey.key"
command = (
f"openssl req {password_option} -x509 -sha256 -days 15 -newkey rsa:2048 -keyout {private_key} "
f"-out {certificate} -subj '/C=EN/ST=IDF/L=London/O=Enterprise/OU=R&D Department/CN=test.com'"
)
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0 and encrypted:
return certificate, private_key, password
elif exitcode == 0:
return certificate, private_key
def get_base64_thumbprint(filename: str):
command = f"openssl x509 -outform der -in {filename} | openssl dgst -binary -sha1 | openssl base64"
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0:
return output
def get_thumbprint(filename):
command = (
f"openssl x509 -fingerprint -noout -in {filename} | tr -d : | cut -d '=' -f2-"
)
exitcode, output = subprocess.getstatusoutput(command)
if exitcode == 0:
return output
def get_base64_inline(filename):
with open(filename, "r") as f:
content = f.read()
return (
content.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "")
.replace("-----END ENCRYPTED PRIVATE KEY-----", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("-----BEGIN RSA PRIVATE KEY-----", "")
.replace("-----END RSA PRIVATE KEY-----", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.replace("\r", "")
)
def get_password(password: str, custom_key_identifier: str, key_id: str):
return {
"customKeyIdentifier": custom_key_identifier,
"keyId": key_id,
"endDateTime": (datetime.now() + timedelta(days=4)).isoformat(),
"startDateTime": (datetime.now() - timedelta(days=4)).isoformat(),
"secretText": password,
}
def gen_key_credentials_from_crt(filename):
return {
"customKeyIdentifier": get_base64_thumbprint(filename),
"keyId": str(uuid.uuid4()),
"type": "AsymmetricX509Cert",
"usage": "Verify",
"key": get_base64_inline(filename),
}
def gen_key_credentials_from_private_key(filename: str, custom_key_identifier: str):
# X509CertAndPassword
return {
"customKeyIdentifier": custom_key_identifier,
"keyId": str(uuid.uuid4()),
"type": "AsymmetricX509Cert",
"usage": "Sign",
"key": get_base64_inline(filename),
}
def gen_all(path: Optional[str] = None):
dir_path = os.path.dirname(os.path.realpath(__file__))
if not path:
path = ""
certificate_filename, private_key_filename, password = gen_cert_and_private_key(
dir_path + path, True
)
public_key = gen_key_credentials_from_crt(certificate_filename)
private_key = gen_key_credentials_from_private_key(
private_key_filename, public_key["customKeyIdentifier"]
)
password_cred = get_password(
password, public_key["customKeyIdentifier"], private_key["keyId"]
)
thumbprint = get_thumbprint(certificate_filename)
return {
"keyCredentials": [private_key, public_key],
"passwordCredentials": [password_cred],
"preferredTokenSigningKeyThumbprint": thumbprint,
}
if __name__ == "__main__":
data = {
"preferredSingleSignOnMode": "saml",
"notificationEmailAddresses": ["fake@test.com"],
}
cred = gen_all()
z = {**cred, **data}
print(json.dumps(z, indent=2))
您发布的数据 “用法”:“签名”, “键”:“MIIFHDBOBgkqhkiG9w0BBQ0wQTApB……”
是 PKCS5。 API 期望这是 PKCS12(或 PFX)数据库 base64 编码。
获取“用法”的密钥:“签名,我的解决方案:
我将 pem 转换为 pfx :
def convert_pem_to_pfx(public_key: str, private_key: str, password: str) -> str:
dir_path = os.path.dirname(os.path.realpath(__file__))
command = f"openssl pkcs12 -export -out {dir_path}/data/certificate.pfx -inkey {private_key} -in {public_key} -passout pass:{password} -passin pass:{password}"
exitcode, output = subprocess.getstatusoutput(command)
if exitcode != 0:
assert False
return f"{dir_path}/data/certificate.pfx"
我从 pfx 文件中提取 base64 :
def get_base64_from_file(filename) -> str:
with open(filename, "rb") as f:
content = f.read()
return base64.b64encode(content).decode('ascii')