2

I am using PBKDF2 password hashing technique in my program. The sample program to which I am referring is in C. The program snippet is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "base64.h"

#define KEY_LENGTH      24
#define SEPARATOR       "$"
#define SALTLEN 12

#define USAGE() fprintf(stderr, "Usage: %s [-i iterations] [-p password]\n", progname)

int main(int argc, char **argv)
{
    int iterations = 901, rc, blen;
    unsigned char   saltbytes[SALTLEN];
    char *salt, *b64;
    unsigned char key[128];
    char *pw1, *pw2, *password;
    char *progname = argv[0];
    int c;
    int prompt;

    prompt = 1;

    while ((c = getopt(argc, argv, "i:p:")) != EOF) {
        switch (c) {
            case 'i':
                iterations = atoi(optarg);
                break;
            case 'p':
                pw1 = strdup(optarg);
                pw2 = strdup(optarg);   
                prompt = 0;
                break;
            default:
                exit(USAGE());
        }
    }

    argc -= optind - 1;
    argv += optind - 1;

    if (argc != 1) {
        exit(USAGE());
    }

    if ( prompt ) {
        pw1 = strdup(getpass("Enter password: "));
        pw2 = getpass("Re-enter same password: ");
    }

    if (strcmp(pw1, pw2) != 0) {
        fprintf(stderr, "Passwords don't match!\n");
        return (1);
    }

    password = pw1;

    rc = RAND_bytes(saltbytes, SALTLEN);
    if (rc == 0) {
        fprintf(stderr, "Cannot get random bytes for salt!\n");
        return 2;
    }

    base64_encode(saltbytes, SALTLEN, &salt);

#ifdef RAW_SALT
    PKCS5_PBKDF2_HMAC(password, strlen(password),
        (unsigned char *)saltbytes, SALTLEN,
        iterations,
        EVP_sha256(), KEY_LENGTH, key);
#else
    int saltlen;
    saltlen = strlen(salt);

    PKCS5_PBKDF2_HMAC(password, strlen(password),
        (unsigned char *)salt, saltlen,
        iterations,
        EVP_sha256(), KEY_LENGTH, key);
#endif


    blen = base64_encode(key, KEY_LENGTH, &b64);
    if (blen > 0) {
        printf("PBKDF2$%s$%d$%s$%s\n",
            "sha256",
            iterations,
            salt,
            b64);

        free(b64);
    }

    free(password);
    return 0;
}

And the C program output is as follows : PBKDF2$sha256$901$QLtznh6yjEs4a4Fl$uzp3QAEpFZsqBvCssnL1eXZFxCiKzV7P

I tried to replicate the same in Java, which is as follows:

public class NewPBKDF2 {

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String originalPassword = "A3E9907E59A6379DB6A9C2657D242A64886D5B21E3586B3D4C2B4E6329570A10";
        String generatedSecuredPasswordHash = generateStorngPasswordHash(originalPassword);
        System.out.println(generatedSecuredPasswordHash);
    }

    private static String generateStorngPasswordHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
        int iterations = 901;
        char[] chars = password.toCharArray();
        byte[] salt = getSalt();
        String salt1 = Base64.getEncoder().encodeToString(salt);
        int length = 24;




        PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, length * 8 );
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] hash = skf.generateSecret(spec).getEncoded();
        String hash1 = bytesToHex(hash);

        try {
            hash1 = Base64.getEncoder().encodeToString(hash1.getBytes("utf-8"));
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(NewPBKDF2.class.getName()).log(Level.SEVERE, null, ex);
        }


        return "PBKDF2$sha256$"+ iterations +"$"+salt1+"$"+hash1;
    }

    private static byte[] getSalt() throws NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");

        byte[] salt = new byte[12];


        sr.nextBytes(salt);
        return salt;
    }

   static char[] hexArray = "0123456789ABCDEF".toCharArray();
     public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

     public static 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;
    }

}

And the above program is giving me output as follows:

PBKDF2$sha256$901$v2DdYPk47r/I3aQJ$N0Y3MjZENzVEOTE5MDcxQkNEOEM5MTAyREQ2MjdEQ0NGNzIzRTZGN0ZCOUYzN0NF

Why I am getting different password length(the last part). I don't get it. Can you please help me find my mistake?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

1 Answers1

1

The answer to your question is laid on the foundations of various Character Encoding styles. And the dissimilarity in length of String is because of the use of different Encoding in both cases(C and Java Implementation).

You can note in your example that the hash1(because of which the dissimilarity in length is prominent) variable is encoded in UTF-8 in Java while the C used ASCII character encoding(Already mentioned in the ANSI standards).
Also, UTF-8 is a multi-byte encoding is a multi-byte encoding which uses between 1 and 4 bytes per character. You can also refer this:How many bytes does Unicode Character takes? which might be interesting and that is why the hashes are longer in length in java as compared to C.

Hope the answer helps to get some insights about the well-researched problem.