【问题标题】:I am unable to obtain access token using OAUTH 2.0 SAML Assertion Bearer flow in python我无法在 python 中使用 OAUTH 2.0 SAML Assertion Bearer 流获取访问令牌
【发布时间】:2019-08-17 18:50:10
【问题描述】:

我正在尝试创建一个 SAML 断言,然后将其发布到销售团队 oauth 端点在我的办公室沙盒环境中获取访问令牌作为响应。

以下代码基于此存储库中提到的代码:https://github.com/salesforceidentity/apex-saml-bearer-flow/blob/master/SAMLBearerAssertion.apex.txt 我已尝试转换 将代码写入python。

import datetime
import random
import hashlib
import binascii
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode, b64decode,urlsafe_b64decode,urlsafe_b64encode
import requests
from xml.etree import ElementTree

class SFSAMLAssertion:

def __init__(self, subject, issuer, audience, action):

    self.subject = subject
    self.issuer = issuer
    self.action = action
    self.audience = audience
    self.not_before = "2019-08-16T06:35:13.654Z"
    self.not_on_or_after = "2024-08-14T06:35:13.654Z"
    self.assertion_id = self.create_assertion_id()
    self.encoded_key = ""
    self.preCannonicalizedResponse = """<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="ASSERTION_ID" IssueInstant="NOT_BEFORE" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">ISSUER</saml:Issuer><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">SUBJECT</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="NOT_ON_OR_AFTER" Recipient="RECIPIENT"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="NOT_BEFORE" NotOnOrAfter="NOT_ON_OR_AFTER"><saml:AudienceRestriction><saml:Audience>AUDIENCE</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="NOT_BEFORE"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>"""
    self.preCannonicalizedSignedInfo = """<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI="#ASSERTION_ID"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>DIGEST</ds:DigestValue></ds:Reference></ds:SignedInfo>"""
    self.signatureBlock = """<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">SIGNED_INFO<ds:SignatureValue>SIGNATURE_VALUE</ds:SignatureValue></ds:Signature><saml:Subject>"""

def create_assertion_id(self):
    num = random.randint(1, 999999)
    hash = hashlib.sha256()
    hash.update(bytes(num))
    digest = hash.digest()

    return binascii.hexlify(digest).decode('UTF-8')

def sign_signedinfo(self, data):
    f = open('../etc/privateKey.pem', 'rb')

    key = b64decode(self.encoded_key)
    rsakey = RSA.importKey(f.read())
    signer = PKCS1_v1_5.new(rsakey)
    digest = SHA256.new()
    digest.update(b64encode(data))
    sign = signer.sign(digest)
    return b64encode(sign)

def create_assertion(self):

    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('ASSERTION_ID',self.assertion_id)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('ISSUER', self.issuer)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('AUDIENCE', self.audience)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('RECIPIENT', self.action)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('SUBJECT', self.subject)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('NOT_BEFORE', self.not_before)
    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('NOT_ON_OR_AFTER', self.not_on_or_after)

    # Prepare the digest
    m = hashlib.sha256()
    m.update(bytes(self.preCannonicalizedResponse, encoding='UTF-8'))
    digest = b64encode(m.digest())

    self.preCannonicalizedSignedInfo = self.preCannonicalizedSignedInfo.replace('ASSERTION_ID',self.assertion_id)
    self.preCannonicalizedSignedInfo = self.preCannonicalizedSignedInfo.replace('DIGEST',digest.decode('UTF-8'))

    # Prepare the signedinfo
    input = bytes(self.preCannonicalizedSignedInfo,encoding='UTF-8')

    signature = self.sign_signedinfo(input)
    signature = b64encode(signature)

    # Prepare the signature block
    self.signatureBlock = self.signatureBlock.replace('SIGNED_INFO',self.preCannonicalizedSignedInfo)
    self.signatureBlock = self.signatureBlock.replace('SIGNATURE_VALUE',signature.decode('UTF-8'))

    self.preCannonicalizedResponse = self.preCannonicalizedResponse.replace('<saml:Subject>',self.signatureBlock)

    return self.preCannonicalizedResponse

def get_base64urlencode_string(self):

    data = self.create_assertion()
    data = '<?xml version="1.0" encoding = "UTF-8"?>' + data
    print("Assertion: "+data)
    tree = ElementTree.ElementTree(ElementTree.fromstring(data))
    root = tree.getroot()
    output = urlsafe_b64encode(bytes(ElementTree.tostring(root, encoding='UTF-8',method = 'xml').decode(encoding='UTF-8'),encoding='UTF-8'))
    return output

def postSAML(self):

    url = "https://<sandbox_domain>.my.salesforce.com/services/oauth2/token"
    assertion = self.get_base64urlencode_string()
    print(assertion.decode('UTF-8'))
    headers = {
        "Content-Type" : "application/x-www-form-urlencoded"
    }

    params = {
        "grant-type" : "urn:ietf:params:oauth:grant-type:saml2-bearer",
        "assertion" : assertion.decode('UTF-8')
    }

    r = requests.post(url,params=params,headers=headers)
    print(r.status_code)
    print(r.content)
    print(r.headers)
    print(r.url)

目前,我在使用我的 python 代码时收到“无效断言”错误。我的代码有什么问题?

【问题讨论】:

    标签: python-3.x oauth-2.0 salesforce saml-2.0


    【解决方案1】:

    您从 SAMLBearerAssertion.apex 复制的代码使用 SHA-1 作为消息摘要,使用 RSA-SHA1 作为签名,而您的代码使用 SHA-256/RSA-SHA256。将相关的 XML 片段更改为:

    <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
    
    <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
    

    有关 Algorithm 的其他值,请参阅 IANA 分配 cheatsheet。

    【讨论】:

    • 我尝试进行上述更改,但仍然收到相同的“无效断言”错误。我还尝试构建在 help.salesforce.com/… 中提到的确切 SAML 断言,但仍然遇到相同的错误。
    • 您的断言被拒绝的原因可能有很多,是的。我们建议切换到JWT Bearer 流。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 2012-12-10
    相关资源
    最近更新 更多