package hamaster.gradesgin.util; import static hamaster.gradesign.ibe.util.Hex.bytesToInt; import static hamaster.gradesign.ibe.util.Hex.intToByte; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * IBECapsule的AES实现<br> * 加密方式 AES256_CBC_PKCS5Padding<br> * 密钥哈希函数 SHA-512<br> * 这个实现不是线程安全的 * @author <a href="mailto:wangyeee@gmail.com">Wang Ye</a> */ public class IBECapsuleAESImpl implements IBECapsule { private static final long serialVersionUID = 7608124228314005862L; private final static String HASH_ALGORITHM = "SHA-512"; private final static String CRYPTO_ALGORITHM = "AES/CBC/PKCS5Padding"; /** * 明文数据 */ private transient byte[] data; /** * 加密密钥 */ private transient byte[] key; /** * 密文数据 */ private transient byte[] cipherText; /** * 密钥的SHA-512摘要 */ private transient byte[] keyHash; public IBECapsuleAESImpl() { } /** * 持久化字段格式<br> * 加密算法名称(长度一字节 名称最多256字节)<br> * 哈希算法名称(长度一字节 名称最多256字节)<br> * 哈希值 长度由算法确定<br> * 数据有效长度 4字节<br> * 加密数据长度 4字节<br> * 加密数据 * @see hamaster.gradesgin.ibe.IBEConstraints#writeExternal(java.io.OutputStream) */ @Override public void writeExternal(OutputStream out) throws IOException { ensureNotNull(out, data, key); byte[] key = Hash.sha256(this.key); byte[] iv = Hash.md5(this.key); byte[] keyHash = Hash.sha512(this.key); byte[] crypt = null; try { Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); crypt = cipher.doFinal(data); } catch (NoSuchAlgorithmException e) { } catch (NoSuchPaddingException e) { } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { } catch (IllegalBlockSizeException e) { throw new IOException(e); } catch (BadPaddingException e) { throw new IOException(e); } finally { Arrays.fill(key, (byte) 0); Arrays.fill(iv, (byte) 0); } if (crypt == null) throw new IOException("Cannot encrypt data!"); byte cl = (byte) CRYPTO_ALGORITHM.length(); out.write(cl); out.write(CRYPTO_ALGORITHM.getBytes()); out.write((byte) HASH_ALGORITHM.length()); out.write(HASH_ALGORITHM.getBytes()); out.write(keyHash); out.write(intToByte(data.length)); out.write(intToByte(crypt.length)); out.write(crypt); out.flush(); } /* * (non-Javadoc) * @see hamaster.gradesgin.ibe.IBEConstraints#readExternal(java.io.InputStream) */ @Override public void readExternal(InputStream in) throws IOException, ClassNotFoundException { int cLength = in.read(); byte[] cBuffer = new byte[cLength]; cLength = in.read(cBuffer); if (cLength != cBuffer.length) throw new IOException("Not enough bytes!"); int hLength = in.read(); byte[] hnBuffer = new byte[hLength]; hLength = in.read(hnBuffer); if (hLength != hnBuffer.length) throw new IOException("Not enough bytes!"); this.keyHash = new byte[64]; in.read(keyHash); byte[] tmp = new byte[4]; int tLength = in.read(tmp); if (tLength != tmp.length) throw new IOException("Not enough bytes!"); int dataLength = bytesToInt(tmp); tLength = in.read(tmp); if (tLength != tmp.length) throw new IOException("Not enough bytes!"); int cipherLength = bytesToInt(tmp); data = new byte[dataLength]; cipherText = new byte[cipherLength]; cipherLength = in.read(cipherText); if (cipherLength != cipherText.length) throw new IOException("Not enough bytes!"); if (this.key != null) decrypt(); } private void decrypt() throws IOException { byte[] key = Hash.sha256(this.key); byte[] iv = Hash.md5(this.key); try { Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); byte[] tmp = cipher.doFinal(cipherText); System.arraycopy(tmp, 0, data, 0, data.length); Arrays.fill(tmp, (byte) 0); } catch (NoSuchAlgorithmException e) { } catch (NoSuchPaddingException e) { } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { } catch (IllegalBlockSizeException e) { throw new IOException(e); } catch (BadPaddingException e) { throw new IOException(e); } finally { Arrays.fill(key, (byte) 0); Arrays.fill(iv, (byte) 0); } } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#protect(byte[]) */ @Override public void protect(byte[] data) { ensureNotNull(data); this.data = data; } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#protect(java.io.Serializable) */ @Override public void protect(Serializable object) { ensureNotNull(object); try { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(buffer); out.writeObject(object); out.flush(); buffer.flush(); this.data = buffer.toByteArray(); buffer.reset(); buffer.close(); out.close(); } catch (IOException e) { } } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#getData() */ @Override public byte[] getData() { return data; } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#getDataAsObject() */ @Override public Object getDataAsObject() throws ClassNotFoundException { try { ByteArrayInputStream buffer = new ByteArrayInputStream(getData()); ObjectInputStream in = new ObjectInputStream(buffer); Object obj = in.readObject(); in.close(); buffer.reset(); buffer.close(); return obj; } catch (IOException e) { return null; } } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#setKey(byte[]) */ @Override public void setKey(byte[] key) { ensureNotNull(key); this.key = key; if (this.cipherText != null) { byte [] exc = Hash.sha512(key); boolean eq = Arrays.equals(exc, keyHash); if (!eq) throw new IllegalArgumentException("Invalid key"); try { decrypt(); } catch (IOException e) { e.printStackTrace(); } } } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#getHashAlgorithm() */ @Override public String getHashAlgorithm() { return HASH_ALGORITHM; } /* * (non-Javadoc) * @see hamaster.gradesgin.util.IBECapsule#getCrypto() */ @Override public String getCrypto() { return CRYPTO_ALGORITHM; } /* * (non-Javadoc) * @see java.lang.Object#finalize() */ protected void finalize() throws Throwable { if (data != null) Arrays.fill(data, (byte) 0); if (key != null) Arrays.fill(key, (byte) 0); if (cipherText != null) Arrays.fill(cipherText, (byte) 0); if (keyHash != null) Arrays.fill(keyHash, (byte) 0); super.finalize(); } private void ensureNotNull(Object ... objs) throws NullPointerException { if (objs == null) throw new NullPointerException(); for (Object obj : objs) { if (obj == null) throw new NullPointerException(); } } }