I want to encrypt the final grades of students in PHP and decrypt it in Android Java. I referred my codes here but it returns wrong value.
This is my PHP encryption function
function encode5t($value1){
for($i=0;$i<3;$i++)
{
$value1=base64_encode(strrev($value1));
}
return $value1;
}
Call the function:
foreach ($rows as $row){
$post["cSemester"] = $row["cSemester"];
$post["cSchoolYear"] = $row["cSchoolYear"];
$post["cSubjectCode"] = $row["cSubjectCode"];
$post["cDescription"] = $row["cDescription"];
$post["nFGrade"] = encode5t($row["nFGrade"]);
$post["nCGrade"] = $row["nCGrade"];
$post["nCredit"] = $row["nCredit"];
//update our response JSON data
array_push($response["posts"], $post);
}
echo json_encode($response);
And this is my Java code.
vGrades = json.getJSONArray(TAG_POSTS);
for (int i = 0; i < vGrades.length(); i++) {
JSONObject c = vGrades.getJSONObject(i);
String cSemester = c.getString(TAG_SEMESTER);
String cSchoolYear = c.getString(TAG_SCHOOLYEAR);
String cSubjectCode = c.getString(TAG_SUBJECTCODE);
String cDescription = c.getString(TAG_DESCRIPTION);
String encrypted_string = c.getString(TAG_FINALGRADE);
String nCGrade = c.getString(TAG_COMPLETIONGRADE);
String nCredit = c.getString(TAG_CREDIT);
HashMap<String, String> map = new HashMap<String, String>();
try{
byteArray = Base64.decode(encrypted_string, Base64.DEFAULT);
decrypt = new String(byteArray, "UTF-8");
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
} // this is where I want to decrypt it.
nFGrade = decrypt;
map.put(TAG_SEMESTER, cSemester);
map.put(TAG_SCHOOLYEAR, cSchoolYear);
map.put(TAG_SUBJECTCODE, cSubjectCode);
map.put(TAG_DESCRIPTION, cDescription);
map.put(TAG_FINALGRADE, nFGrade);
map.put(TAG_COMPLETIONGRADE, nCGrade);
map.put(TAG_CREDIT, nCredit);
ViewGrades.add(map);
}
The PHP encryption is running .. but when i decrypt it the system returns another encrypted value .. for example the fGrade is 1.0.
the PHP encypted String value is: "PT1RVERSRGU="
the Java decrypted value is: "==QTDRDe"
where did I go wrong? I need help please ..thanks guys!
Base64 has no key, anyone can decode it, change it and replace it. But that may be all you need if you just want to thwart the casual user. You need to define who you are protecting against knowing that every scheme can be compromised.
To encrypt data use AES. Encryption is not easy to get correct. The PHP mcrypt encryption function is flawed, do not use it. Consider RNCryptor-php, it provides a full solution including authentication and key derivation.
Related
I am trying to connect my Metamask wallet to my Java Spring-Boot backend. I was trying to follow the example here. I am able to autogenerate the nonce and receive the wallet ID without a problem. I am trying to verify the signed nonce from the Wallet on the server to make sure that the sender is indeed who they say they are. However, I am unable to find any documentation on Web3J to do this.
Is web3j not the right package to use for this? The example shows how to do the verification on NodeJS based on javascript but I don't find any example on how to do this on Java.
My understanding is that the public key is the wallet ID itself and that the message is the nonce signed by the private key of the wallet which is not shared for obvious reasons. According to this, I would need to "decrypt" the message using the public key and see if the decrypted message is same as the nonce that the backend sent to Metamask to sign. Is this correct?
Here is my code to create and send the nonce to UI:
public User findUserByPublicAddress(String publicWalletId) {
User u = userRepository.findByPublicWalletId(publicWalletId);
if(u == null) {
u = new User("", "", "", null, publicWalletId, "");
String nonce = StringUtil.generateRandomAlphaNumericString();
u.setNonce(nonce);
userRepository.saveAndFlush(u);
}
return u;
}
Here, I see if the user is already in my system and if they are not, then I just create a temporary user with a random nonce generated and saved in the DB. This nonce is sent to the UI for Metamask to sign. However, I am not sure how to do the verification part of it.
I was able to figure this out finally. My initial understanding was incorrect. I was not supposed to attempt to decrypt the message to retrieve the nonce. Rather I needed to use the nonce to see if I can retrieve the public key of the private key used to sign the message and see if that public key retrieved matches the wallet ID.
The algorithm:
Receive the signed message and the wallet ID from the client
Retrieve the nonce sent to the client with the same wallet ID
Generate the hash of the nonce
Generate the signature data from the message. This basically retrieves the V, R and S and. R and S are the outputs of the ECDSA Signature and V is the Recovery ID.
Using the ECDSA Signature and Hash of the Nonce, generate the possible public Key that was used to sign the message. At max, one will be able to generate 4 possible public keys for this message.
Check if any of the generated keys match public wallet ID that the client sent. If it matches, then we have a positive match. Generate the JWT and respond to the client. If not, we know that the nonce was not signed by the Metamask wallet we expected.
The Code:
Here is a sample code for UI (JavaScript and HTML):
web3.eth.sign(
web3.utils.sha3(nonce),
window.userWalletAddress)
.then((message) => {
console.log(message)
data['message'] = message // BODY
var xmlReq = new XMLHttpRequest();
xmlReq.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
response = this.responseText
console.log(response)
}
};
xmlReq.open("POST", "/api/users/login", true)
xmlReq.setRequestHeader('Content-Type', 'application/json')
xmlReq.send(JSON.stringify(data))
})
The web3.eth.sign() takes the message to be signed and takes the wallet ID that is signing it. This is then sent to the backend. In the backend:
public User signin(UserLoginDTO loginDetails, HttpServletResponse response) {
try {
// Get the wallet ID and signed message from the body stored in the DTO
String publicWalletId = loginDetails.getPublicWalletId();
String message = loginDetails.getMessage();
// Find the nonce from the DB that was used to sign this message
User user = userRepository.findByPublicWalletId(publicWalletId);
String nonce = user.getNonce();
// Generate the HASH of the Nonce
byte[] nonceHash = Hash.sha3(nonce.getBytes()) // org.web3j.crypto.Hash
// Generate the Signature Data
byte[] signatureBytes = Numeric.hexStringToByteArray(message); // org.web3j.utils.Numeric
byte v = (byte) ((signatureBytes[64] < 27) ? (signatureBytes[64] + 27) : signatureBytes[64]);
byte[] r = Arrays.copyOfRange(signatureBytes, 0, 32);
byte[] s = Arrays.copyOfRange(signatureBytes, 32, 64);
SignatureData signatureData = new SignatureData(v, r, s); // org.web3j.crypto.Sign.SignatureData
// Generate the 4 possible Public Keys
List<String> recoveredKeys = new ArrayList<>();
for(int i = 0; i < 4; i++) {
BigInteger r = new BigInteger(1, signatureData.getR());
BigInteger s = new BigInteger(1, signatureData.getS());
ECDSASignature ecdsaSignature = new ECDSASignature(r, s);
BigInteger recoveredKey = Sign.recoverFromSignature((byte)i, ecdsaSignature, nonceHash);
if(recoveredKey != null) {
recoveredKeys.add("0x" + Keys.getAddressFromKey(recoveredKey)); // org.web3j.crypto.Keys
}
}
// Check if one of the generated Keys match the public wallet ID.
for(String recoveredKey : recoveredKeys) {
if(recoveredKey.equalsIgnoreCase(publicWalletId)) {
// Add Code here to create the JWT and add that to your HttpServletResponse. Not shown here.
return user;
}
}
throw new CustomException("Message Sign Invalid", HttpStatus.UNAUTHORIZED);
}
catch (Exception ex) {
// Custom Error Handling.
}
}
So, PHP code:
$result = hash("whirlpool","xxx".$password."zzz");
JAVA:
import gnu.crypto.Registry;
import gnu.crypto.hash.HashFactory;
import gnu.crypto.hash.IMessageDigest;
public class WhirlpoolHash {
String result;
WhirlpoolHash(String pass) {
String to_encode = "xxx"+pass+"zzz";
IMessageDigest old_encoder = HashFactory.getInstance(Registry.WHIRLPOOL_HASH);
byte[] input = to_encode.getBytes();
old_encoder.update(input, 0, input.length);
byte[] digest = old_encoder.digest();
this.result = gnu.crypto.util.Util.toString(digest).toLowerCase();
}
public String Get() {
return this.result;
}
}
And the result vars are different. I need java class to return the same value as php does.
I have passwords stored in MySQL DB UTF-8 encoded generated by PHP and need to compare it with data sent by JavaFX app.
Of course i can send unencrypted password, and do it with php but I dont whant to.
So Java example for encrypting pwd with whirlpool algorithm using gnu-crypto jar was an answer.
I dont know why but jonelo.jacksum.JacksumAPI gives the result same as PHP.
Late response, but in case it helps someone else.
I had almost the exact same issue and used Bouncy Castle in Java. After some trial and error, I got the hashes with Whirlpool to match my PHP hash, which looked similar to yours. Assuming you pass in the password:
WhirlpoolDigest messageDigest = new WhirlpoolDigest();
final String convertedHash = "xxx" + password + "yyy";
messageDigest.reset();
final byte[] bytes = convertedHash.getBytes();
messageDigest.update(bytes, 0, bytes.length);
byte[] hash = new byte[messageDigest.getDigestSize()];
messageDigest.doFinal(hash, 0);
System.out.println(Hex.toHexString(hash));
The biggest issue for me was the final steps - the doFinal() and the Hex.toHexString() ...
My maven dependency looked like this:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.64</version>
</dependency>
I'm using Play framework to develop consumer for Instagram real-time API. But still could not perform x-hub-signature verification properly. So, how can we perform Instagram x-hub-signature verification using Java and Play framework?
Here is my current code:
From the Play framework, I obtain the JSON payload using this method:
public static Result receiveInstaData(){
JsonNode json = request().body().asJson();
//obtain the x-hub-signature from the header
//obtain the corresponding client secret
VerificationResult verificationResult =
SubscriptionUtil.verifySubscriptionPostSignature(
clientSecret, json.toString(), xHubSignature);
if(verificationResult.isSuccess()){
//do something
}
}
Then inside the SubscriptionUtil, I perform verification using this following code:
public static VerificationResult verifySubscriptionPostSignature(String clientSecret, String rawJsonData, String xHubSignature) {
SecretKeySpec keySpec;
keySpec = new SecretKeySpec(clientSecret.getBytes("UTF-8"), HMAC_SHA1);
Mac mac;
mac = Mac.getInstance(HMAC_SHA1);
mac.init(keySpec);
byte[] result;
result = mac.doFinal(rawJsonData.getBytes("UTF-8"));
String encodedResult = Hex.encodeHexString(result);
return new VerificationResult(encodedResult.equals(xHubSignature), encodedResult);
}
I created a standalone Python script that copies the instagram-python implementation and both of them produce the same results for the same clientSecret and jsonString. Maybe I should provide with raw binary data instead of String.
If let's say we need a raw binary data for JSON request, then I need to create my custom BodyParser to parse the JSON request to raw binary data[5]
References:
[1-4]http://pastebin.com/g4uuDwzn (SO doesn't allow me to post more than 2 links, so I put all the references here. The links contain the signature verification in Ruby, Python and PHP)
[5]https://groups.google.com/forum/#!msg/play-framework/YMQb6yeDH5o/jU8FD--yVPYJ
[6]My standalone python script:
#! /usr/bin/env python
import sys
import hmac
import hashlib
hc_client_secret = "myclientsecret"
hc_raw_response = "[{\"subscription_id\":\"1\",\"object\":\"user\",\"object_id\":\"1234\",\"changed_aspect\":\"media\",\"time\":1297286541},{\"subscription_id\":\"2\",\"object\":\"tag\",\"object_id\":\"nofilter\",\"changed_aspect\":\"media\",\"time\":1297286541}]"
client_secret = hc_client_secret
raw_response = hc_raw_response
if len(sys.argv) != 3:
print 'Usage verify_signature <client_secret> <raw_response>.\nSince the inputs are invalid, use the hardcoded value instead!'
else:
client_secret = sys.argv[1]
raw_response = sys.argv[2]
print "client_secret = " + client_secret
print "raw_response = " + raw_response
digest = hmac.new(client_secret.encode('utf-8'), msg=raw_response.encode('utf-8'), digestmod=hashlib.sha1).hexdigest()
print digest
Finally I managed to find the solution. For the Controller in Play Framework, we need to use BodyParser.Raw so the we can extract the payload request as raw data, i.e. array of bytes.
Here's the code for the controller in Play Framework:
#BodyParser.Of(BodyParser.Raw.class)
public static Result receiveRawInstaData(){
Map<String, String[]> headers = request().headers();
RawBuffer jsonRaw = request().body().asRaw();
if(jsonRaw == null){
logger.warn("jsonRaw is null. Something is wrong with the payload");
return badRequest("Expecting serializable raw data");
}
String[] xHubSignature = headers.get(InstaSubscriptionUtils.HTTP_HEADER_X_HUB_SIGNATURE);
if(xHubSignature == null){
logger.error("Invalid POST. It does not contain {} in its header", InstaSubscriptionUtils.HTTP_HEADER_X_HUB_SIGNATURE);
return badRequest("You are not Instagram!\n");
}
String json;
byte[] jsonRawBytes;
jsonRawBytes = jsonRaw.asBytes();
json = new String(jsonRawBytes, StandardCharsets.UTF_8);
try {
String clientSecret = InstaSubscriptionUtils.getClientSecret(1);
VerificationResult verificationResult = SubscriptionUtil.verifySubscriptionPostRequestSignature
(clientSecret,jsonRawBytes, xHubSignature[0]);
if(verificationResult.isSuccess()){
logger.debug("Signature matches!. Received signature: {}, calculated signature: {}", xHubSignature[0], verificationResult.getCalculatedSignature());
}else{
logger.error("Signature doesn't match. Received signature: {}, calculated signature: {}", xHubSignature[0], verificationResult.getCalculatedSignature());
return badRequest("Signature does not match!\n");
}
} catch (InstagramException e) {
logger.error("Instagram exception.", e);
return internalServerError("Internal server error. We will attend to this problem ASAP!");
}
logger.debug("Received xHubSignature: {}", xHubSignature[0]);
logger.info("Sucessfully received json data: {}", json);
return ok("OK!");
}
And for the code for method verifySubscriptionPostRequestSignature in SubscriptionUtil
public static VerificationResult verifySubscriptionPostRequestSignature(String clientSecret, byte[] rawJsonData, String xHubSignature) throws InstagramException{
SecretKeySpec keySpec;
keySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), HMAC_SHA1);
Mac mac;
try {
mac = Mac.getInstance(HMAC_SHA1);
mac.init(keySpec);
byte[] result = mac.doFinal(rawJsonData);
String encodedResult = Hex.encodeHexString(result);
return new VerificationResult(encodedResult.equals(xHubSignature), encodedResult);
} catch (NoSuchAlgorithmException e) {
throw new InstagramException("Invalid algorithm name!", e);
} catch (InvalidKeyException e){
throw new InstagramException("Invalid key: " + clientSecret, e);
}
}
I implemented this solution in jInstagram, here is the link to the source code: SubscriptionUtil
according to the specification: http://wiki.theory.org/BitTorrentSpecification
info_hash: urlencoded 20-byte SHA1 hash of the value of the info key from the Metainfo file. Note that the value will be a bencoded dictionary, given the definition of the info key above.
torrentMap is my dictionary, I get the info key which is another dictionary, I calculate the hash and I URLencode it.
But I always get an invalid info_hash message when I try to send it to the tracker.
This is my code:
public String GetInfo_hash() {
String info_hash = "";
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
try {
out = new ObjectOutputStream(bos);
out.writeObject(torrentMap.get("info"));
byte[] bytes = bos.toByteArray(); //Map => byte[]
MessageDigest md = MessageDigest.getInstance("SHA1");
info_hash = urlencode(md.digest(bytes)); //Hashing and URLEncoding
out.close();
bos.close();
} catch (Exception ex) { }
return info_hash;
}
private String urlencode(byte[] bs) {
StringBuffer sb = new StringBuffer(bs.length * 3);
for (int i = 0; i < bs.length; i++) {
int c = bs[i] & 0xFF;
sb.append('%');
if (c < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(c));
}
return sb.toString();
}
This is almost certainly the problem:
out = new ObjectOutputStream(bos);
out.writeObject(torrentMap.get("info"));
What you're going to be hashing is the Java binary serialization format of the value of torrentMap.get("info"). I find it very hard to believe that all BitTorrent programs are meant to know about that.
It's not immediately clear to me from the specification what the value of the "info" key is meant to be, but you need to work out some other way of turning it into a byte array. If it's a string, I'd expect some well-specified encoding (e.g. UTF-8). If it's already binary data, then use that byte array directly.
EDIT: Actually, it's sounds like the value will be a "bencoded dictionary" as per your quote, which looks like it will be a string. Quite how you're meant to encode that string (which sounds like it may include values which aren't in ASCII, for example) before hashing it is up for grabs. If your sample strings are all ASCII, then using "ASCII" and "UTF-8" as the encoding names for String.getBytes(...) will give the same result anyway, of course...
What I am trying to do is read from a text file where each line has the path to a file and then space for a separator and a hash that accompanies it. So I call checkVersion() and loadStrings(File f_) returns a String[], one place for each line. When I try to check the hashes however I end up with something that isn't even hex and is twice as long as it should be, it's probably something obvious that my eyes are just overlooking. The idea behind this is an auto-update for my game to save bandwidth, thanks for your time.
The code is fixed, here is the final version if anyone else has this issue, thanks a lot everyone.
void checkVersion() {
String[] v = loadStrings("version.txt");
for(int i=0; i<v.length; i++) {
String[] piece = split(v[i], " "); //BREAKS INTO FILENAME, HASH
println("Checking "+piece[0]+"..."+piece[1]);
if(checkHash(piece[0], piece[1])) {
println("ok!");
} else {
println("NOT OKAY!");
//CONTINUE TO DOWNLOAD FILE AND THEN CALL CHECKVERSION AGAIN
}
}
}
boolean checkHash(String path_, String hash_) {
return createHash(path_).equals(hash_);
}
byte[] messageDigest(String message, String algorithm) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance(algorithm);
md.update(message.getBytes());
return md.digest();
} catch(java.security.NoSuchAlgorithmException e) {
println(e.getMessage());
return null;
}
}
String createHash(String path_) {
byte[] md5hash = messageDigest(new String(loadBytes(path_)),"MD5");
BigInteger bigInt = new BigInteger(1, md5hash);
return bigInt.toString(16);
}
The String.getBytes() method returns the bytes that represent the character encodings for the string. It doesn't parse it into bytes that represent a number in some arbitrary radix. For example "AA".getBytes() would yield you 0x41 0x41 on windows, not 10101010b, which is what it appears you were expecting? To get that you could, for example Byte.parseByte("AA", 16)
The library you're using to create hashes probably has a method for taking back in its own string representation. How to convert back depends on the representation, which you didn't give us.
use following code to convert hash bytes to string
//byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
String output = bigInt.toString(16);
System.out.println("MD5: " + output);
for more information