Is it possible to create a PrivateKey from an encoded byte array alone, without knowing the algorithm in advance?
So in a way it's a twist on that question, that's not adressed in the answers.
Say I have a pair of keys generated this way:
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); // Could be "EC" instead of "RSA"
String privateKeyB64 = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
writePrivateKeyToSafeLocation(privateKeyB64);
To obtain a PrivateKey from the base64-encoded bytes, I can do this, but I have to know the algorithm family in advance:
String privateKeyB64 = readPrivateKeyFromSafeLocation();
EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyB64));
byte[] encodedKeyBytes = encodedKeySpec.getEncoded();
String algorithmFamily = "RSA"; // Can this be deduced from encodedKeyBytes?
PrivateKey key = KeyFactory.getInstance(algorithmFamily).generatePrivate(encodedKeySpec);
Unfortunately encodedKeySpec.getAlgorithm() returns null.
I'm pretty sure the algorithm ID is actually specified within those bytes in PKCS#8 format, but I'm not sure how to read the ASN.1 encoding.
Can I "sniff" the algorithm ID in a reliable way from those bytes?
It's OK to only support RSA and EC (algorithms supported by the JRE, without additional providers).
To get an idea of what I'm after, here is an attempt that seems to work empirically:
private static final byte[] EC_ASN1_ID = {42, -122, 72, -50, 61, 2, 1};
private static final byte[] RSA_ASN1_ID = {42, -122, 72, -122, -9, 13, 1, 1, 1};
private static final int EC_ID_OFFSET = 9;
private static final int RSA_ID_OFFSET = 11;
private static String sniffAlgorithmFamily(byte[] keyBytes) {
if (Arrays.equals(Arrays.copyOfRange(keyBytes, EC_ID_OFFSET, EC_ID_OFFSET + EC_ASN1_ID.length), EC_ASN1_ID)) {
return "EC";
}
if (Arrays.equals(Arrays.copyOfRange(keyBytes, RSA_ID_OFFSET, RSA_ID_OFFSET + RSA_ASN1_ID.length), RSA_ASN1_ID)) {
return "RSA";
}
throw new RuntimeException("Illegal key, this thingy requires either RSA or EC private key");
}
But I have no idea if that is safe to use. Maybe the IDs are not always at those offsets. Maybe they can be encoded in some other ways...
As suggested by James in comments, trying every supported algorithm would work, in a much safer way.
It's possible to dynamically obtain the list of such algorithms:
Set<String> supportedKeyPairAlgorithms() {
Set<String> algos = new HashSet<>();
for (Provider provider : Security.getProviders()) {
for (Provider.Service service : provider.getServices()) {
if ("KeyPairGenerator".equals(service.getType())) {
algos.add(service.getAlgorithm());
}
}
}
return algos;
}
And with that, just try them all to generate the KeyPair:
PrivateKey generatePrivateKey(String b64) {
byte[] bytes = Base64.getDecoder().decode(b64);
for (String algorithm : supportedKeyPairAlgorithms()) {
try {
LOGGER.debug("Attempting to decode key as " + algorithm);
return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(bytes));
} catch (NoSuchAlgorithmException e) {
LOGGER.warn("Standard algorithm " + algorithm + " not known by this Java runtime from outer space", e);
} catch (InvalidKeySpecException e) {
LOGGER.debug("So that key is not " + algorithm + ", nevermind", e);
}
}
throw new RuntimeException("No standard KeyFactory algorithm could decode your key");
}
How to generate secret key using SecureRandom.getInstanceStrong()?
With this code I can receive byte array with random values. Is there any easy way to generate key of a given length (256 bits, for example), type (int, String) and format (hex, bin, dec)?
package com.company;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class KeyGen {
public void generate() throws NoSuchAlgorithmException {
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] values = new byte[32]; // 256 bit
random.nextBytes(values);
StringBuilder sb = new StringBuilder();
for (byte b : values) {
sb.append(String.format("%02x", b));
}
System.out.print("Key: ");
System.out.println(sb.toString());
}
}
Output:
Key: 8fcea84897f48f575c22441ece4e7ddb43ac08cd2c1a83fca46c080768468059
Keys should be of a specific type, e.g. AES. They should preferably be kept inside a SecretKey instance or a similar Key derived class.
Keys for modern symmetric ciphers consist of bits. Usually you would not need a human/String representation of them (and this could actually harm security). Store them in a KeyStore or derive them from a password instead. If you do encode them then the representation format is inconsequential, as long as you don't loose your data during the conversions.
This is probably the best way to generate a strong AES key:
public class GenerateStrongAESKey {
public static SecretKey generateStrongAESKey(final int keysize) {
final KeyGenerator kgen;
try {
kgen = KeyGenerator.getInstance("AES");
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("AES key generator should always be available in a Java runtime", e);
}
final SecureRandom rng;
try {
rng = SecureRandom.getInstanceStrong();
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("No strong secure random available to generate strong AES key", e);
}
// already throws IllegalParameterException for wrong key sizes
kgen.init(keysize, rng);
return kgen.generateKey();
}
public static void main(String[] args) {
SecretKey strongAESKey = generateStrongAESKey(256);
// well, if you must have a human readable string, here it is
// but you've been warned
System.out.println(toHex(strongAESKey.getEncoded()));
}
private static String toHex(final byte[] data) {
final StringBuilder sb = new StringBuilder(data.length * 2);
for (byte b : data) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
}
Note: this requires the unlimited strength jurisdictional files for the Oracle runtime environment for keys > 128 bits.
I have two objects based on which SHA 256 Hash needs to be generated.
First value is a JSONObject
Second value is a String variable.
Ideally, what i need is
Hash hash= new Hash(JSONObject, String);
I couldn't find any hash generation methods which takes two values.
Could anyone help me with this?.
SHA 256 works on a byte array as input. You need to convert your JSONObject and your String to byte arrays, then calculate the SHA 256 hash on the concatenation of these byte arrays.
The proper way of generating a sha256 hashcode using key and value
public static String hashMac(String text, String secretKey)
throws SignatureException {
try {
Key sk = new SecretKeySpec(secretKey.getBytes(), HASH_ALGORITHM);
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk);
final byte[] hmac = mac.doFinal(text.getBytes());
return toHexString(hmac);
} catch (NoSuchAlgorithmException e1) {
// throw an exception or pick a different encryption method
throw new SignatureException(
"error building signature, no such algorithm in device "
+ HASH_ALGORITHM);
} catch (InvalidKeyException e) {
throw new SignatureException(
"error building signature, invalid key " + HASH_ALGORITHM);
}
}
public static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : bytes) {
formatter.format("%02x", b);
}
return sb.toString();
}
I am not an expert in cryptography and I am getting some interesting results when I use the encryption method below.
The server is .NET C# and the client runs JAVA. Basically, We encrypt credit card information and for the 12 credit cards I have, 11 works perfectly with the methods below.
However, one of the cards (real VISA credit CARD) the result returned by encrypt() and converted to hex has a negative symbol in the start of the string, like this:
-6d9830a52b2c3add7a78fd9897bca19d....., it fails when the server tries to decrypt it and I think it should be positive not negative based on this explanation RSA - Encryption with negative exponent
private static byte[] encrypt(String text, PublicKey pubRSA) throws Exception
{
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.ENCRYPT_MODE, pubRSA);
return cipher.doFinal(text.getBytes());
}
//Using this encryption method one card could not be decrypted by vPAY due to negative (exponential) symbol.
//It may have the same affect with other cards
public final static byte[] encrypt(String text)
{
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(Base64.decode(pkBase64));
PublicKey pk = keyFactory.generatePublic(x509Spec);
return encrypt(text, pk);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
Has anyone faced something like that and found a workaround?
I have tried three other algorithms with different KeySpec and the same publicKey (the source is a string in base64 format) but none of them could be decrypted by the server even with the cards the were working before...
UPDATE 1
This is how a convert the encrypted result in bytes to HEX:
public static String byteToHex(byte[] string)
{
try {
return String.format("%04x", new BigInteger(string));
} catch (Exception e) {
// TODO Auto-generated catch block
return null;
}
}
You should print out the hexadecimal string directly from byte[]. This can be done using the following code:
StringBuilder sb = new StringBuilder(data.length * 2);
for (int i = 0; i < data.length; i++) {
sb.append(String.format("%02X", data[i] & 0xFF));
}
return sb.toString();
There is no need to use BigInteger. In fact, it is dangerous to use BigInteger. One reason is the one you've already encountered: BigInteger conversion to/from byte[] is using signed big endian encoding by default. The other thing is that the output of the RSA signature (as integer) may be smaller than the modulus size in hexadecimals. This is why EJP's solution will fail now and then.
RSA output has been defined in bytes, as an unsigned big endian encoded in the same number of bits as the key size (using integer to octet string encoding in the standard documents).
public static String byteToHex(byte[] string)
A byte[] is not a string. It's a byte array. Don't confuse yourself with inappropriate variable names. String is not a container for binary data.
return String.format("%04x", new BigInteger(string));
Try return new BigInteger(1,string).toString(16), and have a look at the Javadoc to see why this works where new BigInteger(string) didn't.
I need to hash passwords for storage in a database. How can I do this in Java?
I was hoping to take the plain text password, add a random salt, then store the salt and the hashed password in the database.
Then when a user wanted to log in, I could take their submitted password, add the random salt from their account information, hash it and see if it equates to the stored hash password with their account information.
You can actually use a facility built in to the Java runtime to do this. The SunJCE in Java 6 supports PBKDF2, which is a good algorithm to use for password hashing.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
System.out.printf("salt: %s%n", enc.encodeToString(salt));
System.out.printf("hash: %s%n", enc.encodeToString(hash));
Here's a utility class that you can use for PBKDF2 password authentication:
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* Hash passwords for storage, and test passwords against password tokens.
*
* Instances of this class can be used concurrently by multiple threads.
*
* #author erickson
* #see StackOverflow
*/
public final class PasswordAuthentication
{
/**
* Each token produced by this class uses this identifier as a prefix.
*/
public static final String ID = "$31$";
/**
* The minimum recommended cost, used by default
*/
public static final int DEFAULT_COST = 16;
private static final String ALGORITHM = "PBKDF2WithHmacSHA1";
private static final int SIZE = 128;
private static final Pattern layout = Pattern.compile("\\$31\\$(\\d\\d?)\\$(.{43})");
private final SecureRandom random;
private final int cost;
public PasswordAuthentication()
{
this(DEFAULT_COST);
}
/**
* Create a password manager with a specified cost
*
* #param cost the exponential computational cost of hashing a password, 0 to 30
*/
public PasswordAuthentication(int cost)
{
iterations(cost); /* Validate cost */
this.cost = cost;
this.random = new SecureRandom();
}
private static int iterations(int cost)
{
if ((cost < 0) || (cost > 30))
throw new IllegalArgumentException("cost: " + cost);
return 1 << cost;
}
/**
* Hash a password for storage.
*
* #return a secure authentication token to be stored for later authentication
*/
public String hash(char[] password)
{
byte[] salt = new byte[SIZE / 8];
random.nextBytes(salt);
byte[] dk = pbkdf2(password, salt, 1 << cost);
byte[] hash = new byte[salt.length + dk.length];
System.arraycopy(salt, 0, hash, 0, salt.length);
System.arraycopy(dk, 0, hash, salt.length, dk.length);
Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
return ID + cost + '$' + enc.encodeToString(hash);
}
/**
* Authenticate with a password and a stored password token.
*
* #return true if the password and token match
*/
public boolean authenticate(char[] password, String token)
{
Matcher m = layout.matcher(token);
if (!m.matches())
throw new IllegalArgumentException("Invalid token format");
int iterations = iterations(Integer.parseInt(m.group(1)));
byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
byte[] check = pbkdf2(password, salt, iterations);
int zero = 0;
for (int idx = 0; idx < check.length; ++idx)
zero |= hash[salt.length + idx] ^ check[idx];
return zero == 0;
}
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations)
{
KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
try {
SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
return f.generateSecret(spec).getEncoded();
}
catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
}
catch (InvalidKeySpecException ex) {
throw new IllegalStateException("Invalid SecretKeyFactory", ex);
}
}
/**
* Hash a password in an immutable {#code String}.
*
* <p>Passwords should be stored in a {#code char[]} so that it can be filled
* with zeros after use instead of lingering on the heap and elsewhere.
*
* #deprecated Use {#link #hash(char[])} instead
*/
#Deprecated
public String hash(String password)
{
return hash(password.toCharArray());
}
/**
* Authenticate with a password in an immutable {#code String} and a stored
* password token.
*
* #deprecated Use {#link #authenticate(char[],String)} instead.
* #see #hash(String)
*/
#Deprecated
public boolean authenticate(String password, String token)
{
return authenticate(password.toCharArray(), token);
}
}
BCrypt is a very good library, and there is a Java port of it.
You could use Spring Security Crypto (has only 2 optional compile dependencies), which supports PBKDF2, BCrypt, SCrypt and Argon2 password encryption.
Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
String aCryptedPassword = argon2PasswordEncoder.encode("password");
boolean passwordIsValid = argon2PasswordEncoder.matches("password", aCryptedPassword);
SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
String sCryptedPassword = sCryptPasswordEncoder.encode("password");
boolean passwordIsValid = sCryptPasswordEncoder.matches("password", sCryptedPassword);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String bCryptedPassword = bCryptPasswordEncoder.encode("password");
boolean passwordIsValid = bCryptPasswordEncoder.matches("password", bCryptedPassword);
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
String pbkdf2CryptedPassword = pbkdf2PasswordEncoder.encode("password");
boolean passwordIsValid = pbkdf2PasswordEncoder.matches("password", pbkdf2CryptedPassword);
You can comput hashes using MessageDigest, but this is wrong in terms of security. Hashes are not to be used for storing passwords, as they are easily breakable.
You should use another algorithm like bcrypt, PBKDF2 and scrypt to store you passwords. See here.
You can use the Shiro library's (formerly JSecurity) implementation of what is described by OWASP.
It also looks like the JASYPT library has a similar utility.
In addition to bcrypt and PBKDF2 mentioned in other answers, I would recommend looking at scrypt
MD5 and SHA-1 are not recommended as they are relatively fast thus using "rent per hour" distributed computing (e.g. EC2) or a modern high end GPU one can "crack" passwords using brute force / dictionary attacks in relatively low costs and reasonable time.
If you must use them, then at least iterate the algorithm a predefined significant amount of times (1000+).
See here for more: https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
And here: http://codahale.com/how-to-safely-store-a-password/ (criticizes SHA family, MD5 etc for password hashing purposes)
And here: http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html (criticizes bcrypt and recommends scrypt and PBKDF2)
Fully agree with Erickson that PBKDF2 is the answer.
If you don't have that option, or only need to use a hash, Apache Commons DigestUtils is much easier than getting JCE code right:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html
If you use a hash, go with sha256 or sha512. This page has good recommendations on password handling and hashing (note it doesn't recommend hashing for password handling):
http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
While the NIST recommendation PBKDF2 has already been mentioned, I'd like to point out that there was a public password hashing competition that ran from 2013 to 2015. In the end, Argon2 was chosen as the recommended password hashing function.
There is a fairly well adopted Java binding for the original (native C) library that you can use.
In the average use-case, I don't think it does matter from a security perspective if you choose PBKDF2 over Argon2 or vice-versa. If you have strong security requirements, I recommend considering Argon2 in your evaluation.
For further information on the security of password hashing functions see security.se.
As of 2020, the most reliable password hashing algorithm in use, most likely to optimise its strength given any hardware, is Argon2id or Argon2i but not its Spring implementation.
The PBKDF2 standard includes the the CPU-greedy/computationally-expensive feature of the block cipher BCRYPT algo, and add its stream cipher capability. PBKDF2 was overwhelmed by the memory exponentially-greedy SCRYPT then by the side-channel-attack-resistant Argon2
Argon2 provides the necessary calibration tool to find optimized strength parameters given a target hashing time and the hardware used.
Argon2i is specialized in memory greedy hashing
Argon2d is specialized in CPU greedy hashing
Argon2id use both methods.
Memory greedy hashing would help against GPU use for cracking.
Spring security/Bouncy Castle implementation is not optimized and relatively week given what attacker could use.
cf: Spring doc Argon2 and Scrypt
The currently implementation uses Bouncy castle which does not exploit
parallelism/optimizations that password crackers will, so there is an
unnecessary asymmetry between attacker and defender.
The most credible implementation in use for java is mkammerer's one,
a wrapper jar/library of the official native implementation written in C.
It is well written and simple to use.
The embedded version provides native builds for Linux, windows and OSX.
As an example, it is used by jpmorganchase in its tessera security project used to secure Quorum, its Ethereum cryptocurency implementation.
Here is an example:
final char[] password = "a4e9y2tr0ngAnd7on6P১M°RD".toCharArray();
byte[] salt = new byte[128];
new SecureRandom().nextBytes(salt);
final Argon2Advanced argon2 = Argon2Factory.createAdvanced(Argon2Factory.Argon2Types.ARGON2id);
byte[] hash = argon2.rawHash(10, 1048576, 4, password, salt);
(see tessera)
Declare the lib in your POM:
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm</artifactId>
<version>2.7</version>
</dependency>
or with gradle:
compile 'de.mkammerer:argon2-jvm:2.7'
Calibration may be performed using de.mkammerer.argon2.Argon2Helper#findIterations
SCRYPT and Pbkdf2 algorithm might also be calibrated by writing some simple benchmark, but current minimal safe iterations values, will require higher hashing times.
Here you have two links for MD5 hashing and other hash methods:
Javadoc API: https://docs.oracle.com/javase/1.5.0/docs/api/java/security/MessageDigest.html
Tutorial: http://www.twmacinta.com/myjava/fast_md5.php
Among all the standard hash schemes, LDAP ssha is the most secure one to use,
http://www.openldap.org/faq/data/cache/347.html
I would just follow the algorithms specified there and use MessageDigest to do the hash.
You need to store the salt in your database as you suggested.
i leaned that from a video on udemy and edited to be stronger random password
}
private String pass() {
String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq##$%^&*" ;
char icon1;
char[] t=new char[20];
int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters
icon1=passswet.charAt(rand1);//will produce char with a special character
int i=0;
while( i <11) {
int rand=(int)(Math.random()*passswet.length());
//notice (int) as the original value of Math>random() is double
t[i] =passswet.charAt(rand);
i++;
t[10]=icon1;
//to replace the specified item with icon1
}
return new String(t);
}
}
Here is my simple PasswordHasher class that I made:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
public class PasswordHasher {
private static final String ALGO = "PBKDF2WithHmacSHA1";
private static final byte[] SALT = {
8, 8, 8, 8, 2,
8, 7, 7, 7, 2,
1, 1, 1, 1, 2,
11
};
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 128;
private SecretKeyFactory mFactory;
byte[] hashPassword(String password) {
SecretKeyFactory factory = getFactory();
if (factory != null) {
try {
KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
return factory.generateSecret(spec).getEncoded();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
return null;
}
boolean verifyPassword(String password, byte[] expectedHashResult) {
byte[] hashedPassword = hashPassword(password);
if (hashedPassword == null) {
// Log fail result
return false;
}
return Arrays.equals(hashedPassword, expectedHashResult);
}
private SecretKeyFactory getFactory() {
if (mFactory == null) {
try {
mFactory = SecretKeyFactory.getInstance(ALGO);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return mFactory;
}
}
import java.security.MessageDigest;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Base64;
import java.util.Properties;
public class Main{
public static void main(String[]a]{
//enter code here
}
public static String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] hash = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
hashPassword is the method that returns a hashed value when we pass a String as its parameter.
MessageDigest is the class that provides the interface for hashing password
getInstance is used to get the instance of a hashing algorithm such as MD-5,SHA 216,SHA-512 and so on.
The hashed Sting is in the forn of -byte []
In the return statement we convert the byte using ToString.