OAuth 401 Unauthorised - java

so learning still but i'm getting an Error 401 unauthorised back from the code below. I know that the OAuth header works as it works in postman so i'm assuming there is a problem with the POST request / Auth header? Any ideas?
//set timestamp
Long timestamp = System.currentTimeMillis()/1000;
//set nonce ***** call from main system*************************************************************
String aString = randomAlphaNumeric(11);
// other stuff
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders headers = new HttpHeaders();
String url = "aURL";
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// String auth = Base64.getEncoder().encodeToString(credentials.getBytes());
List<NameValuePair> oauthHeaders = new ArrayList<>(9);
oauthHeaders.add(new BasicNameValuePair("oauth_consumer_key", "aKey"));
oauthHeaders.add(new BasicNameValuePair("oauth_nonce", aString));
oauthHeaders.add(new BasicNameValuePair("oauth_timestamp", String.valueOf(timestamp)));
oauthHeaders.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
oauthHeaders.add(new BasicNameValuePair("oauth_version", "1.0"));
//generate signature
//encode
String encodedURL = encode(oauthHeaders.toString());
System.out.println("encoded URL:" +encodedURL);
//form base string
String baseString = "POST&"+encode(url).toString()+encodedURL;
System.out.println("Base String: "+baseString);
//form signature
byte[] byteHMAC = null;
try {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec spec;
if (null == secretKey) {
String signingKey = encode(secretKey) + '&';
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
} else {
String signingKey = encode(secretKey) + '&' + encode(secretKey);
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
}
mac.init(spec);
byteHMAC = mac.doFinal(baseString.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
String signature = new BASE64Encoder().encode(byteHMAC);
System.out.println("oauth signature: "+signature);
//set signature to params
oauthHeaders.add(new BasicNameValuePair("oauth_signature", signature));
String test = "OAuth "+oauthHeaders.toString();
headers.set("Authorization", test);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("Name",name.toString());
map.add("Region",region.toString());
HttpEntity<MultiValueMap<String, String>> requestEntity= new HttpEntity<MultiValueMap<String, String>>(headers, map);
System.out.println(requestEntity);
ResponseEntity<String> response= restTemplate.exchange(url ,HttpMethod.POST, requestEntity, String.class);
System.out.println(response.toString());
HttpStatus status = response.getStatusCode();
status.toString();
if(status.equals("200")){
Notification.show("Employer" + name +" added successfully");
}
else{
Notification.show("Unsuccessful, error: "+status);
}
}
removed the URL and consumer key / signature for obvious reasons.
The following System out prints might help as well:
encoded params:
%5Boauth_consumer_key%3aKey%2C%20oauth_nonce%3DWZU8H1B5JA6%2C%20oauth_timestamp%3D1511621759%2C%20oauth_signature_method%3DHMAC-SHA1%2C%20oauth_version%3D1.0%5D
Base String: POST&https%3A%2F%2Fapi.test.payrun.io%2FEmployer%5Boauth_consumer_key%3aKey%2C%20oauth_nonce%3DWZU8H1B5JA6%2C%20oauth_timestamp%3D1511621759%2C%20oauth_signature_method%3DHMAC-SHA1%2C%20oauth_version%3D1.0%5D
oauth signature: DlRJGSzgRIItzz+LzMbgnIfbOqU=

The value of oauth_signature is wrong. You are using asignature as the value of oauth_signature, but you have to compute the correct value for your request and set it to oauth_signature. If the value of oauth_signature is wrong, the server will reject your request. See "3.4. Signature" in RFC 5849 (The OAuth 1.0 Protocol) for details.

For anyone who wants to make this work then please see below for a complete OAuth Generator example :):
public class oAuthGenerator {
private String httpMethod;
private String params;
private String url;
//Required for percent encoding
private static final String ENC = "ASCII";
//Required for nonce
private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
//As provided by Payrun.io
private static final String secretKey = "aSecretKey";
//As provided by Payrun.io
private static final String consumerKey ="aConsumerKey";
private List veriList;
//Constructor for setting signature base string values.
//#Param url needs to be percent encoded
//#Param params needs to be percent encoded, this is done #Method generateOAuth()
public oAuthGenerator(String httpMethod,String url )throws Exception {
veriList= new ArrayList<String>();
veriList.add("POST");
veriList.add("GET");
veriList.add("PUT");
veriList.add("DELETE");
if (veriList.contains(httpMethod)){
this.httpMethod = httpMethod+"&";}
this.url = URLEncoder.encode(url,ENC)+"&";
}
//A method used to generate the OAuth Authorization header
//#Method randomAlphaNumeric() calls internal method using instance variable ALPHA_NUMERIC_STRING
//#Method getSignature() returns String HMACSHA1 > Base64 encoded value of httpMethod,url,params
public String generateOAuth()throws Exception{
//Set timestamp as seconds from 01-01-1970
Timestamp timestamp = new Timestamp(System.currentTimeMillis()/1000);
Long aTimestamp = timestamp.getTime();
//Set nonce which is a 10 digit random, non repeating alpha-numeric value
String aNonce = randomAlphaNumeric(10);
//Normalize and form param string
String normalizedParams = "oauth_consumer_key="+consumerKey+"&"+"oauth_nonce="+aNonce+"&"+"oauth_signature_method="+"HMAC-SHA1"+"&"+"oauth_timestamp="+ aTimestamp.toString()+"&"+"oauth_version="+"1.0";
//Percent encoded params
params = URLEncoder.encode(normalizedParams,ENC);
//Set signature variable
String signature = getSignature();
//place into required format
String oAuthResult = "OAuth "+"oauth_version="+"\"1.0\""+","+"oauth_consumer_key="+"\"" + consumerKey + "\""+","+"oauth_signature_method="+"\"HMAC-SHA1\""+","+"oauth_timestamp="+"\""+aTimestamp+"\""+","+"oauth_nonce="+"\""+aNonce+"\""+","+"oauth_signature="+"\""+signature+"\"";
return oAuthResult;
}
// A method designed to return a hashed and base64 encoded value.
//#Param aString holds HMAC-SHA1 and Base 64 encoded value of variables httpMethod,url,params
//#Param result holds percent encoded value of aString
private String getSignature()
throws Exception {
//form base string
StringBuilder base = new StringBuilder();
base.append(httpMethod);
base.append(url);
base.append(params);
//Set SecretKey of variable secretKey using HMAC-SHA1 algorithm
SecretKey signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Compute the hmac on input data bytes, then encode to Base64
String aString = Base64.getEncoder().encodeToString(mac.doFinal(base.toString().getBytes(ENC))).trim();
//Percent encoded the Base64 value
String result = URLEncoder.encode(aString, ENC);
return new String(result);
}
//Required for nonce, returns a random alpha numeric value by using variable ALPHA_NUMERIC_STRING
private static String randomAlphaNumeric(int count) {
StringBuilder builder = new StringBuilder();
while (count-- != 0) {
int character = (int)(Math.random()*ALPHA_NUMERIC_STRING.length());
builder.append(ALPHA_NUMERIC_STRING.charAt(character));
}
return builder.toString();
}
}

Related

when I use shopee open platform API,I use the follow method to build a url to send a post request,I often get the message "Wrong sign",any solutions?

public static String buildSignAndUrl(long partnerId, String secret, String path){
long timestamp = System.currentTimeMillis() / 1000L;
String base_String = String.format("%s%s%s", partnerId, path, timestamp);
byte[] baseStringBytes;
byte[] partner_key;
BigInteger sign = null;
try{
baseStringBytes = base_String.getBytes(StandardCharsets.UTF_8);
partner_key = secret.getBytes(StandardCharsets.UTF_8);
Mac mac = Mac.getInstance(HmacAlgorithms.HMAC_SHA_256.toString());
SecretKeySpec secret_key = new SecretKeySpec(partner_key, HmacAlgorithms.HMAC_SHA_256.toString());
mac.init(secret_key);
sign = new BigInteger(mac.doFinal(baseStringBytes));
} catch (Exception e){
e.printStackTrace();
}
String url = SANDBOX_HOST + path + String.format("?partner_id=%s&timestamp=%s&sign=%s",
partnerId, timestamp, String.format("%032x", sign));
return url;
}
// Why use this url to send a post may cause "Wrong sign"? the timestamp in url and the timestamp to calculate a sign are same;

How to decode access token and get username and email in java?

How can I get the name and email from a token .
Structure of the token using jwt.io
http://prntscr.com/yzyf2b
Any help is appreciated.
Update full solution with the help of below posts so credits to them .
String jwtToken = token;
System.out.println("------------ Decode JWT ------------");
String[] split_string = jwtToken.split("\\.");
String base64EncodedHeader = split_string[0];
String base64EncodedBody = split_string[1];
String base64EncodedSignature = split_string[2];
System.out.println("~~~~~~~~~ JWT Header ~~~~~~~");
Base64 base64Url = new Base64(true);
String header = new String(base64Url.decode(base64EncodedHeader));
System.out.println("JWT Header : " + header);
System.out.println("~~~~~~~~~ JWT Body ~~~~~~~");
String body = new String(base64Url.decode(base64EncodedBody));
System.out.println("JWT Body : " + body);
JSONObject jsonObject = new JSONObject(body);
System.out.println(jsonObject.get("email"));
System.out.println(jsonObject.get("name"));
A JWToken has the following structure Header.Body.Signature. Hence, first you should split the token into three parts, namely Header, Body and Signature.
For that you can use
String[] token_part = jwtToken.split("\\.");
then apply what you already have done but for the token_part[1] (i.e., the payload or Body), namely:
sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
String a = new String(decoder.decodeBuffer(token_part[1]));
For the decoding part, alternatively, you can try this SO thread.
After that you can use the org.json.JSONObject to parse the JSON. An example:
public class Token {
public static void main(String[] args) {
String token = "{\"name\":\"john doe\", \"email\": \"john#mail.com\"}";
JSONObject jsonObject = new JSONObject(token);
System.out.println(jsonObject.get("email"));
System.out.println(jsonObject.get("name"));
}
}
Output:
john#mail.com
john doe
Full Example:
String jwtToken = token;
System.out.println("------------ Decode JWT ------------");
String[] split_string = jwtToken.split("\\.");
String base64EncodedHeader = split_string[0];
String base64EncodedBody = split_string[1];
String base64EncodedSignature = split_string[2];
System.out.println("~~~~~~~~~ JWT Header ~~~~~~~");
Base64 base64Url = new Base64(true);
String header = new String(base64Url.decode(base64EncodedHeader));
System.out.println("JWT Header : " + header);
System.out.println("~~~~~~~~~ JWT Body ~~~~~~~");
String body = new String(base64Url.decode(base64EncodedBody));
System.out.println("JWT Body : " + body);
JSONObject jsonObject = new JSONObject(body);
System.out.println(jsonObject.get("email"));
System.out.println(jsonObject.get("name"));

invalid_client for sign in with apple

What I try to achieve:
iOS client sends a JWT token to the backend.
Backend (Java) calls https://appleid.apple.com/auth/token to verify the token.
what I have so far:
to make Apple verification call:
restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", clientId); // app_id like com.app.id
String token = generateJWT(); // generated jwt
map.add("client_secret", token);
map.add("grant_type", "authorization_code");
map.add("code", authorizationCode); // JWT code we got from iOS
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
final String appleAuthURL = "https://appleid.apple.com/auth/token";
String response = restTemplate.postForObject(appleAuthURL, request, String.class);
token generation:
final PrivateKey privateKey = getPrivateKey();
final int expiration = 1000 * 60 * 5;
String token = Jwts.builder()
.setHeaderParam(JwsHeader.KEY_ID, keyId) // key id I got from Apple
.setIssuer(teamId)
.setAudience("https://appleid.apple.com")
.setSubject(clientId) // app id com.app.id
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.setIssuedAt(new Date(System.currentTimeMillis()))
.signWith(SignatureAlgorithm.ES256, privateKey) // ECDSA using P-256 and SHA-256
.compact();
return token;
to get my private key from the file:
final Reader pemReader = new StringReader(getKeyData());
final PEMParser pemParser = new PEMParser(pemReader);
final JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
final PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
final PrivateKey pKey = converter.getPrivateKey(object);
I confirmed my JWT has all required fields:
{
"kid": "SAME KEY AS MY KEY ID",
"alg": "ES256"
}
{
"iss": "Blahblah",
"aud": "https://appleid.apple.com",
"sub": "com.app.id",
"exp": 1578513833,
"iat": 1578513533
}
This line caught my attention:
map.add("code", authorizationCode); // JWT code we got from iOS
The authorizationCode is not a jwt
JSON Web Tokens consist of 3 parts separated by dots
but the authorizationCode has 4 parts like this:
text1.text2.0.text3
You are probably using the identityToken from the iOS app instead of the authorizationCode
This is how you retrieve it:
let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!
print("authorizationCode: \(authorizationCode)")
Also good to have the following in mind for those who might come here after getting the same invalid_client error:
kid is the id for the private key from developer.apple.com/account/resources/authkeys/list
keyFile is the file holding the private key downloaded from developer.apple.com
teamID can be found by logging in to developer.apple.com and clicking on account, the teamID can be seen in the upper right corner
the value in aud should be https://appleid.apple.com
app_id is the bundle identifier for the app
In case it might help, here is a working solution in python to create a client_secret:
# $ pip install pyjwt
import jwt
import time
kid = "myKeyId"
keyFile = "/pathToFile/AuthKey.p8"
key = ""
with open(keyFile, 'r') as myFile:
key = myFile.read()
print(key)
timeNow = int(round(time.time()))
time3Months = timeNow + 86400*90
claims = {
'iss': teamID,
'iat': timeNow,
'exp': time3Months,
'aud': 'https://appleid.apple.com',
'sub': app_id,
}
secret = jwt.encode(claims, key, algorithm='ES256', headers={'kid': kid})
print("secret:")
print(secret)
client_secret = secret.decode("utf-8")
print(client_secret)
Save the clientSecret and appleToken into the local DB at login time with Apple ID.
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
print("didCompleteWithAuthorization : -\(authorization)")
switch authorization.credential {
case let appleIDCredential as ASAuthorizationAppleIDCredential:
// Create an account in your system.
let userIdentifier = appleIDCredential.user
let fullName = appleIDCredential.fullName?.givenName
let email = appleIDCredential.email
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
return
}
StorageServices.storeInDefaults(object: idTokenString, key: "appleToken")
// Add new code below
if let authorizationCode = appleIDCredential.authorizationCode,
let codeString = String(data: authorizationCode, encoding: .utf8) {
StorageServices.storeInDefaults(object: codeString, key: "clientSecret")
}
default:
break
}
call the apple token revoke api.
func callRevokeTokenAPI() {
guard let clientSecret = StorageServices.readFromDefaults(key: "clientSecret") as? String else {return}
guard let appleToken = StorageServices.readFromDefaults(key: "appleToken") as? String else {return}
let parameters = "client_id=com.oxstren.Actofit-Wear&client_secret=\(clientSecret)&token=\(appleToken)&token_type_hint=access_token"
print(parameters)
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://appleid.apple.com/auth/revoke")!,timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let response = response as? HTTPURLResponse, error == nil else {
print("error", error ?? URLError(.badServerResponse))
return
}
print(response)
guard let data = data else {
print(String(describing: error))
return
}
print(String(data: data, encoding: .utf8)!)
}
task.resume()
} //end function body.

Rest service call from java Key length not 128/192/256 bits

I want send a rest service call from Java using "GET" request.But i am getting the following error.I am able to use it in the postman but i am unable to send java application.
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)
This the my sample code
package 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 & 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;
}
}
Is there any thing else which i am missing.
Also if i need to do POST request i need to add json data in the body tag directly?
EDIT: Is this really your key? maybe you changed the secret key to another size for not posting the original key here. If so check if keyBytes.length really gives you 16,24 or 32
I really digged deep now... I can't find any error in any of your code.
Your key is 256 bits long:
byte[] keyBytes = "12345678-1234-1234-1234-12345678".getBytes("UTF-8");
int bits = keyBytes.length*8;
System.out.println(bits); //gives 256
So i checked the CMac.java and they basically just copy the key with System.arraycopyso there is no error there.
They check the key in https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java with
int keyLen = key.length;
if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
{
throw new IllegalArgumentException("Key length not 128/192/256 bits.");
}
And since key.length is equal to 32 and 100000 & 111 is obviously 0 I really don't see anything wrong.
Please try to use a key with only 16 characters and tell us if the error still applies. Maybe you should also check if you really use the latest version of org.bouncycastle.crypto
If this doesn't help try just key.getBytes() without the charset UTF-8 for once.

Kraken API - Invalid signature

I'm trying to change a working example from C sharp to Java but i'm struggling and don't know where the problem is. I contacted Kraken and they advised me that I have the wrong signature... The response is:
{"error":["EAPI:Invalid signature"]}
Here's the C sharp version:
private JsonObject QueryPrivate(string a_sMethod, string props = null)
{
// generate a 64 bit nonce using a timestamp at tick resolution
Int64 nonce = DateTime.Now.Ticks;
props = "nonce=" + nonce + props;
string path = string.Format("/{0}/private/{1}", _version, a_sMethod);
string address = _url + path;
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(address);
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
webRequest.Headers.Add("API-Key", _key);
byte[] base64DecodedSecred = Convert.FromBase64String(_secret);
var np = nonce + Convert.ToChar(0) + props;
var pathBytes = Encoding.UTF8.GetBytes(path);
var hash256Bytes = sha256_hash(np);
var z = new byte[pathBytes.Count() + hash256Bytes.Count()];
pathBytes.CopyTo(z, 0);
hash256Bytes.CopyTo(z, pathBytes.Count());
var signature = getHash(base64DecodedSecred, z);
webRequest.Headers.Add("API-Sign", Convert.ToBase64String(signature));
if (props != null)
{
using (var writer = new StreamWriter(webRequest.GetRequestStream()))
{
writer.Write(props);
}
}
//Make the request
try
{
using (WebResponse webResponse = webRequest.GetResponse())
{
using (Stream str = webResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(str))
{
return (JsonObject)JsonConvert.Import(sr);
}
}
}
}
}
The full code is here at:
https://bitbucket.org/arrivets/krakenapi/src/cff138b017c38efde2db1a080fb765790a6d04c8/KrakenClient/KrakenClient.cs?at=master&fileviewer=file-view-default
Here is my Java version:
private void fetch() throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException {
String version = "0";
String key = ".....6";
String secret = "....g==";
long nonce = System.currentTimeMillis();
String props = null;
props = "nonce=" + nonce + props; // I've tried this with and without the 'null' on the end
// url
String url = "https://api.kraken.com";
String path = "/" + version + "/private/" + "Balance";
String address = url + path;
// post req
HttpPost httpPost = new HttpPost(address);
// headers
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setHeader("API-Key", key);
// decode buffer
BASE64Decoder decoder = new BASE64Decoder();
byte[] base64DecodedSecred = decoder.decodeBuffer(secret);
// nonce & props
String np = nonce + (char) 0 + props;
// create byte array
byte[] pathBytes = path.getBytes("UTF-8");
byte[] hash256Bytes = sha256(np);
byte[] z = new byte[pathBytes.length + hash256Bytes.length];
System.arraycopy(pathBytes, 0, z, 0, pathBytes.length);
System.arraycopy(hash256Bytes, 0, z, pathBytes.length, hash256Bytes.length);
// encrypt signature
byte[] signature = hmacEncrypt(z, base64DecodedSecred); // my hmacEncrypt is message, secret (opposite to the c sharp)
BASE64Encoder encoder = new BASE64Encoder();
httpPost.setHeader("API-Sign", encoder.encode(signature));
// Post
List<NameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("nonce", String.valueOf(nonce)));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));
// Client & Response
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
// parse
JsonParser jp = new JsonParser();
JsonElement root = jp.parse(EntityUtils.toString(entity));
System.out.println(root); // {"error":["EAPI:Invalid signature"]}
// close client
httpClient.close();
}
I'm sorry to have posted a large bits of code, any help would be appreciated. Thank you!
there is no cchmac 512 encryption in your code. that is why you get this EAPI:Invalid signature"

Categories

Resources