Java 有没有办法调查一件事;无效断言";在Salesforce中尝试OAuth 2.0 JWT承载令牌流时收到

Java 有没有办法调查一件事;无效断言";在Salesforce中尝试OAuth 2.0 JWT承载令牌流时收到,java,oauth-2.0,salesforce,jwt,Java,Oauth 2.0,Salesforce,Jwt,我已经按照Salesforce的要求编写了一个java客户机,但是响应是“无效断言”。我上传到Salesforce组织的证书是自签名的,我发送的声明包括客户端id(OAuth连接的应用程序的消费者密钥)、用户的电子邮件地址/用户名(这也是证书中声明的电子邮件,用户已通过用户/密码OAuth流成功使用Salesforce REST API),过期时间和访问群体设置为(aud)。我可以看到“数字签名”已在Salesforce中正确加载,并且我的代码对其进行了正确签名。Salesforce中我的Oau

我已经按照Salesforce的要求编写了一个java客户机,但是响应是“无效断言”。我上传到Salesforce组织的证书是自签名的,我发送的声明包括客户端id(OAuth连接的应用程序的消费者密钥)、用户的电子邮件地址/用户名(这也是证书中声明的电子邮件,用户已通过用户/密码OAuth流成功使用Salesforce REST API),过期时间和访问群体设置为(aud)。我可以看到“数字签名”已在Salesforce中正确加载,并且我的代码对其进行了正确签名。Salesforce中我的Oauth作用域已设置为:

访问和管理您的数据(api)
通过Web(Web)访问您的数据
随时代表您执行请求(刷新\u令牌、脱机\u访问)

当我将JWT直接发布到/services/oauth2/token表单时,得到了相同的响应,如下所示:

授权类型=urn%3Aietf%3Aparams%3Aoauth%3Agrant类型%3Ajwt承载和断言=k5ARCVFd1VgfOuM

我包括标题:

“内容类型”,“应用程序/x-www-form-urlencoded”

JWT使用以下方法生成:

import com.payliquid.model.config.OAuthCredentials;
import org.apache.commons.codec.binary.Base64;
import org.joda.time.DateTime;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.CertificateException;

public class JWTGenerator {

  public static final String HEADER = "{\"alg\":\"RS256\"}";
  public static final String CLAIM_TEMPLATE = "'{'\"iss\": \"%s\", \"prn\": \"%s\", \"aud\": \"%s\", \"exp\": \"%s\"'}'";
  public static final String ALGORITHM = "SHA256withRSA";

  private String audience;
  private KeyStore keystore;

  public JWTGenerator(String authServerURL, KeyStore keystore) {
    this.audience = authServerURL;
    this.keystore = keystore;
  }

  public String generateToken(OAuthCredentials oAuthCredentials, String clientId) {

    final String certPassword = oAuthCredentials.getPassword();
    final String principal = oAuthCredentials.getUserName();
    final String certAlias = oAuthCredentials.getSecurityToken().get();

    try {
        StringBuilder jwTokenBuilder = new StringBuilder();

        appendEncodedHeader(jwTokenBuilder);

        appendSeparator(jwTokenBuilder);

        final String joinedClaim = String.format(CLAIM_TEMPLATE, getClaimElements(clientId, principal));

        appendEncodedClaim(jwTokenBuilder, joinedClaim);

        PrivateKey privateKey = getPrivateKey(certPassword, certAlias);

        String signedPayload = signPayload(jwTokenBuilder, privateKey);

        appendSeparator(jwTokenBuilder);

        jwTokenBuilder.append(signedPayload);

        return jwTokenBuilder.toString();

    } catch (Exception e) {
        throw new OAuthRuntimeSystemException("Could not generate JWT Token", e);
    }
  }

  private PrivateKey getPrivateKey(String certPassword, String certAlias) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
    PrivateKey privateKey = loadPrivateKey(certAlias, certPassword);

    if (privateKey == null) {
        throw new OAuthRuntimeSystemException("Could not generate JWT Token as no private key available with " + certAlias);
    }
    return privateKey;
  }

  private String[] getClaimElements(String iss, String prn) {
    return new String[]{
            iss,
            prn,
            audience,
            getExpirationTime()};
 }

  private String getExpirationTime() {
    return new DateTime().plusMinutes(1).toDate().getTime() + "";
 }

  private void appendSeparator(StringBuilder token) {
    token.append(".");
 }

  private PrivateKey loadPrivateKey(String certAlias, String privateKeyPassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
    return (PrivateKey) keystore.getKey(certAlias, privateKeyPassword.toCharArray());
}

 private String signPayload(StringBuilder token, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
    Signature signature = Signature.getInstance(ALGORITHM);
    signature.initSign(privateKey);
    signature.update(getBytesInCharSet(token.toString()));
    return encode(signature.sign());
}

  private void appendEncodedClaim(StringBuilder token, String claim) throws UnsupportedEncodingException {
    token.append(encode(claim));
}

  private void appendEncodedHeader(StringBuilder token) throws UnsupportedEncodingException {
    token.append(encode(HEADER));
}

  private String encode(String payload) throws UnsupportedEncodingException {
    final byte[] bytes = getBytesInCharSet(payload);
    return encode(bytes);
}

  private byte[] getBytesInCharSet(String s) throws UnsupportedEncodingException {
    return s.getBytes("UTF-8");
  }

  private String encode(byte[] bytes) {
    return Base64.encodeBase64URLSafeString(bytes);
  }
}

有没有办法获得更多关于断言失败原因的信息,或者我需要对JWT或Salesforce连接的应用程序或用户做更多的工作才能使其正常工作?

您可以在此处验证JWT:;base64url编码的断言以“k5ARCVFd1VgfOuM…”开头似乎很奇怪,它宁愿以“eyJ…”开头感谢您的验证页面。我的未签名JWT断言确实以eyJ开始,但当我使用私钥对其进行签名时,它将成为X12-CSN_……(我已更改了证书!)。如果我将我的预签名断言与X509证书一起放在页面上,它会正确解析我的头和有效负载,但不会解析sig值。我将头和有效负载合并并对其进行签名,而不是附加“SigValue”。这显然是我的错误,但SigValue是什么?我想我明白了:SigValue是签名头+“+”有效负载。“?签名值通过消息值
base64urlencode(header)
+“+
base64urlencode(payload)
计算,然后签名被base64urlencode编码并连接到消息值,因此:
JWT=base64urlencode(header)+”。base64urlencode(payload)+“+base64urlencode(签名)
感谢您的帮助,我的代码现在运行正常。