问题
- I'm trying to get access token within sandbox environment.
- I've a VB.NET application and referenced DocuSign.eSign.dll
- I examined docusign C# code examples and could not get them run in vb.net
This is the first approach I tried:
Dim ac As ApiClient = New ApiClient()
Dim privateKeyStream() As Byte = Convert.FromBase64String(PrivateKey)
Dim tokenInfo As OAuth.OAuthToken = ac.RequestJWTUserToken("INTEGRATION_ID", "ACCOUNT_ID", "https://account-d.docusign.com/oauth/token", privateKeyStream, 1)
This resulted in the following error:
System.Exception
HResult=0x80131500
Message=Unexpected PEM type
Source=DocuSign.eSign
StackTrace:
at DocuSign.eSign.Client.ApiClient.CreateRSAKeyFromPem(String key) ...
Second approach was using the following codes:
Dim privateKeyStream As Stream = New FileStream("D:\docusign.pem", FileMode.Open)
'Dim privateKeyStream As Stream = New MemoryStream(Encoding.UTF8.GetBytes(PK))
Using SR = New StreamReader(privateKeyStream)
If Not SR Is Nothing And SR.Peek() > 0 Then
Dim privateKeyBytes() As Byte = ReadAsBytes(privateKeyStream)
'Dim privateKeyBytes() As Byte = StreamToByteArray(privateKeyStream)
'Dim privateKeyBytes() As Byte = Convert.FromBase64String(PrivateKey)
'Dim privateKeyBytes() As Byte = Encoding.UTF8.GetBytes(PrivateKey)
Dim privateKeyS As String = Encoding.UTF8.GetString(privateKeyBytes)
Dim handler As JwtSecurityTokenHandler = New JwtSecurityTokenHandler()
handler.SetDefaultTimesOnTokenCreation = False
Dim descriptor As SecurityTokenDescriptor = New SecurityTokenDescriptor()
descriptor.Expires = DateTime.UtcNow.AddHours(1)
descriptor.IssuedAt = DateTime.UtcNow
Dim scopes As List(Of String) = New List(Of String)
scopes.Add(OAuth.Scope_SIGNATURE)
descriptor.Subject = New ClaimsIdentity()
descriptor.Subject.AddClaim(New Claim("scope", String.Join(" ", scopes)))
descriptor.Subject.AddClaim(New Claim("aud", "account-d.docusign.com"))
descriptor.Subject.AddClaim(New Claim("iss", "INTEGRATION_ID"))
descriptor.Subject.AddClaim(New Claim("sub", "ACCOUNT_ID"))
Dim RSA = CreateRSAKeyFromPem(privateKeyS)
Dim rsaKey As RsaSecurityKey = New RsaSecurityKey(RSA)
descriptor.SigningCredentials = New SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256Signature)
Dim Token = handler.CreateToken(descriptor)
Dim jwtToken As String = handler.WriteToken(Token)
Dim baseUri As String = String.Format("https://{0}/", basePath)
Dim RestClient As RestClient = New RestClient(baseUri)
RestClient.Timeout = 10000
Dim contentType As String = "application/x-www-form-urlencoded"
Dim formParams As New Dictionary(Of String, String)
formParams.Add("grant_type", OAuth.Grant_Type_JWT)
formParams.Add("assertion", jwtToken)
Dim queryParams As New Dictionary(Of String, String)
Dim headerParams As New Dictionary(Of String, String)
headerParams.Add("Content-Type", "application/x-www-form-urlencoded")
headerParams.Add("Cache-Control", "no-store")
headerParams.Add("Pragma", "no-cache")
Dim fileParams As New Dictionary(Of String, FileParameter)
Dim pathParams As New Dictionary(Of String, String)
Dim postBody As Object = Nothing
Dim request As RestRequest = PrepareRequest(basePath, Method.POST, queryParams, postBody, headerParams, formParams, fileParams, pathParams, contentType)
Dim response As IRestResponse = RestClient.Execute(request)
If (response.StatusCode >= HttpStatusCode.OK And response.StatusCode < HttpStatusCode.BadRequest) Then
Dim tokenInfo As OAuth.OAuthToken = JsonConvert.DeserializeObject(Of OAuth.OAuthToken)(response.Content)
Return tokenInfo.access_token
Else
Throw New ApiException(response.StatusCode, "Error while requesting server, received a non successful HTTP code " & response.ResponseStatus & " with response Body: " + response.Content, response.Content)
End If
Else
Throw New ApiException(400, "Private key stream not supplied or is invalid!")
End If
End Using
This resulted in the same as the first solution:
System.Exception
HResult=0x80131500
Message=Unexpected PEM type
Source=PropertyServer
StackTrace:
at PropertyServer.classDocusign.CreateRSAKeyFromPem(String key) in...
And my 3rd approach was like the following:
Dim ar1 As JObject = New JObject()
ar1.Add("typ", "JWT")
ar1.Add("alg", "RS256")
Dim header As String = Base64UrlEncoder.Encode(ar1.ToString)
Dim ar2 As JObject = New JObject()
ar2.Add("iss", "INTEGRATION_ID")
ar2.Add("sub", "ACCOUNT_ID")
ar2.Add("iat", DateDiff(DateInterval.Second, New Date(1970, 1, 1), Now))
ar2.Add("exp:", DateDiff(DateInterval.Second, New Date(1970, 1, 1), DateAdd(DateInterval.Hour, 1, Now)))
ar2.Add("aud:", "account-d.docusign.com")
ar2.Add("scope", "signature impersonation")
Dim body As String = Base64UrlEncoder.Encode(ar2.ToString)
Dim stringToSign As String = header & "." & body
Dim bytesToSign() As Byte = Encoding.UTF8.GetBytes(stringToSign)
'Dim data() As Byte = Encoding.UTF8.GetBytes(PrivateKey)
'Dim b64 As String = System.Text.Encoding.UTF8.GetString(data)
Dim keyBytes() As Byte = Convert.FromBase64String(PrivateKey)
Dim privKeyObj = Asn1Object.FromByteArray(keyBytes)
Dim privStruct = RsaPrivateKeyStructure.GetInstance(privKeyObj)
Dim sig As ISigner = SignerUtilities.GetSigner("SHA256withRSA")
sig.Init(True, New RsaKeyParameters(True, privStruct.Modulus, privStruct.PrivateExponent))
sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length)
Dim signature() As Byte = sig.GenerateSignature()
Dim sign As String = Base64UrlEncoder.Encode(signature)
Return header & "." & body & "." & sign
At my 3rt try I was able to get result however when I try to post this as assertion part of the request in Postman, it returns the following:
{
"error": "invalid_grant",
"error_description": "no_valid_keys_or_signatures"
}
I've spent hours to solve the issue but no luck, thanks in advance.
Note: These are imported
Imports System.IO
Imports System.Net
Imports System.Text
Imports DocuSign.eSign.Api
Imports DocuSign.eSign.Client
Imports DocuSign.eSign.Client.Auth
Imports DocuSign.eSign.Model
Imports Microsoft.Azure.KeyVault.Cryptography.Algorithms
Imports Newtonsoft.Json.Linq
Imports System.Security.Cryptography
Imports System
Imports System.Collections.Generic
Imports Org.BouncyCastle.Crypto
Imports Org.BouncyCastle.Crypto.Parameters
Imports Org.BouncyCastle.Security
Imports Newtonsoft.Json
Imports Org.BouncyCastle.Asn1
Imports Org.BouncyCastle.Asn1.Pkcs
Imports RestSharp
Imports System.IdentityModel.Tokens.Jwt
Imports Microsoft.IdentityModel.Tokens
Imports System.Security.Claims
Imports Org.BouncyCastle.OpenSsl
Imports System.Security.Cryptography.X509Certificates
回答1:
Sounds like your third approach was best. Especially since your claims were not correct. They should be:
ar2.Add("iss", "INTEGRATION_ID")
# sub is NOT the account id
ar2.Add("sub", "GUID_VERSION_OF_USER_ID")
# Check that your date is correct
ar2.Add("iat", DateDiff(DateInterval.Second, New Date(1970, 1, 1), Now))
# should be exp, not exp:
ar2.Add("exp", DateDiff(DateInterval.Second, New Date(1970, 1, 1),
DateAdd(DateInterval.Hour, 1, Now)))
# should be "aud", not "aud:"
ar2.Add("aud", "account-d.docusign.com")
# only need signature. impersonation is automatically implied.
ar2.Add("scope", "signature")
I was unable to find anything very useful for creating an RS256 JWT via VB.
This company offers something for a fee but I have no experience with it.
Microsoft apparently now enables the creation of RS256 JWTs but doesn't have a useful example that I could find. Docs.
Please submit an answer to your own question once you've figured it out. Thank you!!
Added
I suggest printing out the JWT that your software produces, then use a verification tool or procedure to verify that the JWT that your software produces is what you expected.
Unfortunately, many of the online verifiers just decode the claims. You need to additionally check that the signature is correct. (Use the public key from the RSA pair that DocuSign gives you.)
回答2:
Finally got it working with the help of Larry K! I'm not sure what I missed again after Larry's response yesterday, but I'm able to get access token now with the following codes: (By the way setting iat and exp to UTC or local time does not make difference)
...Addinionally PriveKey declaration like follows:
Dim PrivateKey As String = "MIIEowIBAAKCAQEAjtTe7UUP/CBI9s...BLABLABLA...JfwZ2hHqFPXA9ecbhc0".Replace(vbLf, "").Replace(vbCr, "")
Dim ar1 As JObject = New JObject()
ar1.Add("typ", "JWT")
ar1.Add("alg", "RS256")
Dim header As String = Base64UrlEncoder.Encode(ar1.ToString)
Dim ar2 As JObject = New JObject()
ar2.Add("iss", "INTEGRATION_ID")
ar2.Add("sub", "GUID_VERSION_OF_USER_ID")
ar2.Add("iat", DateDiff(DateInterval.Second, New Date(1970, 1, 1), Now().ToUniversalTime))
ar2.Add("exp", DateDiff(DateInterval.Second, New Date(1970, 1, 1), DateAdd(DateInterval.Hour, 1, Now().ToUniversalTime)))
ar2.Add("aud", "account-d.docusign.com")
ar2.Add("scope", "signature")
Dim body As String = Base64UrlEncoder.Encode(ar2.ToString)
Dim stringToSign As String = header & "." & body
Dim bytesToSign() As Byte = Encoding.UTF8.GetBytes(stringToSign)
Dim keyBytes() As Byte = Convert.FromBase64String(PrivateKey)
Dim privKeyObj = Asn1Object.FromByteArray(keyBytes)
Dim privStruct = RsaPrivateKeyStructure.GetInstance(privKeyObj)
Dim sig As ISigner = SignerUtilities.GetSigner("SHA256withRSA")
sig.Init(True, New RsaKeyParameters(True, privStruct.Modulus, privStruct.PrivateExponent))
sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length)
Dim signature() As Byte = sig.GenerateSignature()
Dim sign As String = Base64UrlEncoder.Encode(signature)
Return header & "." & body & "." & sign
Thanks a lot Larry K!
来源:https://stackoverflow.com/questions/62864320/docusign-jwt-access-token-request