java - 来自java的Rest服务调用 key 长度不是128/192/256位

标签 java rest

我想使用“GET”请求从 Java 发送休息服务调用。但我收到以下错误。我可以在 postman 中使用它,但无法发送 Java 应用程序。

Exception in thread "main" java.lang.IllegalArgumentException: Key length not 128/192/256 bits.
at org.bouncycastle.crypto.engines.AESFastEngine.generateWorkingKey(Unknown Source)
at org.bouncycastle.crypto.engines.AESFastEngine.init(Unknown Source)
at org.bouncycastle.crypto.modes.CBCBlockCipher.init(Unknown Source)
at org.bouncycastle.crypto.macs.CMac.init(Unknown Source)
at com.rest.OAuth1.generateCmac(OAuth1.java:262)
at com.rest.OAuth1.generateSignature(OAuth1.java:180)
at com.rest.OAuth1.main(OAuth1.java:61)

这是我的示例代码

包 com.rest;

// Java Libraries
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.net.ssl.HttpsURLConnection;

// Apache Commons Libraries used for the Nonce & Base64
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.codec.binary.Base64;

// Bouncy Castle Libraries used for CMAC encryption
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.KeyParameter;

/**
 * Very basic sample code that demonstrates how to make an OAuth 1.0 System-to-System
 * request to the LearningStudio API 
 */
public class OAuth1 {

  public static void main(final String[] args) throws Exception
  {
    // Setup the variables necessary to create the OAuth 1.0 signature and make the request
    String httpMethod  = "GET";
    String URI         = "example.com/one/oauth1/userManagement/v5/users";
    //String appID       = "{applicationId}";
    String consumerKey = "1234567-1234-4186-1234-1234567891011!mailid@example.com";
    String secret      = "12345678-1234-1234-1234-12345678";   
    String body        = "{var:val}"; 
    String signatureMethod = "HMAC-SHA1";
    byte[] requestBody = null;
    HttpsURLConnection request = null;
    BufferedReader in = null;
    URL url = new URL(String.format("https://api.example.com%s", URI));

    // Set the Nonce and Timestamp parameters
    String nonce = getNonce();
    String timestamp = getTimestamp();

    // Set the request body if making a POST or PUT request
    if ("POST".equals(httpMethod)  || "PUT".equals(httpMethod))
    {
      requestBody = body.getBytes("UTF-8");
    }

    // Create the OAuth parameter name/value pair
    Map<String, String> oauthParams = new LinkedHashMap<String, String>();
    oauthParams.put("oauth_consumer_key", consumerKey);
    //oauthParams.put("application_id", appID);
    oauthParams.put("oauth_signature_method", signatureMethod);
    oauthParams.put("oauth_timestamp", timestamp);
    oauthParams.put("oauth_nonce", nonce);

    // Get the OAuth 1.0 Signature
    String signature = generateSignature(httpMethod, url, oauthParams, requestBody, secret);
    System.out.println(String.format("OAuth 1.0 Signature = %s", signature));

    // Add the oauth_signature parameter to the set of OAuth Parameters
    oauthParams.put("oauth_signature", signature);    

    // Generate a string of comma delimited: keyName="URL-encoded(value)" pairs
    StringBuilder sb = new StringBuilder();
    String delimiter = "";
    for (String keyName : oauthParams.keySet()) {
      sb.append(delimiter);
      String value = oauthParams.get((String) keyName);
      sb.append(keyName).append("=\"").append(URLEncoder.encode(value, "UTF-8")).append("\"");
      delimiter=",";
    }

    String urlString = url.toString();
    // omit the queryString from the url
    int startOfQueryString = urlString.indexOf('?');
    if(startOfQueryString != -1) {
      urlString = urlString.substring(0,startOfQueryString);    
    }

    // Build the X-Authorization request header
    String xauth = String.format("OAuth realm=\"%s\",%s", urlString, sb.toString());
    System.out.println(String.format("X-Authorization request header = %s", xauth));

    try
    {
      // Setup the Request
      request = (HttpsURLConnection)url.openConnection();
      request.setRequestMethod(httpMethod);
      request.addRequestProperty("X-Authorization", xauth);

      // Set the request body if making a POST or PUT request
      if ("POST".equals(httpMethod) || "PUT".equals(httpMethod))
      {
        request.setRequestProperty("Content-Length", "" + requestBody.length);
        request.setDoOutput(true);

        OutputStream postStream = request.getOutputStream();
        postStream.write(requestBody, 0, requestBody.length);
        postStream.close();
      }

      // Send Request &amp; Get Response
      InputStreamReader reader = new InputStreamReader(request.getInputStream());
      in = new BufferedReader(reader);

      // Get the response stream
      String response = in.readLine();
      System.out.println(String.format("Successful Response: \r\n%s", response));

    } catch (IOException e )
    {
      // This exception will be raised if the serve didn't return 200 - OK
      System.out.print(e.getMessage());

    } finally
    {
      if (in != null) in.close();
      if (request != null) request.disconnect();
    }   
  }

  /**
   * Generates a random nonce
   * 
   * @return  A unique identifier for the request
   */
  private static String getNonce()
  {
    return RandomStringUtils.randomAlphanumeric(32);
  }

  /**
   * Generates an integer representing the number of seconds since the unix epoch using the
   * date/time the request is issued
   * 
   * @return  A timestamp for the request
   */
  private static String getTimestamp()
  {    
    return Long.toString((System.currentTimeMillis() / 1000));
  }

  /**
   * Generates an OAuth 1.0 signature
   * 
   * @param   httpMethod  The HTTP method of the request
   * @param   URL     The request URL
   * @param   oauthParams The associative set of signable oAuth parameters
   * @param   requestBody The serialized POST/PUT message body
   * @param   secret    Alphanumeric string used to validate the identity of the education partner (Private Key)
   * 
   * @return  A string containing the Base64-encoded signature digest
   * 
   * @throws  UnsupportedEncodingException
   */  
  private static String generateSignature(
      String httpMethod,
      URL url,
      Map<String, String> oauthParams,
      byte[] requestBody,
      String secret
  ) throws UnsupportedEncodingException
  {
    // Ensure the HTTP Method is upper-cased
    httpMethod = httpMethod.toUpperCase();

    // Construct the URL-encoded OAuth parameter portion of the signature base string
    String encodedParams = normalizeParams(httpMethod, url, oauthParams, requestBody);

    // URL-encode the relative URL
    String encodedUri = URLEncoder.encode(url.getPath(), "UTF-8");

    // Build the signature base string to be signed with the Consumer Secret
    String baseString = String.format("%s&%s&%s", httpMethod, encodedUri, encodedParams);

    return generateCmac(secret, baseString);
  }

  /**
   * Normalizes all OAuth signable parameters and url query parameters according to OAuth 1.0
   * 
   * @param   httpMethod  The upper-cased HTTP method
   * @param   URL     The request URL
   * @param   oauthParams The associative set of signable oAuth parameters
   * @param   requstBody  The serialized POST/PUT message body
   * 
   * @return  A string containing normalized and encoded oAuth parameters
   * 
   * @throws  UnsupportedEncodingException
   */
  private static String normalizeParams(
      String httpMethod,
      URL url,
      Map<String, String> oauthParams,
      byte[] requestBody
  ) throws UnsupportedEncodingException
  {

    // Sort the parameters in lexicographical order, 1st by Key then by Value
    Map<String, String> kvpParams = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
    kvpParams.putAll(oauthParams); 

    // Place any query string parameters into a key value pair using equals ("=") to mark
    // the key/value relationship and join each parameter with an ampersand ("&")
    if (url.getQuery() != null)
    {
      for(String keyValue : url.getQuery().split("&"))
      {
        String[] p = keyValue.split("=");
        kvpParams.put(p[0],p[1]);
      }

    }

    // Include the body parameter if dealing with a POST or PUT request
    if ("POST".equals(httpMethod) || "PUT".equals(httpMethod))
    {
      String body = Base64.encodeBase64String(requestBody).replaceAll("\r\n", "");
      // url encode the body 2 times now before combining other params
      body = URLEncoder.encode(body, "UTF-8");
      body = URLEncoder.encode(body, "UTF-8");
      kvpParams.put("body", body);    
    }

    // separate the key and values with a "="
    // separate the kvp with a "&"
    StringBuilder combinedParams = new StringBuilder();
    String delimiter="";
    for(String key : kvpParams.keySet()) {
      combinedParams.append(delimiter);
      combinedParams.append(key);
      combinedParams.append("=");
      combinedParams.append(kvpParams.get(key));
      delimiter="&";
    }

    // url encode the entire string again before returning
    return URLEncoder.encode(combinedParams.toString(), "UTF-8");
  }

  /**
   * Generates a Base64-encoded CMAC-AES digest
   * 
   * @param   key The secret key used to sign the data
   * @param   msg The data to be signed
   * 
   * @return  A CMAC-AES hash
   * 
   * @throws  UnsupportedEncodingException 
   */
  private static String generateCmac(String key, String msg)
      throws UnsupportedEncodingException
  {
    byte[] keyBytes = key.getBytes("UTF-8");
    byte[] data = msg.getBytes("UTF-8");

    CMac macProvider = new CMac(new AESFastEngine());
    macProvider.init(new KeyParameter(keyBytes));
    macProvider.reset();

    macProvider.update(data, 0, data.length);
    byte[] output = new byte[macProvider.getMacSize()];
    macProvider.doFinal(output, 0);

    // Convert the CMAC to a Base64 string and remove the new line the Base64 library adds
    String cmac = Base64.encodeBase64String(output).replaceAll("\r\n", "");

    return cmac;
  }
}

还有什么我遗漏的吗?

另外,如果我需要执行 POST 请求,我需要直接在 body 标记中添加 json 数据吗?

最佳答案

编辑:这真的是您的 key 吗? 也许您因未在此处发布原始 key 而将 key 更改为其他大小。如果是这样,检查 keyBytes.length 是否真的给你 16,24 或 32

我现在真的深入挖掘了...我在您的任何代码中都找不到任何错误。

您的 key 长度为 256 位:

byte[] keyBytes = "12345678-1234-1234-1234-12345678".getBytes("UTF-8");
int bits = keyBytes.length*8;
System.out.println(bits); //gives 256

所以我检查了 CMac.java,他们基本上只是使用 System.arraycopy 复制 key ,所以那里没有错误。

他们检查 https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java 中的 key

int keyLen = key.length;
if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
{
    throw new IllegalArgumentException("Key length not 128/192/256 bits.");
}

由于 key.length 等于 32,而 100000 & 111 显然是 0真的不知道没看出有什么问题。

请尝试使用只有 16 个字符的 key ,并告诉我们错误是否仍然存在。也许您还应该检查您是否确实使用了最新版本的org.bouncycaSTLe.crypto

如果这没有帮助,请尝试一次不使用字符集 UTF-8key.getBytes()

关于java - 来自java的Rest服务调用 key 长度不是128/192/256位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55903272/

相关文章:

java - 改造查询链接

java - 如何模拟 KeyguardManager

java - Android 对话框与 edittext() 不断返回 null 抛出异常

Java:public static final double 不能设置为小数?

api - REST API 中错误 HTTP 方法的返回代码?

java - 从 Struts 操作类在后台调用 RESTful 服务

java - 最佳实践 - Swing、数据库访问

java - 堆优先级队列如何记住其在数组中的索引?

java - 如何使 Jersey Rest POST 请求同步

java - 用于从数据库读取和更新 REST 服务器的多线程 Java 控制台应用程序