|
package test; |
|
|
|
import java.io.ByteArrayInputStream; |
|
import java.io.ByteArrayOutputStream; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.nio.charset.StandardCharsets; |
|
import java.security.InvalidAlgorithmParameterException; |
|
import java.security.InvalidKeyException; |
|
import java.security.NoSuchAlgorithmException; |
|
import java.security.NoSuchProviderException; |
|
import java.security.SecureRandom; |
|
import java.security.Security; |
|
|
|
import javax.crypto.BadPaddingException; |
|
import javax.crypto.Cipher; |
|
import javax.crypto.CipherInputStream; |
|
import javax.crypto.IllegalBlockSizeException; |
|
import javax.crypto.KeyGenerator; |
|
import javax.crypto.NoSuchPaddingException; |
|
import javax.crypto.SecretKey; |
|
import javax.crypto.spec.GCMParameterSpec; |
|
|
|
import org.bouncycastle.jce.provider.BouncyCastleProvider; |
|
|
|
|
|
public class Crypt |
|
{ |
|
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException |
|
{ |
|
Security.addProvider(new BouncyCastleProvider()); |
|
|
|
SecureRandom random = SecureRandom.getInstanceStrong(); |
|
|
|
// create random secret key |
|
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); |
|
keyGen.init(128, random); |
|
SecretKey secretKey = keyGen.generateKey(); |
|
|
|
byte[] cipher = createEncrypted(secretKey); |
|
// cipher[12]++; // introduce modification |
|
decrypt(cipher, secretKey); |
|
} |
|
|
|
static byte[] createEncrypted(SecretKey key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException |
|
{ |
|
// generate random IV (as GCMParameter) |
|
byte ivbytes[] = new byte[12]; |
|
new SecureRandom().nextBytes(ivbytes); // no need for "strong" |
|
GCMParameterSpec spec = new GCMParameterSpec(/*taglen(bits)=*/128, ivbytes); |
|
|
|
Cipher c = Cipher.getInstance("AES/GCM/NoPadding"); |
|
c.init(Cipher.ENCRYPT_MODE, key, spec); |
|
|
|
assert spec.getIV().length == ivbytes.length; |
|
assert c.getIV().length == spec.getIV().length; |
|
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
|
bos.write(spec.getIV()); |
|
|
|
byte[] cb; |
|
|
|
// write out sample 33 byte data not block aligned |
|
cb = c.update("1234567".getBytes(StandardCharsets.UTF_8)); |
|
if (cb != null) bos.write(cb); |
|
cb = c.doFinal("89012345612345678901234561".getBytes(StandardCharsets.UTF_8)); |
|
if (cb != null) bos.write(cb); |
|
bos.close(); |
|
|
|
byte[] cipher = bos.toByteArray(); |
|
System.out.println("ciphertext (+12) len=" + cipher.length); |
|
|
|
return cipher; |
|
} |
|
|
|
static void decrypt(byte[] cipherText, SecretKey key) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException |
|
{ |
|
InputStream in = new ByteArrayInputStream(cipherText); |
|
|
|
// read back IV |
|
byte[] ivbytes = readFully(in, 12); |
|
GCMParameterSpec spec = new GCMParameterSpec(128, ivbytes); |
|
|
|
Cipher c = Cipher.getInstance("AES/GCM/NoPadding" /*, "BC" */ ); |
|
c.init(Cipher.DECRYPT_MODE, key, spec); // same as encrypt mode |
|
|
|
CipherInputStream cis = new CipherInputStream(in, c); |
|
// if we read exactly plaintext bytes BC will throw in close, SunJCE always in first(!) read |
|
for(int i=0;i<32;i++) System.out.print(" " + Integer.toHexString(cis.read())); |
|
System.out.println(); |
|
cis.close(); |
|
} |
|
|
|
private static byte[] readFully(InputStream in, int i) throws IOException |
|
{ |
|
byte[] b = new byte[i]; |
|
int len = i; |
|
int off = 0; |
|
do { |
|
int read = in.read(b, off, len); |
|
off+=read; |
|
len-=read; |
|
} while(len > 0); |
|
return b; |
|
} |
|
} |