0

I have an Android client that sends JSON data to my spring boot application server.

In other to maintain a certain level of data integrity during communication I require that the original payload be hashed and then set into the outgoing payload along with the original JSON data.

This is what I mean (Android Client)

signUpUserRequest = new SignUpUserRequest(
                        lastName.getText().toString(),
                        Long.valueOf(phoneNumber.getText().toString().trim()),
                        emailAddress.getText().toString(),
                        Config.getAndroidId(this),
                        Type.INDIVIDUAL.toString(),
                        firstName.getText().toString(),
                        Config.ToBase64Encode(confirmPassword.getText().toString())
                );
                signUpUserRequest.setHash(Config.HashString(signUpUserRequest.toDataString().trim()));

Below is the the SignUpUserRequest Pojo (Setters and getters removed for brevity)

public class SignUpUserRequest {

private String firstName;

private String lastName;

private long phoneNumber;

private String type;

private String email;

private String password;

private String phoneId;

private String hash;

public SignUpUserRequest(String lastName, Long phoneNumber, String email, String phoneId, String type, String firstName, String password) {
    this.lastName = lastName;
    this.phoneNumber = phoneNumber;
    this.email = email;
    this.phoneId = phoneId;
    this.type = type;
    this.firstName = firstName;
    this.password = password;
}
.... setters and getters removed ....

public String toDataString() {
    return "SignUpUserRequest{" + "firstName=" + firstName + ", lastName=" + lastName + ", phoneNumber=" + phoneNumber + ", type=" + type + ", email=" + email + ", password=" + password + ", phoneId=" + phoneId + '}';
}

@Override
public String toString() {
    return "SignUpUserRequest{" + "firstName=" + firstName + ", lastName=" + lastName + ", phoneNumber=" + phoneNumber + ", type=" + type + ", email=" + email + ", password=" + password + ", phoneId=" + phoneId + ", hash=" + hash + '}';
}

The problem I have is when I send the JSON over to my spring boot application and then carry out an integrity check on the request data, the hash generated on the server side is always different from the hash generated on the client side.

Server Side Code (Spring boot Application)

 public ResponseEntity getSignupSessionJWTToken(@RequestBody SignUpUserRequest request) {
    Response response = new Response();
    String hashString = Config.HashString(request.toDataString().trim());

   if (hashString.equals(request.getHash())) {
   ...... do anything here .....
   }else{
   ..... integrity exception ....
   }

So My question is this the right approach? Am I doing something fundamentally wrong and how best can I achieve data integrity between my client and server.

Implementation of the hashString (Android)

public static String HashString(String text) {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(text.trim().getBytes("UTF-8"));
        Log.i("CONFIG", "HASHING TEXT = " + ToBase64Encode(hash));
        return ToBase64Encode(hash);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, ex);
        return "";
    } catch (UnsupportedEncodingException e) {
        Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, e);
        return "";
    }
}

public static String ToBase64Encode(byte[] originalInput) {
    return new String(Base64.encodeToString(originalInput, Base64.DEFAULT));
}

Implementation of the hashString (SpringBoot)

public static String HashString(String text) {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
        // System.out.println("Compare this = " + ToBase64Encode(hash));
        return Base64.getEncoder().encodeToString(hash);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(RiceHelper.class.getName()).log(Level.SEVERE, null, ex);
        return "";
    }
}
DaviesTobi alex
  • 610
  • 1
  • 9
  • 34
  • Can you explain the process of (or show the implementation) `Config.HashString`? Also, do note that a potential hacker can still change the hash in the payload according to the changed data and your server will validate it successfully. – Tejashwi Kalp Taru Jan 10 '19 at 08:58
  • @TejashwiKalpTaru I have added the `hashString` implementation – DaviesTobi alex Jan 10 '19 at 09:10
  • @TejashwiKalpTaru in response to your attacker comment: if I added salt to the data before it is hashed, would it then make it impossible for the attacker to alter the data and fool the system. – DaviesTobi alex Jan 11 '19 at 08:40
  • Well, the code to create and add salt will be available on your Android app, right? If so, one can easily decompile and get that code, and then play as they want. In my opinion, the salt will just make attack 2 or 5% more hard than before, not more than that. If you want some serious anti-tamper with your request and response, why don't you use RSA for that? Ping me if you need an elaborated sample – Tejashwi Kalp Taru Jan 11 '19 at 10:26
  • yes please @TejashwiKalpTaru I do need an elaborate sample – DaviesTobi alex Jan 11 '19 at 11:04
  • Here you go, https://github.com/tejashwikalptaru/encrypted-communication/tree/master/mobile – Tejashwi Kalp Taru Jan 11 '19 at 11:05
  • @TejashwiKalpTaru Thanks alot – DaviesTobi alex Jan 11 '19 at 11:06

2 Answers2

1

The base64 output at your client side is not Url safe since it uses Base64.DEFAULT flag while encoding. The "+" character in the encoded result is being converted to a "-" character when received at the server side. Hence the comparison is failing. To verify this you can the encoded base64 string you are sending from your client and the string you receive at the server side.

To make it Url safe, use -

byte[] encoded = Base64.encode(
strBytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
rupesh
  • 129
  • 1
  • 5
0

You could put a HashString method in SignUpUserRequest and use that on both ends. As has been mentioned, an attacker can alter the hash themselves as part of their attack and so defeat your check - whether that's an issue depends on the threats you're trying to mitigate. Take a look at DSA (Digital Secure Algorithm) if you want to properly secure your messages.

For most implementations, you can just send your registration over https and rely on the SSL connection for integrity.

user-10884042
  • 31
  • 1
  • 5
  • but if I added salt to the data before it is hashed, it would then make it impossible for the attacker to alter the data and fool the system. Would it not? – DaviesTobi alex Jan 11 '19 at 08:39
  • 'Impossible'... Impossible doesn't really exist in (non-quantum) crypto - you could use eliptic curve crypto and it's still 'possible' (if extremely hard) to crack. So it all depends on who you're trying to defeat. Yes, you could add a salt to your hash... ever heard of the phrase 'Security through obscurity is broken by design'? – user-10884042 Jan 12 '19 at 11:21
  • So, you've added a salt to your hash... could an attacker assume it's a hash with a salt? Yes. Could they try all the standard hash algos with a huge number of different types of salt length/val? Well, yes, they *could*... would they? Depends who they are and what you're hiding... – user-10884042 Jan 12 '19 at 11:22
  • Obviously, once they've cracked the scheme they can alter the data and stitch the hash back up to fool the system. You could then do something even more weird (obscure)... but you scheme is still fundamentally 'broken' (mathematically). What if (when) an attacker succeeds in breaking your scheme - how will you feel trying to defend the crypto scheme you used if it's one you made up yourself... – user-10884042 Jan 12 '19 at 11:22