Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.rapidoid.crypto.Crypto Maven / Gradle / Ivy
/*-
* #%L
* rapidoid-commons
* %%
* Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.rapidoid.crypto;
import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.commons.Str;
import org.rapidoid.config.Conf;
import org.rapidoid.log.Log;
import org.rapidoid.u.U;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
@Authors("Nikolche Mihajlovski")
@Since("4.0.0")
public class Crypto extends RapidoidThing {
public static final SecureRandom RANDOM = new SecureRandom();
private static final AESCypherTool AES = new AESCypherTool();
private static final String HMAC_SHA_256 = "HmacSHA256";
static final int HMAC_KEY_LENGTH = 256; // bits
private static volatile CryptoKey secretKey;
static final byte[] DEFAULT_PBKDF2_SALT = {
0, -3, -76, 48, 23, 1, 43, -41, -120, 45, -92, -113, -100, 70, -68, -46, 96, -93, 15, 99
};
public static void reset() {
secretKey = null;
}
public static MessageDigest digest(String algorithm) {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw U.rte("Cannot find crypto algorithm: " + algorithm);
}
}
public static Cipher cipher(String transformation) {
try {
return Cipher.getInstance(transformation);
} catch (NoSuchAlgorithmException e) {
throw U.rte("Cannot find crypto algorithm: " + transformation);
} catch (NoSuchPaddingException e) {
throw U.rte("No such padding: " + transformation);
}
}
public static String bytesAsText(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
public static byte[] md5Bytes(byte[] bytes) {
MessageDigest md5 = digest("MD5");
md5.update(bytes);
return md5.digest();
}
public static String md5(byte[] bytes) {
return bytesAsText(md5Bytes(bytes));
}
public static String md5(String data) {
return md5(data.getBytes());
}
public static byte[] sha1Bytes(byte[] bytes) {
MessageDigest sha1 = digest("SHA-1");
sha1.update(bytes);
return sha1.digest();
}
public static String sha1(byte[] bytes) {
return bytesAsText(sha1Bytes(bytes));
}
public static String sha1(String data) {
return sha1(data.getBytes());
}
public static byte[] sha512Bytes(byte[] bytes) {
MessageDigest sha1 = digest("SHA-512");
sha1.update(bytes);
return sha1.digest();
}
public static String sha512(byte[] bytes) {
return bytesAsText(sha512Bytes(bytes));
}
public static String sha512(String data) {
return sha512(data.getBytes());
}
public static synchronized CryptoKey getSecretKey() {
if (secretKey == null) {
initSecret();
}
U.notNull(secretKey, "secret key");
return secretKey;
}
private static synchronized void initSecret() {
String secret = Conf.ROOT.entry("secret").str().getOrNull();
char[] src;
if (secret == null) {
Log.warn("!Application secret was not specified, generating random secret!");
src = Str.toHex(randomBytes(128)).toCharArray();
} else {
src = secret.toCharArray();
}
secretKey = CryptoKey.from(src);
}
public static String randomStr(int byteCount) {
return DatatypeConverter.printHexBinary(randomBytes(byteCount));
}
public static byte[] encrypt(byte[] data, CryptoKey key) {
try {
return AES.encrypt(data, key);
} catch (Exception e) {
throw U.rte(e);
}
}
public static byte[] decrypt(byte[] data, CryptoKey key) {
try {
return AES.decrypt(data, key);
} catch (Exception e) {
throw U.rte(e);
}
}
public static byte[] encrypt(byte[] data) {
return encrypt(data, getSecretKey());
}
public static byte[] decrypt(byte[] data) {
return decrypt(data, getSecretKey());
}
public static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int length) {
try {
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, length);
return getPBKDFInstance().generateSecret(keySpec).getEncoded();
} catch (Exception e) {
throw U.rte(e);
}
}
public static String passwordHash(char[] password) {
return passwordHash(password, 100000);
}
public static String passwordHash(char[] password, int iterations) {
return passwordHash(password, iterations, randomBytes(20));
}
public static String passwordHash(char[] password, int iterations, byte[] salt) {
byte[] hash = pbkdf2(password, salt, iterations, 256);
return Str.toBase64(hash) + "$" + Str.toBase64(salt) + "$" + iterations;
}
public static boolean passwordMatches(char[] password, String saltedHash) {
String[] parts = saltedHash.split("\\$");
if (parts.length != 3) {
return false;
}
byte[] expectedHash = Str.fromBase64(parts[0]);
byte[] salt = Str.fromBase64(parts[1]);
int iterations;
try {
iterations = U.num(parts[2]);
} catch (Exception e) {
return false;
}
byte[] realHash = pbkdf2(password, salt, iterations, 256);
return Arrays.equals(expectedHash, realHash);
}
public static boolean passwordMatches(String password, String saltedHash) {
return passwordMatches(password.toCharArray(), saltedHash);
}
public static byte[] randomBytes(int byteCount) {
byte[] bytes = new byte[byteCount];
RANDOM.nextBytes(bytes);
return bytes;
}
public static byte[] hmac(byte[] data, byte[] secret, byte[] salt) throws Exception {
Mac m = Mac.getInstance(HMAC_SHA_256);
m.init(new SecretKeySpec(secret, HMAC_SHA_256));
return m.doFinal(data);
}
public static boolean hmacMatches(byte[] hmac, byte[] data, byte[] secret, byte[] salt) throws Exception {
return MessageDigest.isEqual(hmac, Crypto.hmac(data, secret, salt));
}
private static SecretKeyFactory getPBKDFInstance() throws NoSuchAlgorithmException {
try {
return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
} catch (NoSuchAlgorithmException e) {
return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
}
}
}