As of version 3.2.1 DidiSoft OpenPGP Library for Java offers a custom storage mechanism for its KeyStore class.
Up till now, the KeyStore class was limited to in-memory and file-based storage.
If an application design required the keys to be stored on a different kind of media, for example in a database BLOB field, the only option was to use an in-memory KeyStore and use the saveToStream method on each operation that modified its state.
Version 3.2.1 provides a storage interface IKeyStoreStorage
package com.didisoft.pgp.storage; public interface IKeyStoreStorage { InputStream getInputStream() throws IOException; void store(InputStream data, int length) throws IOException; } |
A KeyStore instance created with a IKeyStoreStorage backend loads its initial state by invoking IKeyStoreStorage.getInputStream().
When it needs to get persisted, it invokes IKeyStoreStorage.store(InputStream data, int length). The data parameter contains InputStream obtained from the data to be persisted and the length parameter contains the size in bytes of the data.
Additional constructors that utilize the IKeyStoreStorage interface has been added:
// saves the keys in unprotected format KeyStore(IKeyStoreStorage storage); // saves the keys encrypted with AES-256 bit key password KeyStore(IKeyStoreStorage storage, String password); |
The old file-based persisted storage implemented as IKeyStoreStorage
The old KeyStore constructor that stores the keys in a file is now using a file-based backend with an implementation like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class FileKeyStorage implements IKeyStoreStorage { private String fileName; public FileKeyStorage(String fileName) { this.fileName = fileName; } public InputStream getInputStream() throws IOException { File storeFile = new File(this.fileName); if (storeFile.exists()) { return new FileInputStream(this.fileName); } return null; } public void store(InputStream data, int length) throws IOException { OutputStream fOut = new FileOutputStream(fileName); try { byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = data.read(buffer, 0, buffer.length)) > 0) { fOut.write(buffer, 0, bytesRead); } } finally { fOut.close(); fOut = null; } } } |
Storing keys in a database?
In order to save the keys in a database BLOB field we have to implement a storage class that implements the IKeyStoreStorage interface. The database table assumed by the class below was created with the SQL code: CREATE TABLE keys(storage BLOB);
(full source code available in the distribution ZIP archive/Examples/storage/BlobKeyStorage.java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import java.sql.*; import java.io.*; public class BlobKeyStorage implements IKeyStoreStorage { static String url = "jdbc:oracle:thin:@localhost:1521:javaDemo"; static String username = "username"; static String password = "welcome"; public InputStream getInputStream() throws IOException { try { Class.forName("oracle.jdbc.driver.OracleDriver"); Connection conn = DriverManager.getConnection(url, username, password); String sql = "SELECT storage FROM keys"; PreparedStatement stmt = conn.prepareStatement(sql); ResultSet resultSet = stmt.executeQuery(); while (resultSet.next()) { InputStream is = resultSet.getBinaryStream(1); return is; } } catch (SQLException e) { throw new IOException(e.getMessage()); } catch (ClassNotFoundException e) { throw new IOException(e.getMessage()); } return null; } public void store(InputStream data, int length) throws IOException { try { Class.forName("oracle.jdbc.driver.OracleDriver"); Connection conn = DriverManager.getConnection(url, username, password); conn.setAutoCommit(false); String sql = "SELECT storage FROM keys"; PreparedStatement stmt = conn.prepareStatement(sql); ResultSet resultSet = stmt.executeQuery(); if (resultSet.next()) { sql = "UPDATE keys SET storage = ?"; PreparedStatement stmt2 = conn.prepareStatement(sql); stmt2.setBinaryStream(1, data, length); stmt2.execute(); } else { sql = "INSERT INTO keys (storage) VALUES (?)"; PreparedStatement stmt2 = conn.prepareStatement(sql); stmt2.setBinaryStream(1, data, length); stmt2.execute(); } conn.commit(); conn.close(); } catch (SQLException e) { throw new IOException(e.getMessage()); } catch (ClassNotFoundException e) { throw new IOException(e.getMessage()); } } } |
Storing keys in a remote system
Let’s suppose that we decide to save and load the KeyStore from a remote system. All we have to do is implement a method that loads the keys from the remote system and another one that pushes the data back:
1 2 3 4 5 6 7 8 9 | public class RemoteKeyStorage implements IKeyStoreStorage { public InputStream getInputStream() throws IOException { return RemoteAPI.getInputStream(); } public void store(InputStream data, int length) throws IOException { RemoteAPI.store(data, length); } } |