This is a follow question on this answer: Link Here
Based on that answer I am able to bypass the security check of testcookie-nginx-module used by byethost hosting.
The problem is that the cookies I used are copied from a web browser. I need to get the COOKIE from my website, using my android device so that I can use it to make request on byethost server.
Byethost provides a __test cookie to check for validity of a request on an existing session, if it seems that the only way for me to access to server is to be a "valid browser", How to tell the server that I am valid browser from an android device? So that I can have the cookie given to the web browsers.
I met the same problem and first I used WebView to access the page and get the cookies, use that to bypass the security check of testcookie-nginx-module
WebView myWebView = new WebView(this);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.loadUrl("my_page_here");
String cookies = CookieManager.getInstance().getCookie("my_page_here");
System.out.println(cookies);
myWebView.destroy();
Then to use with Volley, i created a CustomRequest extends StringRequest and override getHeaders like this:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240 ");
params.put("Cookie", cookies+"; expires=Fri, 1-Jan-38 06:55:55 GMT; path=/");
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
And that's all, do you have any other solution yet? if not you could check this :D
In case someone still needs a better answer, I would like to add mine.
First, I created a Splash Screen Activity which first connects to the byethost server and get the response and then parse the "__test" cookie from it.
void getCookie() {
RequestQueue mQueue = Volley.newRequestQueue(this);
StringRequest stringRequest = new StringRequest(Request.Method.GET, Constants.SERVER_URL,
response -> {
try {
if (response.contains("src=\"/aes.js\"") || response.contains("src=\"/aes.min.js\"")) {
String beginOffsetA = "var a=toNumbers(\"";
String beginOffsetB = "\"),b=toNumbers(\"";
String beginOffsetC = "\"),c=toNumbers(\"";
String endOffsetC = "\");document.cookie=";
String a = response.substring((response.indexOf(beginOffsetA) + (beginOffsetA).length()), response.indexOf(beginOffsetB)); // Value of var a
String b = response.substring((response.indexOf(beginOffsetB) + (beginOffsetB).length()), response.indexOf(beginOffsetC)); // Value of var b
String c = response.substring((response.indexOf(beginOffsetC) + (beginOffsetC).length()), response.indexOf(endOffsetC)); // Value of var c
Constants.COOKIE = "__test=" + encrypt(hexStringToByteArray(a), hexStringToByteArray(b), hexStringToByteArray(c)).toLowerCase() + "; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/"; //This is the "__test" Cookie, e.g., "__test=8927389y1huwieqyue"
} else {
theServerDoesNotNeedTestCookie();
}
} catch (Exception e){
e.printStackTrace();
didntWork();
}
},
error -> doesNotWork();
);
mQueue.add(stringRequest);
}
public String encrypt(byte[] key, byte[] initVector, byte[] data) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] encrypted = cipher.doFinal(data);
return bytesToHex(encrypted);
} catch (Exception ex) {
new Reporter(this, ex);
}
return null;
}
public String bytesToHex(byte[] bytes) {
final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
This works for byethost.
Related
I have a trouble with send HTTP Get request via Binance exchange.
(I need to return my wallet status)
the GitHub manual says that (https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md)
Account information (USER_DATA)
GET /api/v3/account (HMAC SHA256)
Get current account information.
Weight: 5
Parameters:
Name Type Mandatory Description
recvWindow LONG NO
timestamp LONG YES
my codes are as shown follows
public static String timestamp = String.valueOf(System.currentTimeMillis());
public static void wallet_status () throws NoSuchAlgorithmException, InvalidKeyException {
String url = "https://api.binance.com/api/v3/account×tamp=" + timestamp;
//sign url
Mac shaMac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(BINANCE_SECRET_KEY.getBytes(), "HmacSHA256");
shaMac.init(keySpec);
final byte[] macData = shaMac.doFinal(url.getBytes());
String sign = Hex.encodeHexString(macData);
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet("https://api.binance.com/api/v3/account"+"?timestamp="+timestamp+"?signature="+sign);
request.addHeader("X-MBX-APIKEY", BINANCE_API_KEY);
try {
HttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
if (entity != null) {
try (InputStream stream = entity.getContent()) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
} //end
the server response is like below
{"code":-1100,"msg":"Illegal characters found in parameter 'timestamp'; legal range is '^[0-9]{1,20}$'."}
but my String timestamp is a 13 digit numeric string which should be no problem. please help.
Your url is wrong. Change ?signature= to &signature=.
You have to use & as the delimeter for subsequent variables in a URL. Currently, the ?signature... is seen as the value of the timestamp variable, causing that error message.
Query string delimiter is & not ?
Use: "https://api.binance.com/api/v3/account"+"?timestamp="+timestamp+"&signature="+sign
This Post is really old, but maybe someone will help this solution:
// Binance testnet Data
private String baseUrl = "https://api.binance.com";
private String apiKey = "you API Key";
private String apiSecret = "Your Secret";
private String endpoint = "/api/v3/account";
private String parameters = "recvWindow=20000×tamp=" + System.currentTimeMillis();
public void getData() throws Exception {
byte[] bytes = hmac("HmacSHA256", apiSecret.getBytes(), parameters.getBytes());
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(baseUrl + endpoint + "?" + parameters + "&signature=" + bytesToHex(bytes)))
.setHeader("X-MBX-APIKEY", apiKey)
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// print status code
System.out.println(response.statusCode());
// print response body
System.out.println(response.body());
}
public static byte[] hmac(String algorithm, byte[] key, byte[] message) throws Exception {
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(message);
}
public static String bytesToHex(byte[] bytes) {
final char[] hexArray = "0123456789abcdef".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0, v; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Iam in process to migrate system from .Net to Java users passwords stored in database, the problem that java don't return same Hashed value as what calculated in .Net and the problem is due to bytes in java are signed while in c# unsigned, so any idea how to solve this using java ?
public static string NetHash(int pSalt, string pPassword, string customerCode)
{
// Create Byte array of password string
ASCIIEncoding encoder = new ASCIIEncoding();
Byte[] secretBytes = encoder.GetBytes(pPassword + customerCode.ToLower());
// Create a new salt
Byte[] saltBytes = new Byte[4];
saltBytes[0] = (byte)(pSalt >> 24);
saltBytes[1] = (byte)(pSalt >> 16);
saltBytes[2] = (byte)(pSalt >> 8);
saltBytes[3] = (byte)(pSalt);
// append the two arrays
Byte[] toHash = new Byte[secretBytes.Length + saltBytes.Length];
Array.Copy(secretBytes, 0, toHash, 0, secretBytes.Length);
Array.Copy(saltBytes, 0, toHash, secretBytes.Length, saltBytes.Length);
SHA1 sha1 = SHA1.Create();
Byte[] computedHash = sha1.ComputeHash(toHash);
return encoder.GetString(computedHash);
}
same was changed to Java as following
public String javaHash(int pSalt, String pPassword, String customerCode) {
String result = "";
String s = pPassword + customerCode.toLowerCase();
byte[] secretBytes = s.getBytes();
byte[] saltBytes = new byte[4];
saltBytes[0] = (byte) (pSalt >> 24);
saltBytes[1] = (byte) (pSalt >> 16);
saltBytes[2] = (byte) (pSalt >> 8);
saltBytes[3] = (byte) (pSalt);
byte[] toHash = ArrayUtils.addAll(secretBytes, saltBytes);
try {
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1");
byte[] digest1 = md.digest(toHash);
result = new String(digest1);
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Unable to check hash password for user [{}]", customerCode, e);
}
return result;
}
incase any one need similar thing, both resulting the same but the problem was in result string encoding , both should use the same encoding "i.e ISO-8859 or UTF8".
I am currently trying to recreate a Google One Time Password generator. I use a shared secret generated when I setup Google Authenticator.
I tried looking into the Google Authenticator sources and all around the internet really and I find a lot of similarities with my code but I can't really find where i'm wrong.
The first part seems correct. As for the hmac, I don't think I could mess up here but I might be wrong. The truncating part is still a bit blurry for me and I tried a lot of different implementations but I just cannot get a working OTP. (I'm using Google Authenticator to compare the results)
private String truncateHash(byte[] hash) {
int offset = hash[hash.length - 1] & 0xF;
long truncatedHash = 0;
for (int i = 0; i < 4; ++i) {
truncatedHash <<= 8;
truncatedHash |= (hash[offset + i] & 0xFF);
}
truncatedHash &= 0x7FFFFFFF;
truncatedHash %= 1000000;
int code = (int) truncatedHash;
String result = Integer.toString(code);
for (int i = result.length(); i < 6; i++) {
result = "0" + result;
}
return result;
}
private byte[] hmacSha1(byte[] value, byte[] keyBytes) {
try {
Mac mac = HmacUtils.getHmacSha1(keyBytes);
byte[] rawHmac = mac.doFinal(value);
return new Hex().encode(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String GoogleAuthenticatorCode(String secret) throws UnsupportedEncodingException {
Base32 base = new Base32();
byte[] key = base.decode(secret);
//Update from Andrew Rueckert's response
long value = new Date().getTime() / TimeUnit.SECONDS.toMillis(30);
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8) {
data[i] = (byte) value;
}
//
System.out.println("Time remaining : " + new Date().getTime() / 1000 % 30);
byte[] hash = hmacSha1(data, key);
return truncateHash(hash);
}
UPDATE :
I tried copying and pasting the code from Andrew Rueckert's response's link as well as this one https://github.com/wstrange/GoogleAuth/blob/master/src/main/java/com/warrenstrange/googleauth/GoogleAuthenticator.java and the one from RFC 4226. Neither of these give me a correct OTP
Can anyone enlighten me please?
Your byte value[] needs to be the byte representation of the time as a long, and it looks like it's currently the byte representation of that number as a String of digit characters. Instead of
Double time = floor(new Date().getTime() / 1000 / 30);
String message = String.valueOf(time.intValue());
byte[] value = message.getBytes("UTF-8");
byte[] hash = hmacSha1(value, key);
You'd want something like:
// decimal truncation is free when dealing with int/long
long value = new Date().getTime() / 1000 / 30;
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8) {
data[i] = (byte) value;
}
byte[] hash = hmacSha1(data, key);
I managed to get a Google TOTP implementation set up by following this guide, if you want one more resource to look into.
I solved my problem so I thought I would post it there in case someone needs it.
It was partialy due to the Base32 class I was using which didn't return a correct key. The truncating wasn't correct either.
It's compatible with Google Authenticator app.
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Authentication {
Authentication() {};
private String truncateHash(byte[] hash) {
String hashString = new String(hash);
int offset = Integer.parseInt(hashString.substring(hashString.length() - 1, hashString.length()), 16);
String truncatedHash = hashString.substring(offset * 2, offset * 2 + 8);
int val = Integer.parseUnsignedInt(truncatedHash, 16) & 0x7FFFFFFF;
String finalHash = String.valueOf(val);
finalHash = finalHash.substring(finalHash.length() - 6, finalHash.length());
return finalHash;
}
private byte[] hmacSha1(byte[] value, byte[] keyBytes) {
SecretKeySpec signKey = new SecretKeySpec(keyBytes, "HmacSHA1");
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);
byte[] rawHmac = mac.doFinal(value);
return new Hex().encode(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String GoogleAuthenticatorCode(String secret) throws Exception {
if (secret == null || secret == "") {
throw new Exception("Secret key does not exist.");
}
long value = new Date().getTime() / TimeUnit.SECONDS.toMillis(30);
Base32 base = new Base32(Base32.Alphabet.BASE32, false, true);
byte[] key = base.fromString(secret);
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8) {
data[i] = (byte) value;
}
byte[] hash = hmacSha1(data, key);
return truncateHash(hash);
}
}
The Base32 I used is available here if needed along with the rest of the project : https://github.com/Poncholay/OTPGenerator/blob/master/src/main/java/com/requireris/app/web/rest/Base32.java
I have an API documentation that requires encrypting a key to authenticate,
I managed to build and compile their sample code, but the results on windows are different than linux.
When I run and test from Windows, all seems to be correct and works with the API.
That same test on Linux outputs a different result. I need it working on Linux since that's the main server.
I am using & running the same jar file on both environments.
This is the key I am trying to encrypt (it's a dynamic key):
2136230$486B91E1BEA5D082BA3601CD803585CE$20140409$20140409$$ABCDEFGH$Reserved$CTC
This is the correct output on Windows (it's obviously quite longer):
F7BE2B7E0CEAD9D09135FCF2A8AEB11E2937D26B33CCBC9B8132A29A3534040C9737B2A8E3F271A9DF6454696CF890F7886223AE9C86F81EF58E41AEAA3D34A80F7089154E64F4FD36E75C25A7C2DA7FF03D21F57DA950F5
This is the wrong output from Linux:
F66D4CE1238B30EE54ABC74966D7AC3064FEA3ADFB9D37548E41509CE4FED9CB1D146651B491F2433169999A85F73DAF9ACD07A090DF3D85477BE4201ADC9E1A0181EA7CB763050A
What is causing this and how to correct it ?
This is the source code of the program to use as we received from the API company:
public class DESUtil
{
private static final String Algorithm = "DESede/ECB/PKCS5Padding";// DESede/ECB/PKCS5Padding;DESede
private static final String DESede = "DESede";
public static byte[] encrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static byte[] decrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static String byte2hex(byte[] b)
{
StringBuffer hs = new StringBuffer();
String stmp = "";
for (int n = 0; n <b.length; n++)
{
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1)
hs.append("0").append(stmp);
else
hs.append(stmp);
}
return hs.toString().toUpperCase(Locale.getDefault());
}
public static byte[] hex2byte(String hexStr)
{
if (hexStr.length() % 2 != 0)
{
AppLogger.error("hex2bytes's hexStr length is not even.");
return null;
}
byte[] toBytes = new byte[hexStr.length() / 2];
for (int i = 0, j = 0; i <hexStr.length(); j++, i = i + 2)
{
int tmpa = Integer.decode(
"0X" + hexStr.charAt(i) + hexStr.charAt(i + 1)).intValue();
toBytes[j] = (byte) (tmpa & 0XFF);
}
return toBytes;
}
public static void main(String[] args)
{
Security.addProvider(new com.sun.crypto.provider.SunJCE());
final byte[] rawKey = "db90e7eb".getBytes();
final byte[] keyBytes = new byte[24];
for (int i = 0; i <rawKey.length; i++)
{
keyBytes[i] = rawKey[i];
}
for (int i = rawKey.length; i <keyBytes.length; i++)
{
keyBytes[i] = (byte)0;
}
String szSrc = "20926330$AD75B1697FB5EB6345B2D412124030D2$10086$10086$10.164.111$ABCDEFGH$Reserved$CTC";
System.out.println("string before encrypt:" + szSrc);
byte[] encoded = null;
try
{
encoded = encrypt(keyBytes, szSrc.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string after encrypt::" + byte2hex(encoded));
byte[] srcBytes = null;
try
{
srcBytes = decrypt(keyBytes, encoded);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string before decode: :" + (new String(srcBytes)));
}
}
Almost certainly your use of szSrc.getBytes() which uses the platform's default character encoding.
Try szSrc.getBytes("ISO-8859-1") as a starter if it's working on Windows, but if this string comes from an external service you should determine the encoding scheme dynamically (eg. if it comes through a Servlet use httpRequest.getCharacterEncoding()).
Can someone figure out why the output of these (php and java) snippets of code don't return the same SHA512 for the same input?
$password = 'whateverpassword';
$salt = 'ieerskzcjy20ec8wkgsk4cc8kuwgs8g';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
echo "digest: ".base64_encode($digest);
for ($i = 1; $i < 5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encoded_pass = base64_encode($digest);
echo $encoded_pass;
This is the code on the android application:
public String processSHA512(String pw, String salt, int rounds)
{
try {
md = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("No Such Algorithm");
}
String result = hashPw(pw, salt, rounds);
System.out.println(result);
return result;
}
private static String hashPw(String pw, String salt, int rounds) {
byte[] bSalt;
byte[] bPw;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
try {
bSalt = appendedSalt.getBytes("ISO-8859-1");
bPw = pw.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported Encoding", e);
}
byte[] digest = run(bPw, bSalt);
Log.d(LCAT, "first hash: " + Base64.encodeBytes(digest));
for (int i = 1; i < rounds; i++) {
digest = run(digest, bSalt);
}
return Base64.encodeBytes(digest);
}
private static byte[] run(byte[] input, byte[] salt) {
md.update(input);
return md.digest(salt);
}
The library for base64 encoding is this: base64lib
This java code is actually some modified code I found around another question in StackOverflow.
Although the Android code is running fine it doesn't match with the output from the php script. It doesn't even match the first hash!
Note 1: On php hash('sha512',$input, $raw_output) returns raw binary output
Note 2: On java I tried to change the charset (UTF-8, ASCII) but it also didn't work.
Note 3: The code from the server can not be changed, so I would appreciate any answer regarding how to change my android code.
The first hash should be the same on the server and in Java. But then in the loop what gets appended to the digest is password{salt} in the PHP code, but only {salt} in the Java code.
For the lazy ones, one example better than a thousand words ;). I finally understood what was happening. The method update appends bytes to the digest, so when you append $password.{$salt} is the same as doing mda.update(password bytes) and the mda.digest("{$salt}" bytes. I do that answer because I was going crazy finding why it was not working and it was all in this answer.
Thanks guys.
This is the example that works in a Java Server:
public static String hashPassword(String password, String salt) throws Exception {
String result = password;
String appendedSalt = new StringBuilder().append('{').append(salt).append('}').toString();
String appendedSalt2 = new StringBuilder().append(password).append('{').append(salt).append('}').toString();
if(password != null) {
//Security.addProvider(new BouncyCastleProvider());
MessageDigest mda = MessageDigest.getInstance("SHA-512");
byte[] pwdBytes = password.getBytes("UTF-8");
byte[] saltBytes = appendedSalt.getBytes("UTF-8");
byte[] saltBytes2 = appendedSalt2.getBytes("UTF-8");
byte[] digesta = encode(mda, pwdBytes, saltBytes);
//result = new String(digesta);
System.out.println("first hash: " + new String(Base64.encode(digesta),"UTF-8"));
for (int i = 1; i < ROUNDS; i++) {
digesta = encode(mda, digesta, saltBytes2);
}
System.out.println("last hash: " + new String(Base64.encode(digesta),"UTF-8"));
result = new String(Base64.encode(digesta));
}
return result;
}
private static byte[] encode(MessageDigest mda, byte[] pwdBytes,
byte[] saltBytes) {
mda.update(pwdBytes);
byte [] digesta = mda.digest(saltBytes);
return digesta;
}