com.kuzumeji.platform.standard.SecurityService.java Source code

Java tutorial

Introduction

Here is the source code for com.kuzumeji.platform.standard.SecurityService.java

Source

// ----------------------------------------------------------------------------
// Copyright (C) Kuzumeji Evolution Laboratory. All rights reserved.
// GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
// http://www.gnu.org/licenses/gpl-3.0-standalone.html
// ----------------------------------------------------------------------------
package com.kuzumeji.platform.standard;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Properties;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

/**
 * 
 * <dl>
 * <dt>?
 * <dd>TODO ?????????(DDD/?)
 * </dl>
 * @author nilcy
 */
@SuppressWarnings("static-method")
public final class SecurityService implements Service {
    /** ?? */
    private static final String KEY_PUBLIC_ENCODED = "%s.public";
    /** ?? */
    private static final String KEY_PRIVATE_ENCODED = "%s.private";
    /** RSA??? */
    private static final String RSA_ALGO_NAME = "RSA";
    /** ? */
    private static final int KEYSIZE = 2048;
    /** ? */
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
    /** PBKDF2??? */
    private static final String PBKDF2_ALGO_NAME = "PBKDF2WithHmacSHA1";
    /** PBE?? */
    private static final int PBE_KEY_LENGTH = 128;
    /** PBE??? */
    private static final int PBE_ITER_COUNT = 65536;
    /** RSA??? */
    private static final String RSA_TRANSFORM_NAME = "RSA/ECB/PKCS1Padding";
    /** AES??? */
    private static final String AES_TRANSFORM_NAME = "AES/CBC/PKCS5Padding";
    /** AES??? */
    private static final String AES_ALGO_NAME = "AES";
    /** ????? */
    private static final String SIGN_ALGO_NAME = "SHA512withRSA";
    /** ?? */
    private static final String PROPERTY_NAME = "security.properties";

    /**  */
    public SecurityService() {
    }

    /**
     * RSA?(?/?)??
     * <dl>
     * <dt>?
     * <dd>RSA?(?/?)??(?=2048)
     * </dl>
     * @return ?
     */
    public KeyPair generateKeyPair() {
        try {
            final KeyPairGenerator keygen = KeyPairGenerator.getInstance(RSA_ALGO_NAME);
            keygen.initialize(KEYSIZE, SECURE_RANDOM);
            return keygen.generateKeyPair();
        } catch (final NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA???
     * <dl>
     * <dt>?
     * <dd>RSA???
     * </dl>
     * @param name RSA???
     * @param keyPair RSA?
     */
    public void saveKeyPair(final String name, final KeyPair keyPair) {
        try {
            final Properties property = new PropertyService(PROPERTY_NAME).getProperty();
            final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            property.setProperty(String.format(KEY_PUBLIC_ENCODED, name),
                    Hex.encodeHexString(publicKey.getEncoded()));
            final RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            property.setProperty(String.format(KEY_PRIVATE_ENCODED, name),
                    Hex.encodeHexString(privateKey.getEncoded()));
            try (FileOutputStream stream = new FileOutputStream(
                    Thread.currentThread().getContextClassLoader().getResource(PROPERTY_NAME).getPath());) {
                property.store(stream, "RSAPublicKey and RSAPrivateKey");
            }
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA??
     * <dl>
     * <dt>?
     * <dd>RSA??
     * </dl>
     * @param name RSA???
     * @return RSA?
     */
    public KeyPair loadKeyPair(final String name) {
        try {
            final Properties property = new PropertyService(PROPERTY_NAME).getProperty();
            final KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGO_NAME);
            final RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(
                    Hex.decodeHex(property.getProperty(String.format(KEY_PUBLIC_ENCODED, name)).toCharArray())));
            final RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(
                    Hex.decodeHex(property.getProperty(String.format(KEY_PRIVATE_ENCODED, name)).toCharArray())));
            return new KeyPair(publicKey, privateKey);
        } catch (final IOException | DecoderException | InvalidKeySpecException | NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * RSA???
     * <dl>
     * <dt>?
     * <dd>RSA????
     * </dl>
     * @param key RSA?
     * @return RSA?
     */
    public File savePublicKeyFile(final RSAPublicKey key) {
        try {
            final File file = File.createTempFile("public", ".key");
            try (FileOutputStream fos = new FileOutputStream(file);
                    DataOutputStream dos = new DataOutputStream(fos)) {
                final byte[] modulus = key.getModulus().toByteArray();
                dos.writeInt(modulus.length);
                dos.write(modulus);
                final byte[] publicExponent = key.getPublicExponent().toByteArray();
                dos.writeInt(publicExponent.length);
                dos.write(publicExponent);
            }
            return file;
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * <dl>
     * <dt>?
     * <dd>PBKDF2???????
     * </dl>
     * @param password 
     * @param salt 
     * @return ?(?)
     */
    public byte[] createCommonKey(final char[] password, final byte[] salt) {
        try {
            return SecretKeyFactory.getInstance(PBKDF2_ALGO_NAME)
                    .generateSecret(new PBEKeySpec(password, salt, PBE_ITER_COUNT, PBE_KEY_LENGTH)).getEncoded();
        } catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ??
     * <dl>
     * <dt>?
     * <dd>RSA????
     * </dl>
     * @param key RSA?
     * @param plain 
     * @return ?
     */
    public byte[] encrypt(final RSAPublicKey key, final byte[] plain) {
        try {
            final Cipher cipher = Cipher.getInstance(RSA_TRANSFORM_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(plain);
        } catch (final NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * <dl>
     * <dt>?
     * <dd>RSA?????
     * </dl>
     * @param key RSA?
     * @param encrypted ?
     * @return 
     */
    public byte[] decrypt(final RSAPrivateKey key, final byte[] encrypted) {
        try {
            final Cipher cipher = Cipher.getInstance(RSA_TRANSFORM_NAME);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(encrypted);
        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
                | BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ??
     * <dl>
     * <dt>?
     * <dd>AES??????
     * </dl>
     * @param key ?
     * @param plain 
     * @return {@link SecuredData ?}
     */
    public SecuredData encrypt(final byte[] key, final byte[] plain) {
        try {
            final Cipher cipher = Cipher.getInstance(AES_TRANSFORM_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, AES_ALGO_NAME));
            try (final ByteArrayOutputStream bos = new ByteArrayOutputStream();) {
                bos.write(cipher.doFinal(plain));
                return new SecuredData(bos.toByteArray(), cipher.getIV());
            }
        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
                | BadPaddingException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * <dl>
     * <dt>?
     * <dd>AES???????
     * </dl>
     * @param key ?
     * @param secured {@link SecuredData ?}
     * @return 
     */
    public byte[] decrypt(final byte[] key, final SecuredData secured) {
        try {
            final Cipher cipher = Cipher.getInstance(AES_TRANSFORM_NAME);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, AES_ALGO_NAME),
                    new IvParameterSpec(secured.getVector()));
            try (final ByteArrayOutputStream stream = new ByteArrayOutputStream();) {
                stream.write(cipher.doFinal(secured.getEncrypted()));
                return stream.toByteArray();
            }
        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException
                | IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * <dl>
     * <dt>?
     * <dd>SHA-512?RSA???????
     * </dl>
     * @param key ?
     * @param plain 
     * @return ??
     */
    public byte[] signature(final PrivateKey key, final byte[] plain) {
        try {
            final Signature signatureSign = Signature.getInstance(SIGN_ALGO_NAME);
            signatureSign.initSign(key, SECURE_RANDOM);
            signatureSign.update(plain);
            return signatureSign.sign();
        } catch (final NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ?
     * <dl>
     * <dt>?
     * <dd>SHA-512?RSA????????
     * </dl>
     * @param key ?
     * @param signature ??
     * @param plain 
     * @return ?
     */
    public boolean verify(final PublicKey key, final byte[] signature, final byte[] plain) {
        try {
            final Signature verifier = Signature.getInstance(SIGN_ALGO_NAME);
            verifier.initVerify(key);
            verifier.update(plain);
            return verifier.verify(signature);
        } catch (final NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            throw new RuntimeException(e);
        }
    }
}