Java tutorial
// ---------------------------------------------------------------------------- // 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); } } }