All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.lib.settings.Settings Maven / Gradle / Ivy

The newest version!
package aQute.lib.settings;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import aQute.lib.io.IO;
import aQute.lib.json.JSONCodec;

/**
 * Maintains persistent settings for bnd (or other apps). The default is
 * ~/.bnd/settings.json). The settings are normal string properties but it
 * specially maintains a public/private key pair and it provides a method to
 * sign a byte array with this pair.
 * 

* Why not keystore and preferences? Well, keystore is hard to use (you can only * store a private key when you have a certificate, but you cannot create a * certificate without using com.sun classes) and preferences are not editable. */ public class Settings implements Map { static JSONCodec codec = new JSONCodec(); private final File where; private PublicKey publicKey; private PrivateKey privateKey; private boolean loaded; private boolean dirty; public static class Data { public int version = 1; public byte[] secret; public byte[] id; public Map map = new HashMap<>(); } Data data = new Data(); private char[] password; public Settings() { this("~/.bnd/settings.json"); } public Settings(String where) { this(IO.getFile(IO.work, where)); } public Settings(File where) { assert where != null; this.where = where; } public boolean load() { return load(password); } @SuppressWarnings("resource") public boolean load(char[] password) { this.password = password; if (this.where.isFile() && this.where.length() > 1) { try { InputStream in = IO.stream(this.where); try { if (password != null) { PasswordCryptor cryptor = new PasswordCryptor(); in = cryptor.decrypt(password, in); } else { String secret = System.getenv() .get("BND_SETTINGS_PASSWORD"); if (secret != null && secret.length() > 0) { PasswordCryptor cryptor = new PasswordCryptor(); in = cryptor.decrypt(secret.toCharArray(), in); } } data = codec.dec() .from(in) .get(Data.class); loaded = true; return true; } finally { in.close(); } } catch (Exception e) { throw new RuntimeException("Cannot read settings file " + this.where, e); } } if (!data.map.containsKey("name")) data.map.put("name", System.getProperty("user.name")); return false; } private void check() { if (loaded) return; load(); loaded = true; } public void save() { save(password); } @SuppressWarnings("resource") public void save(char[] password) { try { IO.mkdirs(this.where.getParentFile()); } catch (IOException e) { throw new RuntimeException("Cannot create directory in " + this.where.getParent(), e); } try { OutputStream out = IO.outputStream(this.where); try { if (password != null) { PasswordCryptor cryptor = new PasswordCryptor(); out = cryptor.encrypt(password, out); } else { String secret = System.getenv() .get("BND-SETTINGS-PASSWORD"); if (secret != null) { PasswordCryptor cryptor = new PasswordCryptor(); out = cryptor.encrypt(secret.toCharArray(), out); } } codec.enc() .to(out) .put(data) .flush(); } finally { out.close(); } assert this.where.isFile(); } catch (Exception e) { throw new RuntimeException("Cannot write settings file " + this.where, e); } } public void generate() throws Exception { generate(password); } public void generate(char[] password) throws Exception { check(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); keyGen.initialize(2048, random); KeyPair pair = keyGen.generateKeyPair(); privateKey = pair.getPrivate(); publicKey = pair.getPublic(); data.secret = privateKey.getEncoded(); data.id = publicKey.getEncoded(); save(password); } public String getEmail() { return get("email"); } public void setEmail(String email) { put("email", email); } public void setKeyPair(byte[] id, byte[] secret) throws Exception { data.secret = secret; data.id = id; privateKey = null; publicKey = null; initKeys(); save(); } public void setName(String v) { put("name", v); } public String getName() { String name = get("name"); if (name != null) return name; return System.getProperty("user.name"); } /** * Return an encoded public RSA key. this key can be decoded with an * X509EncodedKeySpec * * @return an encoded public key. * @throws Exception */ public byte[] getPublicKey() throws Exception { initKeys(); return data.id; } /** * Return an encoded private RSA key. this key can be decoded with an * PKCS8EncodedKeySpec * * @return an encoded private key. * @throws Exception */ public byte[] getPrivateKey() throws Exception { initKeys(); return data.secret; } /* * Initialize the keys. */ private void initKeys() throws Exception { check(); if (privateKey != null) return; if (data.id == null || data.secret == null) { generate(); } else { PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(data.secret); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); privateKey = keyFactory.generatePrivate(privateKeySpec); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(data.id); publicKey = keyFactory.generatePublic(publicKeySpec); } } /** * Sign a byte array */ public byte[] sign(byte[] con) throws Exception { initKeys(); Signature hmac = Signature.getInstance("SHA1withRSA"); hmac.initSign(privateKey); hmac.update(con); return hmac.sign(); } /** * Verify a signed byte array */ public boolean verify(byte[] con) throws Exception { initKeys(); Signature hmac = Signature.getInstance("SHA1withRSA"); hmac.initVerify(publicKey); hmac.update(con); return hmac.verify(con); } @Override public void clear() { data = new Data(); IO.delete(where); } @Override public boolean containsKey(Object key) { check(); return data.map.containsKey(key); } @Override public boolean containsValue(Object value) { check(); return data.map.containsValue(value); } @Override public Set> entrySet() { check(); return data.map.entrySet(); } @Override public String get(Object key) { check(); return data.map.get(key); } @Override public boolean isEmpty() { check(); return data.map.isEmpty(); } @Override public Set keySet() { check(); return data.map.keySet(); } @Override public String put(String key, String value) { check(); dirty = true; return data.map.put(key, value); } @Override public void putAll(Map v) { check(); dirty = true; data.map.putAll(v); } @Override public String remove(Object key) { check(); dirty = true; return data.map.remove(key); } @Override public int size() { check(); return data.map.size(); } @Override public Collection values() { check(); return data.map.values(); } public boolean isDirty() { return dirty; } @Override public String toString() { return "Settings[" + where + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy