All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.osgl.util.Crypto Maven / Gradle / Ivy

The newest version!
/* 
 * Copyright (C) 2013 The Java Tool project
 * Gelin Luo 
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
*/
package org.osgl.util;

/*-
 * #%L
 * Java Tool
 * %%
 * Copyright (C) 2014 - 2017 OSGL (Open Source General Library)
 * %%
 * 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%
 */

import com.alibaba.fastjson.JSON;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import static sun.security.x509.CertificateAlgorithmId.ALGORITHM;

/**
 * Cryptography utils. Comes from play!framework under apache license
 */
public enum Crypto {
    ;

    private static CryptoService svc;
    private static final String ALGO = "AES/CBC/PKCS5Padding";

    public static void setCryptoService(CryptoService service) {
        SecurityManager security = System.getSecurityManager();
       	if (security != null) {
       	    security.checkPermission(new RuntimePermission("OSGL.SetCryptoService"));
       	}
        svc = service;
    }

    /**
     * Define a hash type enumeration for strong-typing
     */
    public enum HashType {
        MD5("MD5"),
        SHA1("SHA-1"),
        SHA256("SHA-256"),
        SHA512("SHA-512");
        private String algorithm;

        HashType(String algorithm) {
            this.algorithm = algorithm;
        }

        @Override
        public String toString() {
            return this.algorithm;
        }
    }

    /**
     * Set-up MD5 as the default hashing algorithm
     */
    private static final HashType DEFAULT_HASH_TYPE = HashType.MD5;

    static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static char[] generatePassword() {
        return generatePassword(null);
    }

    public static char[] generatePassword(int len) {
        return generatePassword(new char[len]);
    }

    public static char[] generatePassword(char[] ca) {
        return generatePassword(ca, new SecureRandom());
    }

    private static char[] generatePassword(char[] ca, Random r) {
        int len = null == ca ? 0 : ca.length;
        if (0 == len) {
            len = Math.abs(r.nextInt(6)) + 12;
            ca = new char[len];
        }
        char[] chars = S._COMMON_CHARS_;
        int charsLen = S._COMMON_CHARS_LEN_;
        while (len-- > 0) {
            int i = r.nextInt(charsLen);
            ca[len] = chars[i];
        }
        return ca;
    }

    /**
     * Sign a message with a key
     *
     * @param message The message to sign
     * @param key     The key to use
     * @return The signed message (in hexadecimal)
     */
    public static String sign(String message, byte[] key) {

        if (key.length == 0) {
            return message;
        }

        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
            mac.init(signingKey);
            byte[] messageBytes = message.getBytes("utf-8");
            byte[] result = mac.doFinal(messageBytes);
            int len = result.length;
            char[] hexChars = new char[len * 2];


            for (int charIndex = 0, startIndex = 0; charIndex < hexChars.length; ) {
                int bite = result[startIndex++] & 0xff;
                hexChars[charIndex++] = HEX_CHARS[bite >> 4];
                hexChars[charIndex++] = HEX_CHARS[bite & 0xf];
            }
            return new String(hexChars);
        } catch (UnsupportedEncodingException ex) {
            throw E.encodingException(ex);
        } catch (Exception ex) {
            throw E.unexpected(ex);
        }

    }

    /**
     * Create a password hash using the default hashing algorithm
     *
     * @param input The password
     * @return The password hash
     */
    public static String passwordHash(String input) {
        return passwordHash(input, DEFAULT_HASH_TYPE);
    }

    /**
     * Create a password hash using specific hashing algorithm
     *
     * @param input    The password
     * @param hashType The hashing algorithm
     * @return The password hash
     */
    public static String passwordHash(String input, HashType hashType) {
        try {
            MessageDigest m = MessageDigest.getInstance(hashType.toString());
            byte[] out = m.digest(input.getBytes());
            return new String(Base64.encode(out));
        } catch (NoSuchAlgorithmException e) {
            throw E.unexpected(e);
        }
    }

    /**
     * Create a password hash using the default hashing algorithm
     *
     * @param input The password
     * @return The password hash
     */
    public static char[] passwordHash(char[] input) {
        return passwordHash(input, DEFAULT_HASH_TYPE);
    }

    /**
     * Create a password hash using specific hashing algorithm
     *
     * @param input    The password
     * @param hashType The hashing algorithm
     * @return The password hash
     */
    public static char[] passwordHash(char[] input, HashType hashType) {
        try {
            MessageDigest m = MessageDigest.getInstance(hashType.toString());
            byte[] out = m.digest(toByte(input));
            return Base64.encode(out);
        } catch (NoSuchAlgorithmException e) {
            throw E.unexpected(e);
        }
    }

    /**
     * This method is deprecated. Please use {@link #encryptAES(String, byte[])} instead
     *
     * Encrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      The String to encrypt
     * @param privateKey The key used to encrypt
     * @return An hexadecimal encrypted string
     */
    @Deprecated
    public static String encryptAES(String value, String privateKey) {
        return encryptAES(value, privateKey.getBytes(Charsets.UTF_8));
    }

    /**
     * Encrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      The String to encrypt
     * @param privateKey The key used to encrypt
     * @return An hexadecimal encrypted string
     */
    public static String encryptAES(String value, byte[] privateKey) {
        if (null == value) {
            return null;
        }
        try {
            if (null != svc) return svc.encrypt(value, privateKey);
            MessageDigest md = MessageDigest.getInstance("SHA-384");
            md.update(privateKey);
            byte[] ba = md.digest();
            byte[] key = new byte[32], iv = new byte[16];
            System.arraycopy(ba, 0, key, 0, 32);
            SecureRandom sr = new SecureRandom();
            sr.nextBytes(iv);

            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            ba = cipher.doFinal(value.getBytes(Charsets.UTF_8));

            byte[] ba2 = new byte[ba.length + 16];
            System.arraycopy(ba, 0, ba2, 0, ba.length);
            System.arraycopy(iv, 0, ba2, ba.length, 16);

            return Codec.byteToHexString(ba2);
        } catch (Exception ex) {
            throw E.unexpected(ex);
        }
    }

    /**
     * This method is deprecated, please use {@link #encryptAES(String, byte[])} instead
     *
     * Encrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      The String to encrypt
     * @param privateKey The key used to encrypt
     * @param salt       The salt
     * @return An hexadecimal encrypted string
     */
    @Deprecated
    public static String encryptAES(String value, String privateKey, String salt) {
        return encryptAES(value, privateKey.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8));
    }

    /**
     * Encrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      The String to encrypt
     * @param privateKey The key used to encrypt
     * @param salt       The salt
     * @return An hexadecimal encrypted string
     */
    public static String encryptAES(String value, byte[] privateKey, byte[] salt) {
        if (null == value) {
            return null;
        }
        try {
            if (null != svc) return svc.encrypt(value, privateKey, salt);
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(privateKey);
            byte[] key = md.digest();
            md = MessageDigest.getInstance("SHA-1");
            md.update(salt);
            byte[] tmp = md.digest();
            byte[] iv = new byte[16];
            System.arraycopy(tmp, 0, iv, 0, 16);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            byte[] ba = cipher.doFinal(value.getBytes("utf-8"));
            return Codec.byteToHexString(ba);
        } catch (Exception ex) {
            throw E.unexpected(ex);
        }
    }


    /**
     * This method is deprecated. please use {@link #decryptAES(String, byte[])} instead
     *
     * Decrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      An hexadecimal encrypted string
     * @param privateKey The key used to encrypt
     * @return The decrypted String
     */
    @Deprecated
    public static String decryptAES(String value, String privateKey) {
        return decryptAES(value, privateKey.getBytes(Charsets.UTF_8));
    }

    /**
     * Decrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      An hexadecimal encrypted string
     * @param privateKey The key used to encrypt
     * @return The decrypted String
     */
    public static String decryptAES(String value, byte[] privateKey) {
        if (null == value) {
            return null;
        }
        try {
            if (null != svc) return svc.decrypt(value, privateKey);
            byte[] ba0 = Codec.hexStringToByte(value);
            byte[] baVal = new byte[ba0.length - 16];
            System.arraycopy(ba0, 0, baVal, 0, ba0.length - 16);
            MessageDigest md = MessageDigest.getInstance("SHA-384");
            md.update(privateKey);
            byte[] ba = md.digest();
            byte[] key = new byte[32], iv = new byte[16];
            System.arraycopy(ba, 0, key, 0, 32);
            System.arraycopy(ba0, ba0.length - 16, iv, 0, 16);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            ba = cipher.doFinal(baVal);
            return new String(ba);
        } catch (Exception ex) {
            throw E.unexpected(ex);
        }
    }


    /**
     * This method is deprecated. please use {@link #decryptAES(String, byte[], byte[])} instead
     *
     * Decrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      An hexadecimal encrypted string
     * @param privateKey The key used to encrypt
     * @param salt       the salt
     * @return The decrypted String
     */
    @Deprecated
    public static String decryptAES(String value, String privateKey, String salt) {
        return decryptAES(value, privateKey.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8));
    }

    /**
     * Decrypt a String with the AES encryption standard. Private key must have a length of 16 bytes
     *
     * @param value      An hexadecimal encrypted string
     * @param privateKey The key used to encrypt
     * @param salt       the salt
     * @return The decrypted String
     */
    public static String decryptAES(String value, byte[] privateKey, byte[] salt) {
        if (null == value) {
            return null;
        }
        try {
            if (null != svc) return svc.decrypt(value, privateKey, salt);
            byte[] baVal = Codec.hexStringToByte(value);
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(privateKey);
            byte[] key = md.digest();
            md = MessageDigest.getInstance("SHA-1");
            md.update(salt);
            byte[] tmp = md.digest();
            byte[] iv = new byte[16];
            System.arraycopy(tmp, 0, iv, 0, 16);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            byte[] ba = cipher.doFinal(baVal);
            return new String(ba);
        } catch (Exception ex) {
            throw E.unexpected(ex);
        }
    }

    public static final String ALGO_RSA = "RSA";

    public static String encryptRSA(String value, String urlSafeBase64EncodedPublicKey) {
        return encryptRSA(value, Codec.decodeUrlSafeBase64(urlSafeBase64EncodedPublicKey));
    }

    public static String encryptRSA(String value, byte[] publicKey) {
        try {
            PublicKey key = KeyFactory.getInstance(ALGO_RSA).generatePublic(new X509EncodedKeySpec(publicKey));
            Cipher cipher = Cipher.getInstance(ALGO_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] ba = cipher.doFinal(value.getBytes(Charsets.UTF_8));
            return Codec.byteToHexString(ba);
        } catch (Exception e) {
            throw E.unexpected(e);
        }
    }

    public static String decryptRSA(String value, String urlSafeBase64EncodedPrivateKey) {
        return decryptRSA(value, Codec.decodeUrlSafeBase64(urlSafeBase64EncodedPrivateKey));
    }

    public static String decryptRSA(String value, byte[] privateKey) {
        try {
            PrivateKey key = KeyFactory.getInstance(ALGO_RSA)
                    .generatePrivate(new PKCS8EncodedKeySpec(privateKey));

            Cipher cipher = Cipher.getInstance(ALGO_RSA);
            cipher.init(Cipher.DECRYPT_MODE, key);

            byte[] ba = cipher.doFinal(Codec.hexStringToByte(value));
            return new String(ba);
        } catch (Exception e) {
            throw E.unexpected(e);
        }
    }

    public static KeyPair generateKeyPair() {
        return generateKeyPair(1024);
    }

    public static KeyPair generateKeyPair(int keysize) {
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGO_RSA);
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
            keyGen.initialize(keysize, random);
            return new KeyPair(keyGen.generateKeyPair());
        } catch (Exception e) {
            throw E.unexpected(e);
        }
    }


    /**
     * Generate a secret string from random byte array
     * @param len the number of bytes used to generate the secret
     * @return the secret
     */
    public static String genSecret(int len) {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[len];
        random.nextBytes(bytes);
        String s = Codec.encodeUrlSafeBase64(bytes);
        if (s.endsWith(".")) {
            s = S.beforeFirst(s, ".");
        }
        return s;
    }

    /**
     * Generate secret string using 4 bytes
     * @return the secret
     */
    public static String genSecret() {
        return genSecret(4);
    }

    private static final char[] digits = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
    };

    private static final int[] POWER_OF_TEN = {
            1,
            10,
            100,
            1000,
            10000,
            100000,
            1000000,
            10000000,
            100000000,
            1000000000
    };

    private static final int DIGITS_SIZE = digits.length;

    /**
     * Generate random digital string with the size specified. The
     * method use the {@link SecureRandom}
     * @param len the number of digits in the string generated
     * @return a string contains random digits
     */
    public static String genRandomDigits(int len) {
        E.illegalArgumentIf(len < 1);
        if (len < 10) {
            SecureRandom random = new SecureRandom();
            int n = random.nextInt(POWER_OF_TEN[len]);
            int base = POWER_OF_TEN[len - 1];
            if (n < base) {
                n += base;
            }
            return String.valueOf(n);
        } else {
            return genRandomX(len, digits, DIGITS_SIZE);
        }
    }

    /**
     * Generate random digital string of random size range from 4 to 12 inclusive
     * @return a random digital string
     * @see #genRandomDigits(int)
     */
    public static String genRandomDigits() {
        SecureRandom random = new SecureRandom();
        int n = random.nextInt(8) + 4;
        return genRandomDigits(n);
    }

    private final static char[] symbols = {
            '0' , '1' , '2' , '3' , '4' , '5' ,
            '6' , '7' , '8' , '9' , 'a' , 'b' ,
            'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
            'i' , 'j' , 'k' , 'm' , 'n' ,
            'o' , 'p' , 'q' , 'r' , 's' , 't' ,
            'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
            'A' , 'B' , 'C' , 'D' , 'E' , 'F' ,
            'G' , 'H' , 'J' , 'K' , 'L' ,
            'M' , 'N' , 'P' , 'Q' , 'R' ,
            'S' , 'T' , 'U' , 'V' , 'W' , 'X' ,
            'Y' , 'Z' , '!' , '.' , '-' , '*' ,
    };

    private final static int SYMBOL_SIZE = symbols.length;

    /**
     * Generate random String with readable characters. This
     * method will use the {@link SecureRandom}
     * @param len the size of the string returned
     * @return the string generated
     */
    public static String genRandomStr(int len) {
        E.illegalArgumentIf(len < 1);
        return genRandomX(len, symbols, SYMBOL_SIZE);
    }

    private static String genRandomX(int len, char[] space, int spaceSize) {
        SecureRandom random = new SecureRandom();
        char[] ca = new char[len];
        for (int i = 0; i < len; ++i) {
            ca[i] = space[random.nextInt(spaceSize)];
        }
        return new String(ca);
    }

    /**
     * Generate random string with random length from 4 to 12 inclusive
     * @return the string generated
     * @see #genRandomStr(int)
     */
    public static String genRandomStr() {
        SecureRandom random = new SecureRandom();
        int len = random.nextInt(12) + 4;
        return genRandomStr(len);
    }

    private static byte[] toByte(char[] chars) {
        CharBuffer charBuffer = CharBuffer.wrap(chars);
        ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
        byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
                byteBuffer.position(), byteBuffer.limit());
        Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
        Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
        return bytes;
    }

    public static void main(String[] args) {
        KeyPair keyPair = Crypto.generateKeyPair();
        String privateKey = keyPair.getPrivateKeyAsString();
        String publicKey = keyPair.getPublicKeyAsString();
        String s = "Hello world";
        String encrypted = encryptRSA(s, publicKey);
        System.out.println("publicKey: " + publicKey);
        System.out.println(decryptRSA(encrypted, privateKey));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy