I was creating a signed URL for uploading an audio file. Url is used in Android apps. It's migration from azure to GCP so can write azure code here:
String cdnUrl = fileUploadConfig.has(containerName) ? fileUploadConfig.getString(containerName)
: Constants.AZURE_STORAGE_URL;
BlobServiceClient client = new BlobServiceClientBuilder().connectionString(storageConnectionString)
.buildClient();
BlobClient blobClient = client.getBlobContainerClient(containerName).getBlobClient(key);
BlobSasPermission blobSasPermission = new BlobSasPermission().setWritePermission(true);
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
BlobServiceSasSignatureValues values = new BlobServiceSasSignatureValues(expiryTime, blobSasPermission)
.setStartTime(OffsetDateTime.now());
String signature = blobClient.generateSas(values);
Map<String,String> headerMap = new HashMap<String,String>(){{
put("x-ms-blob-type","BlockBlob");
}};
JSONObject res = new JSONObject();
System.out.println(blobClient.getBlobUrl());
cdnUrl += key;
res.put("presignedUrl", blobClient.getBlobUrl() + "?" + signature);
res.put("cdnUrl", cdnUrl);
res.put("header",new JSONObject(headerMap) );
return res.toString();
When I am transforming it into GCP, I am writing the following code:
String cdnUrl;
try {
cdnUrl = KhabriUtils.getSecret("s3-cdn-mapping-" + containerName);
} catch (Exception e) {
// TODO Auto-generated catch block
cdnUrl = Constants.GCP_STORAGE_URL;
e.printStackTrace();
}
Storage storage = StorageOptions.newBuilder().setProjectId(Constants.GCP_PROJECT_ID).build().getService();
// Define resource
BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(containerName, key)).build();
// Generate Signed URL
Map<String, String> extensionHeaders = new HashMap<>();
extensionHeaders.put("Content-Type", "application/octet-stream");
URL url =
storage.signUrl(
blobInfo,
15,
TimeUnit.MINUTES,
Storage.SignUrlOption.httpMethod(HttpMethod.PUT),
Storage.SignUrlOption.withExtHeaders(extensionHeaders),
Storage.SignUrlOption.withV4Signature());
JSONObject res = new JSONObject();
cdnUrl += key;
res.put("presignedUrl", url);
res.put("cdnUrl", cdnUrl);
res.put("header", new JSONObject(extensionHeaders));
return res.toString();
It is giving me an error
Server response: code 403, body SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.GOOG4-RSA-SHA256
Tried to see if there is any permission missing.
Related
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.
Am trying to integrate finland's checkout api in my project. It always gives me 401 (i.e., as per API my Hmac calculation failed).
Her is the link to api where they calculated Hmac using nodejs and php.
https://checkoutfinland.github.io/psp-api/#/examples
Can anyone help me where am going wron in my java code. Here is my code:
// Make sure all check-out headers are in alphabetical order and
// all header keys must be in lower case
// if request contains body - it should be valid JSON and content-type header should be included
Map<String, String> headersMap = new TreeMap<>();
headersMap.put("checkout-account", "375917");
headersMap.put("checkout-algorithm", "sha256");
headersMap.put("checkout-method", "POST");
headersMap.put("checkout-nonce", String.valueOf(UUID.randomUUID())); // unique identifier for this request
headersMap.put("checkout-timestamp", ZonedDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.ISO_INSTANT )); // ISO 8601 format date time
/**
* All API calls need to be signed using HMAC and SHA-256 or SHA-512
*/
String headers[] = new String[headersMap.size()];
int index = 0;
for(Map.Entry<String, String> headersEntry: headersMap.entrySet()){
headers[index++] = headersEntry.getKey()+":"+headersEntry.getValue();
}
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("stamp", "29858472952");
requestBody.put("reference", "9187445");
requestBody.put("amount", 1500);
requestBody.put("currency", "EUR");
requestBody.put("language", "FI");
List<List<Map<String, Object>>> itemsList = new ArrayList<>();
List<Map<String, Object>> item = new ArrayList<>();
Map<String, Object> itemObj = new HashMap<>();
itemObj.put("unitPrice", 1500);
itemObj.put("units",1);
itemObj.put("vatPercentage", 5);
itemObj.put("productCode", "#1234");
itemObj.put("deliveryDate", "2018-09-01");
item.add(itemObj);
itemsList.add(item);
requestBody.put("items", itemsList);
Map<String, Object> customer = new HashMap<>();
customer.put("email", "test.customer#example.com");
requestBody.put("customer", customer);
Map<String, Object> redirectUrls = new HashMap<>();
redirectUrls.put("success","example.com");
redirectUrls.put("cancel","example.com");
requestBody.put("redirectUrls",redirectUrls);
String body = new ObjectMapper().writeValueAsString(requestBody);
String data = String.join("\n", ArrayUtils.addAll(headers, body));
String hmacSha256Hash = calculateHmacSha256("SAIPPUAKAUPPIAS", data);
if(hmacSha256Hash != null){
headersMap.put("content-type", "application/json; charset=utf-8");
headersMap.put("signature", hmacSha256Hash);
// Make network call checkout api
}
Hmac Calculation method:
private String calculateHmacSha256(String key, String data){
String resultHash = null;
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"),"HmacSHA256");
sha256_HMAC.init(secret_key);
resultHash = Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes()));
} catch (Exception e) {
System.out.println("Error calculating HmacSHA256 hash for data data"+ e);
}
return resultHash;
}
I implemented it identical to the one which they have in the examples.
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"
I am trying to delete amazon s3 object using rest API but not getting any success. I have created the URL(signed url) at server side using java and then made XHR request to that URL at client side(i.e. from browser).
Java code that i have used to sign the url:
public static String getSignedURL(String fileName, int fileOwnerId, String versionId){
Date expiration = new Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 10; // Add 10 minutes.
long seconds = (milliSeconds)/1000L;
String URL = null;
try {
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
String canonicalizedResource = "/"+AWS_BUCKET_NAME+"/" + fileOwnerId + "/" + encodedFileName;
String stringToSign = "DELETE\n\n\n" + seconds + "\n" + canonicalizedResource +"?versionId="+versionId;
byte[] keyBytes = AWS_SECRET_API_KEY.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] digest = mac.doFinal(stringToSign.getBytes());
byte[] base64bytes = Base64.encodeBase64(digest);
String signedString = new String(base64bytes, "UTF-8");
String signature = URLEncoder.encode(signedString, "UTF-8");
URL = "https://"+AWS_BUCKET_NAME+".s3.amazonaws.com/" + fileOwnerId +
"/" + encodedFileName +"?versionId="+versionId +"&Expires=" + seconds+"&AWSAccessKeyId=" +
AWS_ACCESS_KEY + "&Signature=" + signature;
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(Utilities.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException nsae) {
} catch (InvalidKeyException ike) {
}
System.out.println("URL IS :"+URL);
return URL;
}
And at client side:
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", deleteComplete, false);
xhr.open('DELETE', URL_GENERATED_FROM_SERVER, true);
xhr.setRequestHeader ("Access-Control-Allow-Origin", "*");
xhr.send();
Using this code for downloading an object from amazon s3 bucket works fine by replacing 'DELETE' request with 'GET'. But delete is not working. I have searched a lot but there is very less help available for rest API.
Finally, i integrated the aws sdk to delete the object from amazon s3 bucket and it works like lightning. But unable to get help doing it with rest API. So now i have used rest API for uploading and downloading and the sdk for deleting an object.
I am trying to test an android app using Robotium in Eclipse android JUnit testing.
I want to capture screenshots at different stages using Robotium and process them using OpenCV library on my PC instead of the android device.
I have been reading forums on separating these two tasks. However, haven't been able to do so.
It would be great if anyone can shed some pointers on this.
Thanks
To separate this two tasks, you need to realize (for example) REST client on android and server on the desktop.
Client should connect to server
Server pings client and client sends him screenshots. Perhaps, you will need to create separate thread in test.
Client should do the following:
When you need to take screenshot:
Object res = null;
res = new File(takeScreenShot(solo));
Then
public static String takeScreenShot(Solo _solo) {
solo = _solo;
String dir = "/sdcard/Robotium-Screenshots/";
String fileName = "screenshot";
java.io.File file = new java.io.File(dir, fileName);
if (file.exists()) {
file.delete();
}
solo.takeScreenshot(fileName);
solo.sleep(2000);
return "/sdcard/Robotium-Screenshots/" + fileName + ".jpg";
}
You need to send it
service.sendScreen(file);
and in your service will do smthng like this:
public static JSONObject sendScreen(final File file) {
JSONObject res;
String response;
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(host + "/" + methodName);
MultipartEntity entity = new MultipartEntity();
entity.addPart("file", new FileBody(file));
httppost.setEntity(entity);
try{
response = httpclient.execute(httppost, responseHandler);
res = (JSONObject) JSONValue.parse(response);
}
catch(IOException e)
{
res = null;
e.printStackTrace();
}
return res;
}
Here's example of code on C# to parse screenshot
public void StepError(Stream stream)
{
var result = new DCStepErrorResponce();
const string imageName = "file";
string fullFileName = String.Empty;
var parser = new MultipartFormDataParser(stream);
if (parser.Files.Any(file => file.Name == imageName))
{
Stream data = parser.Files.First(file => file.Name == imageName).Data;
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "HFAutomationTesting", "Temp", "ScreenShots");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
do
{
string fileName = String.Format("{0:yy_MM_dd_HH_mm_ss_ffff}.jpg", DateTime.Now);
fullFileName = Path.Combine(path, fileName);
} while (File.Exists(fullFileName));
var fileToUpload = new FileStream(fullFileName, FileMode.Create);
byte[] bytearray = ToByteArray(data);
fileToUpload.Write(bytearray, 0, bytearray.Length);
fileToUpload.Close();
fileToUpload.Dispose();
}
}
You need just to realize rest server on C# and debug it.