Java 如何使用HMACSHA1和密钥创建签名以连接到Kayako API

Java 如何使用HMACSHA1和密钥创建签名以连接到Kayako API,java,encryption,Java,Encryption,我正在尝试使用ApacheCommonsHTTP客户端连接到第三方应用程序API。我尝试连接的API是 API要求我传递API密钥和签名以及用于创建签名的salt 根据API文档,以下是创建签名的步骤 import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import jav

我正在尝试使用ApacheCommonsHTTP客户端连接到第三方应用程序API。我尝试连接的API是

API要求我传递API密钥和签名以及用于创建签名的salt

根据API文档,以下是创建签名的步骤

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Base64Encoder;

public class GenSign2 {
    public static void main(String[] args) throws GeneralSecurityException,
            IOException {
        String secretKey = "M2Y2YjkxZDEtYmNlOC1mYmI0LTkxZTgtOTNiY2RiMDhmN2E2YjExNGUwYjktNGJkYy1jZTM0LWQ1MWYtZGIwYWRlZTE0NGNh";
        String salt = "0123456789";

        String generateHmacSHA256Signature = generateHmacSHA256Signature(salt,
                secretKey);
        System.out.println("Signature: " + generateHmacSHA256Signature);

        String urlEncodedSign = URLEncoder.encode(generateHmacSHA256Signature,
                "UTF-8");

        System.out.println("Url encoded value: " + urlEncodedSign);
    }

    public static String generateHmacSHA256Signature(String data, String key)
            throws GeneralSecurityException, IOException {
        byte[] hmacData = null;

        try {
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"),
                    "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(secretKey);
            hmacData = mac.doFinal(data.getBytes("UTF-8"));
            ByteArrayOutputStream bout = new ByteArrayOutputStream();

            new Base64Encoder().encode(hmacData, 0, hmacData.length, bout);
            return bout.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new GeneralSecurityException(e);
        }
    }
}
  • 生成一个随机字符串来创建一个salt(在PHP中,您可以使用mt_和()来完成此操作)
  • 通过使用SHA256对salt进行散列并将密钥作为密钥来生成签名(在PHP中,您可以使用hash_hmac()来完成此操作)
  • base64对签名进行编码(在PHP中,可以使用base64_encode()完成此操作)
  • URL编码输出(在PHP中,您可以使用urlencode()完成此操作)
  • 已更新

    根据我得到的回复,我修改了一些代码,并用Kayako创建了一个演示帐户来测试API

    我使用下面的类来生成签名

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.security.GeneralSecurityException;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.bouncycastle.util.encoders.Base64Encoder;
    
    public class GenSign2 {
        public static void main(String[] args) throws GeneralSecurityException,
                IOException {
            String secretKey = "M2Y2YjkxZDEtYmNlOC1mYmI0LTkxZTgtOTNiY2RiMDhmN2E2YjExNGUwYjktNGJkYy1jZTM0LWQ1MWYtZGIwYWRlZTE0NGNh";
            String salt = "0123456789";
    
            String generateHmacSHA256Signature = generateHmacSHA256Signature(salt,
                    secretKey);
            System.out.println("Signature: " + generateHmacSHA256Signature);
    
            String urlEncodedSign = URLEncoder.encode(generateHmacSHA256Signature,
                    "UTF-8");
    
            System.out.println("Url encoded value: " + urlEncodedSign);
        }
    
        public static String generateHmacSHA256Signature(String data, String key)
                throws GeneralSecurityException, IOException {
            byte[] hmacData = null;
    
            try {
                SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"),
                        "HmacSHA256");
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(secretKey);
                hmacData = mac.doFinal(data.getBytes("UTF-8"));
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
    
                new Base64Encoder().encode(hmacData, 0, hmacData.length, bout);
                return bout.toString("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new GeneralSecurityException(e);
            }
        }
    }
    
    测试api如下所示

    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    
    public class TestApi {
    
        public static void main(String[] args) throws ClientProtocolException,
                IOException, URISyntaxException {
            HttpClient client = new DefaultHttpClient();
    
            List<NameValuePair> qparams = new ArrayList<NameValuePair>();
            qparams.add(new BasicNameValuePair("apikey",
                    "f165dc40-ce3f-6864-7d5e-27a7188b2e62"));
            qparams.add(new BasicNameValuePair("salt", "0123456789"));
            qparams.add(new BasicNameValuePair("signature", "mbrhpXkP0LzNMNDygHAorqMx%2FDGovl%2FauMTOMB6RNMA%3D"));
    
            HttpPost httpget = new HttpPost(
                    "http://aruntest.kayako.com/api/index.php?e=/Core/Test");
    
            HttpResponse response = client.execute(httpget);
            System.out.println(response.getProtocolVersion());
            System.out.println(response.getStatusLine().getStatusCode());
            System.out.println(response.getStatusLine().getReasonPhrase());
            System.out.println(response.getStatusLine().toString());
        }
    
    }
    
    import java.io.IOException;
    导入java.net.URISyntaxException;
    导入java.util.ArrayList;
    导入java.util.List;
    导入org.apache.http.HttpResponse;
    导入org.apache.http.NameValuePair;
    导入org.apache.http.client.ClientProtocolException;
    导入org.apache.http.client.HttpClient;
    导入org.apache.http.client.methods.HttpPost;
    导入org.apache.http.impl.client.DefaultHttpClient;
    导入org.apache.http.message.BasicNameValuePair;
    公共类测试{
    公共静态void main(字符串[]args)抛出ClientProtocolException,
    IOException,URISyntaxException{
    HttpClient=new DefaultHttpClient();
    List qparams=new ArrayList();
    添加(新的BasicNameValuePair(“apikey”),
    “f165dc40-ce3f-6864-7d5e-27a7188b2e62”);
    添加(新的BasicNameValuePair(“salt”,“0123456789”);
    qparams.add(新的BasicNameValuePair(“签名”、“mbrhpXkP0LzNMNDygHAorqMx%2FDGovl%2FauMTOMB6RNMA%3D”);
    HttpPost httpget=新的HttpPost(
    "http://aruntest.kayako.com/api/index.php?e=/Core/Test");
    HttpResponse response=client.execute(httpget);
    System.out.println(response.getProtocolVersion());
    System.out.println(response.getStatusLine().getStatusCode());
    System.out.println(response.getStatusLine().getReasonPhrase());
    System.out.println(response.getStatusLine().toString());
    }
    }
    
    演示站点可以使用 URL:
    用户:admin
    密码:ty386rhjzz


    当我尝试连接时,它会引发未经授权的访问异常。

    尝试将您的签名方法与此进行比较(它有效)

    该调用的结果将是属性签名的

    String signature = generateHmacSHA256Signature(salt, key);
    qparams.add(new BasicNameValuePair("signature", signature));
    
    生成salt/nonce的简单方法

    String nonce = String.valueOf(System.currentTimeMillis());
    

    请参见

    我认为整个
    getSaltedKey()
    例程是不必要的。您只需使用HMAC对salt(应称为a)进行签名,并使用提供的密钥进行签名,看起来您不需要对密钥+salt进行签名。

    Kayako已使用一个新的文件更新了他们的文档,该文件运行良好。

    在创建签名的四个步骤中,我看不到任何关于您试图签署的数据的引用。获取了实际API文档的URL吗?:)我想您第一次调用
    System.arraycopy()
    时遇到了问题--似乎您正在复制
    0
    字节。尝试在第一次调用中交换
    salt.length
    0
    ,看看这是否能改善情况。@sarnold我正在尝试连接到卓越,谢谢链接。
    sun.misc.BASE64Encoder
    。你可以使用任何你想要的风格。@GregS,还有其他有效的Base64类是JDK的一部分吗?@Elite Gentle:据我所知,你要么自己编写,要么使用第三方实现。为Sun(现在的Oracle)的这一事实感到羞耻,但您永远不应该使用Sun.*类。它们可能不在下一次更新中,也可能不在IBM运行时或其他程序中,等等。如果您使用它们,您的代码就不被视为纯Java。@GregS,是的,因此我前面说过,您可以使用任何您想要的味道。我刚刚演示了如何创建HMAC-SHA256签名。@精英绅士:我毫不怀疑你能理解,但你必须想到孩子们!他们会在不知道后果的情况下复制粘贴。