I have a database schema that I do not control (it's a sqlite3 file exported from a desktop application that I need to interoperate with), that contains UUIDs for some of the columns. I'm using sqlite-net-pcl in a Xamarin.Forms app, and I cannot work out how to successfully read these columns. Here's what I've tried:
using the sqlite3 command line, I've confirmed that the schema has type uuid for the relevant column, and using select count(distinct uuidcolumn) from mytable; I've confirmed that there are values for each row. (The column is nullable which is relevant for the code snippet below but in practice all the rows have non-null values)
I have this model object:
namespace brahms.Model
{
[Table("mytable")]
public class MyTable
{
[Column("uuidcolumn")]
public Guid UUIDColumn { get; }
[PrimaryKey, AutoIncrement, NotNull]
[Column("recordid")]
public int RecordID { get; set; }
}
}
if I fetch an object using database.Query<MyTable>() queries, UUIDColumn is always equal to Guid.Empty.
I tried switching the type in the class definition to Byte[]; it's always null.
I tried switching the type in the class definition to string; it's always null.
Same applies to the UInt16[] type (the GUID might be stored as a blob of 16-bit words, so I tried that type too)
How can I read the values in uuid-typed columns using sqlite-net-pcl?
I gave up on using the ORM features in sqlite-net-pcl and used this query:
db.executeScalar<byte[]>('select hex(uuidcolumn) from mytable where recordid=1');
What I get back is 72 bytes, which appear to represent the 36 ASCII characters in a string representation of a Guid (every so often one of the characters is 2D, which is - in the ASCII set). So I think that the backing store is a blob but one that's storing the text representation of the Guid, which is weird, but I'll be able to reconstruct the Guid from here.
Using this answer and getting that blob as a string, I ended up with this implementation:
public Guid GetUUIDColumn()
{
string dbRep = _database.ExecuteScalar<string>("select hex(uuidcolumn) from mytable where recordid = ?", RecordID);
if (dbRep == null || dbRep == string.Empty) return Guid.Empty;
var bytes = new byte[dbRep.Length / 2];
// each pair of bytes represents the ASCII code (in hexadecimal) for a character in the string representation of a Guid.
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(dbRep.Substring(i * 2, 2), 16);
}
string asString = Encoding.ASCII.GetString(bytes);
return new Guid(asString);
}
I am trying to figure out how to do RSA encryption with OAEPwithMD5andMGF1Padding in node-rsa.
Below is my code in node.js.
var NodeRSA = require('node-rsa');
var fs = require('fs');
var publicKey = '-----BEGIN PUBLIC KEY-----\n*****\n-----END PUBLIC KEY-----';
var privateKey = '-----BEGIN RSA PRIVATE KEY-----\n*****\n-----END RSA PRIVATE KEY-----'
const constants = require('constants');
var options1 = {
environment: 'node',
encryptionScheme: {
scheme: 'pkcs1_oaep',
hash: 'md5', //hash using for scheme
}
}
var text = 'This is the string to be encrypted using RSA!';
var encryptKey = new NodeRSA(publicKey, 'pkcs8-public', options1);
encryptKey.setOptions(options1)
var encrypted = encryptKey.encrypt(text, 'base64');
console.log(encrypted);
console.log(encryptKey.isPublic(true))
var options2 = {
environment: 'node',
encryptionScheme: {
scheme: 'pkcs1_oaep', //scheme
hash: 'md5', //hash using for scheme
}
}
var decryptKey = new NodeRSA(privateKey, 'pkcs1', options2);
decryptKey.setOptions(options2)
var decrypted = decryptKey.decrypt(encrypted, 'utf8');
console.log('decrypted: ', decrypted);
Result of running the above code.
f1zi49yKJSqkWW2J3Jt2lf1fe79JgqufFawYESOJRqhM4YEcGQBcaP39yptn7vShhsJBCTUOsbiV1YcW/YUzoaSQzX9YU0iTMara7h+LNLUrq4FZ2twy5X3uyAP1sUD1SnvQvlRJqrAh23UAwnx31rv6ySC+XgpLPR7wHYaDbSgyQKiF3qhGRj2SIAZ6weziNPfEm9FifBVjnWMvGDQYbjLbanbnSriN+bWpRtXKH9pQqMoskkiMwCviJdKtKzz/vVr0littPLnw0ojbsGSPKQPS3U3xCH3QiBmxEegc0uy3sJdk6aH/2SMuoPzGu7VS+PsLQctxnvKNnC9qsLFWyA==
true
decrypted: This is the string to be encrypted using RSA!
Below is my code in JAVA
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class DecryptATT {
public static void main(String[] args) throws Exception {
String encryptedData = "f1zi49yKJSqkWW2J3Jt2lf1fe79JgqufFawYESOJRqhM4YEcGQBcaP39yptn7vShhsJBCTUOsbiV1YcW/YUzoaSQzX9YU0iTMara7h+LNLUrq4FZ2twy5X3uyAP1sUD1SnvQvlRJqrAh23UAwnx31rv6ySC+XgpLPR7wHYaDbSgyQKiF3qhGRj2SIAZ6weziNPfEm9FifBVjnWMvGDQYbjLbanbnSriN+bWpRtXKH9pQqMoskkiMwCviJdKtKzz/vVr0littPLnw0ojbsGSPKQPS3U3xCH3QiBmxEegc0uy3sJdk6aH/2SMuoPzGu7VS+PsLQctxnvKNnC9qsLFWyA==";
// Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPwithMD5andMGF1Padding");
Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding");
RSAPrivateKey privateKey = getPrivateKey();
System.out.println("test");
decrypt.init(Cipher.DECRYPT_MODE, privateKey);
byte[] original = decrypt.doFinal(Base64.getDecoder().decode(encryptedData));
System.out.println(new String(original));
}
public static RSAPrivateKey getPrivateKey() throws Exception {
String keyPath = "/Users/C.SubbiahVeluAngamuthu/Desktop/Samsung/Docs/att/Keys/3_my_testing/pkcs8_key";
File privKeyFile = new File(keyPath);
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(privKeyFile));
} catch (FileNotFoundException e) {
throw new Exception("Could not locate keyfile at '" + keyPath + "'", e);
}
byte[] privKeyBytes = new byte[(int) privKeyFile.length()];
bis.read(privKeyBytes);
bis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(ks);
return privKey;
}
}
Below is the result of running the JAVA code
test
This is the string to be encrypted using RSA!
But when I change the cipher instance from RSA/ECB/OAEPwithSHA1andMGF1Padding to "RSA/ECB/OAEPwithMD5andMGF1Padding"(which I am assuming is the one that I mentioned in encryptionScheme of node.js program) it throws the below error
test
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:499)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:293)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at DecryptATT.main(DecryptATT.java:26)
Could some one help me where it is going wrong ?
RSAES-OAEP is parametrized by
the hash Hash used by OAEP, and its width in octet noted hLen
the size of the public key, k octets
the Mask Generation Function (MGF) used by OAEP
Almost invariably, the MGF is MFG1, which itself is parametrized by the hash Hash' used by MFG1, and its width in octet noted hLen' (the ' is not in the standard, I'm making up this notation).
You guessed it, there's noting stating that Hash and Hash' are the same, or even that hLen= hLen'.
And, believe me, unless something special is done about it, under a typical Java environement "RSA/ECB/OAEPwithMD5andMGF1Padding" (if supported) will use MD5 for Hash but default to SHA-1 for Hash'; when perhaps node.js uses MD5 for both.
Facing a similar problem with SHA-256 rather than MD5, we can coerce the nice Java runtime to do the Right Thing with
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey, new OAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
));
I fear you won't be so lucky, since MGF1ParameterSpec seems to never have had an MD5 specifier; but perhaps give a try to new MGF1ParameterSpec("MD5") to get one before giving up.
If one really needs to get the job done under Java, one option is to roll one's RSAES-OAEP with MD5 on top of Cipher invoked with "RSA/ECB/NoPadding", which will perform textbook RSA, by far the most complex building block (at least, all the key management, modular arithmetic, and ability to offload to an HSM is taken care of). That's few dozens lines of code, including MFG1.
Another option might be BouncyCastle.
It's a bad idea to keep using MD5, even in MFG1. And it is an unmitigated disaster to use it as the main hash if adversaries can choose a part of the message at a time when they know what's before that part. If in doubt, don't use MD5.
I am working with encryption using AES. My customer is encrypting some of the sensitive data while posting the data to my web API. And my code will decrypt these fields before insert them to the database.
Originally we agree to use a fixed secret key. Below is the code:
public class AESEncryptor {
private static final String ALGO = "AES";
private static final String keyVal = "!5Po4#j82Adsu39/*na3n5";
public static String encrypt(String data) {
try {
Key key = genKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(data.getBytes());
return Base64.encodeBase64String(encVal);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt (String encryptedData) throws Exception{
Key key = genKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] data = Base64.decodeBase64(encryptedData);
byte[] decByptes = c.doFinal(data);
return new String(decByptes);
}
private static Key genKey() throws Exception {
fixKeyLength();
return new SecretKeySpec(keyVal.getBytes(), ALGO);
}
}
Then the other party suggested we should switch to KeyGenerator to generate a random secure key. Something like the following.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
final byte[] nonce = new byte[32];
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(16 * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
I am not sure that is possible. Because the correct decryption relies on the same key for encryption. If the key is random, how would my API know what key to use every time? Or is there a solution to handle this situation?
There is no solution to handle this problem. Symmetric encryption requires that both parties know the key in order to encrypt and decrypt. If the key is random each time, then you need a way to communicate the key.
The scheme you have designed is quite poor, since a fixed key means that the key being compromised will bring down the whole system. You're also using ECB mode, which is inherently insecure. No authentication either.
If you want to communicate data securely from one party to another, use TLS with client authentication. This is the industry standard way to solve this problem and you don't have to get your hands dirty with the crypto.
I know that we can define the key explicitly using the below line. For 3des the key length should be 24 bytes, if I am not wrong.
Dim Newkey() As Byte = Convert.FromBase64String("24 bytes enter here")
something like
Dim Newkey() As Byte = Convert.FromBase64String("c:\temp\mykey.pem")
How do i make sure that the file gives back 24 bytes of data, for 3des encryption?
What/How do i generate such a file?
Simply check the length of the array using NewKey.Length after decoding
Use TripleDES.GenerateKey, retrieve the key property. This will generate keys that should be compatible with TripleDES, then call Converter.ToBase64String
Please look up a tutorial of creating/reading textual files yourself, using FromBase64String on a file name won't work.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.generatekey.aspx#Y0
Note that 16 byte keys may be used for TripleDES (ABA keys) as well as 24 byte (ABC) keys. Also note that there are weak keys in DES, and that keys include parity bits. Most implementations simply ignore the parity bits, but it is better to use a special key generation function to be sure they are set correctly.
using System;
using System.Text;
using System.Security.Cryptography;
namespace Crypto
{
public class KeyCreator
{
public static void Main(String[] args)
{
String[] commandLineArgs = System.Environment.GetCommandLineArgs();
string decryptionKey = CreateKey(System.Convert.ToInt32(commandLineArgs[1]));
string validationKey = CreateKey(System.Convert.ToInt32(commandLineArgs[2]));
Console.WriteLine("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" validation=\"SHA1\"/>", validationKey, decryptionKey);
}
static String CreateKey(int numBytes)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[numBytes];
rng.GetBytes(buff);
return BytesToHexString(buff);
}
static String BytesToHexString(byte[] bytes)
{
StringBuilder hexString = new StringBuilder(64);
for (int counter = 0; counter < bytes.Length; counter++)
{
hexString.Append(String.Format("{0:X2}", bytes[counter]));
}
return hexString.ToString();
}
}
}
I am creating a client side application which needs to create a log of the user activity but for various reasons this log must not be human readable.
Currently for my development I am creating a plain text log which looks something like this:
12/03/2009 08:34:21 -> User 'Bob' logged in
12/03/2009 08:34:28 -> Navigated to config page
12/03/2009 08:34:32 -> Option x changed to y
When I deploy my application, the log must not be in plain text, so all text must be encrypted. This doesn't appear to be straightforward to achieve as I need the log file to dynamically update as each entry is added.
The approach I was thinking about was to create a binary file, encrypt each log entry in isolation and then append it to the binary file with some suitable demarcation between each entry.
Does anyone know of any common approaches to this problem, I'm sure there has to be a better solution!
Don't encrypt individual log entries separately and write them to a file as suggested by other posters, because an attacker would easily be able to identify patterns in the log file. See the block cipher modes Wikipedia entry to learn more about this problem.
Instead, make sure that the encryption of a log entry depends on the previous log entries. Although this has some drawbacks (you cannot decrypt individual log entries as you always need to decrypt the entire file), it makes the encryption a lot stronger. For our own logging library, SmartInspect, we use AES encryption and the CBC mode to avoid the pattern problem. Feel free to give SmartInspect a try if a commercial solution would be suitable.
This is not really my thing, I'll admit that readily, but can't you encrypt each entry individually and then append it to the logfile? If you that refrain from encrypting the timestamp, you can easily find entries your are looking for and decrypt those when needed.
My point being mainly that appending individual encrypted entries to a file does not necessarily need to be binary entries appended to a binary file. Encryption with (for example) gpg will yield ascii garble that can be appended to an ascii file. Would that solve you problem?
FWIW, the one time I needed an encrypted logger I used a symmetric key (for performance reasons) to encrypt the actual log entries.
The symmetric 'log file key' was then encrypted under a public key and stored at the beginning of the log file and a separate log reader used the private key to decrypt the 'log file key' and read the entries.
The whole thing was implemented using log4j and an XML log file format (to make it easier for the reader to parse) and each time the log files were rolled over a new 'log file key' was generated.
Assuming you're using some sort of logging framework, e.g., log4j et al, then you should be able to create a custom implementation of Appender (or similar) that encrypts each entry, as #wzzrd suggested.
It is not clear to me wheter your concern is on the security, or the implement.
A simple implement is to hook up with a stream encryptor. A stream encryptor maintains its own state and can encrypt on the fly.
StreamEncryptor<AES_128> encryptor;
encryptor.connectSink(new std::ofstream("app.log"));
encryptor.write(line);
encryptor.write(line2);
...
Very old question and I'm sure the tech world has made much progress, but FWIW Bruce Schneier and John Kelsey wrote a paper on how to do this: https://www.schneier.com/paper-auditlogs.html
The context is not just security but also preventing the corruption or change of existing log file data if the system that hosts the log/audit files is compromised.
Encrypting each log entry individually would decrease the security of your ciphertext a lot, especially because you're working with very predictable plaintext.
Here's what you can do:
Use symmetric encryption (preferably AES)
Pick a random master key
Pick a security window (5 minutes, 10 minutes, etc.)
Then, pick a random temporary key at the beginning of each window (every 5 minutes, every 10 minutes, etc.)
Encrypt each log item separately using the temporary key and append to a temporary log file.
When the window's closed (the predetermined time is up), decrypt each element using the temporary key, decrypt the master log file using the master key, merge the files, and encrypt using the master key.
Then, pick a new temporary key and continue.
Also, change the master key each time you rotate your master log file (every day, every week, etc.)
This should provide enough security.
I'm wondering what kind of application you write. A virus or a Trojan horse? Anyway ...
Encrypt each entry alone, convert it to some string (Base64, for example) and then log that string as the "message".
This allows you to keep parts of the file readable and only encrypt important parts.
Notice that there is another side to this coin: If you create a fully encrypted file and ask the user for it, she can't know what you will learn from the file. Therefore, you should encrypt as little as possible (passwords, IP addresses, costumer data) to make it possible for the legal department to verify what data is leaving.
A much better approach would be to an obfuscator for the log file. That simply replaces certain patterns with "XXX". You can still see what happened and when you need a specific piece of data, you can ask for that.
[EDIT] This story has more implications that you'd think at first glance. This effectively means that a user can't see what's in the file. "User" doesn't necessarily include "cracker". A cracker will concentrate on encrypted files (since they are probably more important). That's the reason for the old saying: As soon as someone gets access to the machine, there is no way to prevent him to do anything on it. Or to say it another way: Just because you don't know how doesn't mean someone else also doesn't. If you think you have nothing to hide, you haven't thought about yourself.
Also, there is the issue of liability. Say, some data leaks on the Internet after you get a copy of the logs. Since the user has no idea what is in the log files, how can you prove in court that you weren't the leak? Bosses could ask for the log files to monitor their pawns, asking to have it encoded so the peasants can't notice and whine about it (or sue, the scum!).
Or look at it from a completely different angle: If there was no log file, no one could abuse it. How about enabling debugging only in case of an emergency? I've configured log4j to keep the last 200 log messages in a buffer. If an ERROR is logged, I dump the 200 messages to the log. Rationale: I really don't care what happens during the day. I only care for bugs. Using JMX, it's simple to set the debug level to ERROR and lower it remotely at runtime when you need more details.
For .Net see Microsoft Application blocks for log and encrypt functionality:
http://msdn.microsoft.com/en-us/library/dd203099.aspx
I would append encrypted log entries to a flat text file using suitable demarcation between each entry for the decryption to work.
I have the exact same need as you. Some guy called 'maybeWeCouldStealAVa' wrote a good implementation in: How to append to AES encrypted file , however this suffered from not being flushable - you would have to close and reopen the file each time you flush a message, to be sure not to lose anything.
So I've written my own class to do this:
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;
public class FlushableCipherOutputStream extends OutputStream
{
private static int HEADER_LENGTH = 16;
private SecretKeySpec key;
private RandomAccessFile seekableFile;
private boolean flushGoesStraightToDisk;
private Cipher cipher;
private boolean needToRestoreCipherState;
/** the buffer holding one byte of incoming data */
private byte[] ibuffer = new byte[1];
/** the buffer holding data ready to be written out */
private byte[] obuffer;
/** Each time you call 'flush()', the data will be written to the operating system level, immediately available
* for other processes to read. However this is not the same as writing to disk, which might save you some
* data if there's a sudden loss of power to the computer. To protect against that, set 'flushGoesStraightToDisk=true'.
* Most people set that to 'false'. */
public FlushableCipherOutputStream(String fnm, SecretKeySpec _key, boolean append, boolean _flushGoesStraightToDisk)
throws IOException
{
this(new File(fnm), _key, append,_flushGoesStraightToDisk);
}
public FlushableCipherOutputStream(File file, SecretKeySpec _key, boolean append, boolean _flushGoesStraightToDisk)
throws IOException
{
super();
if (! append)
file.delete();
seekableFile = new RandomAccessFile(file,"rw");
flushGoesStraightToDisk = _flushGoesStraightToDisk;
key = _key;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16];
byte[] headerBytes = new byte[HEADER_LENGTH];
long fileLen = seekableFile.length();
if (fileLen % 16L != 0L) {
throw new IllegalArgumentException("Invalid file length (not a multiple of block size)");
} else if (fileLen == 0L) {
// new file
// You can write a 16 byte file header here, including some file format number to represent the
// encryption format, in case you need to change the key or algorithm. E.g. "100" = v1.0.0
headerBytes[0] = 100;
seekableFile.write(headerBytes);
// Now appending the first IV
SecureRandom sr = new SecureRandom();
sr.nextBytes(iv);
seekableFile.write(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
} else if (fileLen <= 16 + HEADER_LENGTH) {
throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)");
} else {
// file length is at least 2 blocks
needToRestoreCipherState = true;
}
} catch (InvalidKeyException e) {
throw new IOException(e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new IOException(e.getMessage());
} catch (NoSuchPaddingException e) {
throw new IOException(e.getMessage());
} catch (InvalidAlgorithmParameterException e) {
throw new IOException(e.getMessage());
}
}
/**
* Writes one _byte_ to this output stream.
*/
public void write(int b) throws IOException {
if (needToRestoreCipherState)
restoreStateOfCipher();
ibuffer[0] = (byte) b;
obuffer = cipher.update(ibuffer, 0, 1);
if (obuffer != null) {
seekableFile.write(obuffer);
obuffer = null;
}
}
/** Writes a byte array to this output stream. */
public void write(byte data[]) throws IOException {
write(data, 0, data.length);
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
*
* #param data the data.
* #param off the start offset in the data.
* #param len the number of bytes to write.
*/
public void write(byte data[], int off, int len) throws IOException
{
if (needToRestoreCipherState)
restoreStateOfCipher();
obuffer = cipher.update(data, off, len);
if (obuffer != null) {
seekableFile.write(obuffer);
obuffer = null;
}
}
/** The tricky stuff happens here. We finalise the cipher, write it out, but then rewind the
* stream so that we can add more bytes without padding. */
public void flush() throws IOException
{
try {
if (needToRestoreCipherState)
return; // It must have already been flushed.
byte[] obuffer = cipher.doFinal();
if (obuffer != null) {
seekableFile.write(obuffer);
if (flushGoesStraightToDisk)
seekableFile.getFD().sync();
needToRestoreCipherState = true;
}
} catch (IllegalBlockSizeException e) {
throw new IOException("Illegal block");
} catch (BadPaddingException e) {
throw new IOException("Bad padding");
}
}
private void restoreStateOfCipher() throws IOException
{
try {
// I wish there was a more direct way to snapshot a Cipher object, but it seems there's not.
needToRestoreCipherState = false;
byte[] iv = cipher.getIV(); // To help avoid garbage, re-use the old one if present.
if (iv == null)
iv = new byte[16];
seekableFile.seek(seekableFile.length() - 32);
seekableFile.read(iv);
byte[] lastBlockEnc = new byte[16];
seekableFile.read(lastBlockEnc);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] lastBlock = cipher.doFinal(lastBlockEnc);
seekableFile.seek(seekableFile.length() - 16);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] out = cipher.update(lastBlock);
assert out == null || out.length == 0;
} catch (Exception e) {
throw new IOException("Unable to restore cipher state");
}
}
public void close() throws IOException
{
flush();
seekableFile.close();
}
}
Here's an example of using it:
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.io.BufferedWriter;
public class TestFlushableCipher {
private static byte[] keyBytes = new byte[] {
// Change these numbers, lest other StackOverflow readers can decrypt your files.
-53, 93, 59, 108, -34, 17, -72, -33, 126, 93, -62, -50, 106, -44, 17, 55
};
private static SecretKeySpec key = new SecretKeySpec(keyBytes,"AES");
private static int HEADER_LENGTH = 16;
private static BufferedWriter flushableEncryptedBufferedWriter(File file, boolean append) throws Exception
{
FlushableCipherOutputStream fcos = new FlushableCipherOutputStream(file, key, append, false);
return new BufferedWriter(new OutputStreamWriter(fcos, "UTF-8"));
}
private static InputStream readerEncryptedByteStream(File file) throws Exception
{
FileInputStream fin = new FileInputStream(file);
byte[] iv = new byte[16];
byte[] headerBytes = new byte[HEADER_LENGTH];
if (fin.read(headerBytes) < HEADER_LENGTH)
throw new IllegalArgumentException("Invalid file length (failed to read file header)");
if (headerBytes[0] != 100)
throw new IllegalArgumentException("The file header does not conform to our encrypted format.");
if (fin.read(iv) < 16) {
throw new IllegalArgumentException("Invalid file length (needs a full block for iv)");
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new CipherInputStream(fin,cipher);
}
private static BufferedReader readerEncrypted(File file) throws Exception
{
InputStream cis = readerEncryptedByteStream(file);
return new BufferedReader(new InputStreamReader(cis));
}
#Test
public void test() throws Exception {
File zfilename = new File("c:\\WebEdvalData\\log.x");
BufferedWriter cos = flushableEncryptedBufferedWriter(zfilename, false);
cos.append("Sunny ");
cos.append("and green. \n");
cos.close();
int spaces=0;
for (int i = 0; i<10; i++) {
cos = flushableEncryptedBufferedWriter(zfilename, true);
for (int j=0; j < 2; j++) {
cos.append("Karelia and Tapiola" + i);
for (int k=0; k < spaces; k++)
cos.append(" ");
spaces++;
cos.append("and other nice things. \n");
cos.flush();
tail(zfilename);
}
cos.close();
}
BufferedReader cis = readerEncrypted(zfilename);
String msg;
while ((msg=cis.readLine()) != null) {
System.out.println(msg);
}
cis.close();
}
private void tail(File filename) throws Exception
{
BufferedReader infile = readerEncrypted(filename);
String last = null, secondLast = null;
do {
String msg = infile.readLine();
if (msg == null)
break;
if (! msg.startsWith("}")) {
secondLast = last;
last = msg;
}
} while (true);
if (secondLast != null)
System.out.println(secondLast);
System.out.println(last);
System.out.println();
}
}