// TODO: Android N seems to deprecate SHA1PRNG // https://android-developers.googleblog.com/2016/06/security-crypto-provider-deprecated-in.html static class PKI { static String getProvider() { return isAndroid() ? /*"AndroidOpenSSL"*/ "BC" : "SUN"; } public static boolean verifySignature(byte[] publicKey, byte[] signature, String file) throws InvalidKeyException, NoSuchProviderException, NoSuchAlgorithmException, IOException, SignatureException, InvalidKeySpecException { Signature sig = initPublicSignature(publicKey); FileInputStream datafis = new FileInputStream(file); BufferedInputStream bufin = new BufferedInputStream(datafis); byte[] buffer = new byte[1024]; while (bufin.available() != 0) { int len = bufin.read(buffer); sig.update(buffer, 0, len); } bufin.close(); return sig.verify(signature); } public static boolean verifySignature(byte[] publicKey, byte[] signature, byte[] data) ctex { Signature sig = initPublicSignature(publicKey); sig.update(data); return sig.verify(signature); } private static Signature initPublicSignature(byte[] publicKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException { Signature sig = Signature.getInstance("SHA1withDSA", getProvider()); PublicKey pub = KeyFactory.getInstance("DSA", getProvider()).generatePublic(new X509EncodedKeySpec(publicKey)); sig.initVerify(pub); return sig; } public static byte[] sign(String file, byte[] privateKey) ctex { Signature dsa = initSignature(privateKey); FileInputStream fis = new FileInputStream(file); BufferedInputStream bufin = new BufferedInputStream(fis); byte[] buffer = new byte[1024]; int len; while ((len = bufin.read(buffer)) >= 0) dsa.update(buffer, 0, len); bufin.close(); return dsa.sign(); } public static byte[] sign(byte[] data, byte[] privateKey) ctex { Signature dsa = initSignature(privateKey); dsa.update(data); return dsa.sign(); } private static Signature initSignature(byte[] privateKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException { Signature dsa = Signature.getInstance("SHA1withDSA", getProvider()); //X509EncodedKeySpec keySpec = new X509EncodedKeySpec(privateKey); EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); PrivateKey priv = KeyFactory.getInstance("DSA", getProvider()).generatePrivate(keySpec); dsa.initSign(priv); return dsa; } public static KeyPair makeKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", getProvider()); // OLD SecureRandom random = SecureRandom.getInstance("SHA1PRNG", getProvider()); // should work on Android? SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); keyGen.initialize(1024, random); return keyGen.generateKeyPair(); } // Standard key pair is for the MACHINE (not the user) public static boolean makeStandardKeyPairIfNecessary() ctex { File publicKeyPath = getStandardPublicKeyPath(); File privateKeyPath = getStandardPrivateKeyPath(); if (!publicKeyPath.exists() || !privateKeyPath.exists()) { print("PKI: Making standard key pair"); KeyPair keyPair = makeKeyPair(); byte[] privateKey = keyPair.getPrivate().getEncoded(); print("PKI: Private key hex (debug) - " + bytesToHex(privateKey)); saveTextFile(privateKeyPath, privateKeyToString(privateKey)); String publicKey = publicKeyToString(keyPair.getPublic()); saveTextFile(publicKeyPath, publicKey); print("PKI: Standard key pair made!"); print(); printInfo(); return true; } return false; } static void printInfo() { print("PKI: Machine's public key: " + publicKeyToString(getStandardPublicKey())); print("PKI: Machine's private key in: " + getStandardPrivateKeyPath()); print("PKI: Machine's public key also in: " + getStandardPublicKeyPath()); print("PKI: User's public key: " + publicKeyToString(userPublicKey())); print("PKI: User's private key in: " + userPrivateKeyPath()); print("PKI: User's public key also in: " + userPublicKeyPath()); } public static File getStandardPrivateKeyPath() { return new File(getSecretProgramDir("#1001547"), "Computer-Private-Key"); } public static File userPublicKeyPath() { return new File(getProgramDir("#1001547"), "User-Public-Key"); } public static File userPrivateKeyPath() { return new File(getSecretProgramDir("#1001547"), "User-Private-Key"); } public static File getStandardPublicKeyPath() { return new File(getProgramDir("#1001547"), "Computer-Public-Key"); } public static byte[] getStandardPrivateKey() { makeStandardKeyPairIfNecessary(); return privateKeyFromString(loadTextFile(getStandardPrivateKeyPath())); } public static byte[] getStandardPublicKey() { makeStandardKeyPairIfNecessary(); return publicKeyFromString(loadTextFile(getStandardPublicKeyPath())); } public static byte[] userPrivateKey() { makeUserKeyPairIfNecessary(); return privateKeyFromString(loadTextFile(userPrivateKeyPath())); } public static byte[] userPublicKey() { makeUserKeyPairIfNecessary(); return publicKeyFromString(loadTextFile(userPublicKeyPath())); } private static String publicKeyToString(PublicKey publicKey) { return publicKeyToString(publicKey.getEncoded()); } public static String privateKeyToString(PrivateKey privateKey) { return privateKeyToString(privateKey.getEncoded()); } public static byte[] publicKeyFromString(String key) { return base64decode(dropPrefixMandatory("publickey:", trim(key))); } public static byte[] privateKeyFromString(String key) { return base64decode(dropPrefixMandatory("privatekey:", trim(key))); } public static byte[] signatureFromString(String key) { return base64decode(key); } public static byte[] dataFromString(String text) { return toUtf8(text); } public static String signatureToString(byte[] signature) { return base64encode(signature); } public static String publicKeyToString(byte[] key) { return "publickey:" + base64encode(key); } public static String privateKeyToString(byte[] key) { return "privatekey:" + base64encode(key); } static boolean makeUserKeyPairIfNecessary() ctex { File publicKeyPath = userPublicKeyPath(); File privateKeyPath = userPrivateKeyPath(); if (!publicKeyPath.exists() || !privateKeyPath.exists()) { print("PKI: Making user key pair"); KeyPair keyPair = makeKeyPair(); byte[] privateKey = keyPair.getPrivate().getEncoded(); print("PKI: Private key hex (debug) - " + bytesToHex(privateKey)); saveTextFile(privateKeyPath, privateKeyToString(privateKey)); String publicKey = publicKeyToString(keyPair.getPublic()); saveTextFile(publicKeyPath, publicKey); print("PKI: User key pair made!"); print(); printInfo(); return true; } return false; } }