/** * Copyright (c) 2015 unfoldingWord * http://creativecommons.org/licenses/MIT/ * See LICENSE file for details. * Contributors: * PJ Fechner <pj@actsmedia.com> */ package signing; /** * Created by Fechner on 3/11/15. */ import android.util.Base64; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; /** * This class handles the verification of signatures for uW content. */ public class Crypto { /** * TRICKY: We must use Spongy Castle as the security provider because ECDSA is removed from Bouncy Castle in Android * for this we only need the core and prov jars * requires https://rtyley.github.io/spongycastle/ */ static { Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); } /** * Verifies the signature of some data * @param key the public key * @param sig the signature * @param data the data to verify the signature against * @return */ public static Status verifyECDSASignature(PublicKey key, byte[] sig, byte[] data) { String sigAlgorithm = "SHA384WITHECDSA"; if(key != null && data.length > 0 && sig.length > 0) { try { Signature signature = Signature.getInstance(sigAlgorithm); signature.initVerify(key); signature.update(data); if(signature.verify(sig)) { return Status.VERIFIED; } else { return Status.FAILED; } } catch (Exception e) { e.printStackTrace(); } } return Status.ERROR; } /** * Reads a public ECDSA key from a file * The key may contain comments and new lines * @param keyFile * @return */ public PublicKey loadPublicECDSAKey(File keyFile) { try { return loadPublicECDSAKey(new FileInputStream(keyFile)); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } } /** * Reads a public ECDSA key from an input stream * The key may contain comments and new lines * @param keyStream * @return */ public static PublicKey loadPublicECDSAKey(InputStream keyStream) { BufferedReader br = new BufferedReader(new InputStreamReader(keyStream)); StringBuilder sb = new StringBuilder(); String line; try { while ((line = br.readLine()) != null) { if(!line.startsWith("-----")) { sb.append(line); } } } catch (IOException e) { e.printStackTrace(); return null; } if(sb.length() > 0) { String keyString = sb.toString(); return loadPublicECDSAKey(keyString); } else { return null; } } /** * Loads a public key from a string. * The key should not contain any newlines or comments * @param keyString * @return */ public static PublicKey loadPublicECDSAKey(String keyString) { byte[] keyBytes; try { keyBytes = Base64.decode(keyString.getBytes("UTF-8"), Base64.DEFAULT); return loadPublicECDSAKey(keyBytes); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } /** * Loads the public key from a byte array * @param keyBytes the public key * @return if an error occures null will be returned */ public static PublicKey loadPublicECDSAKey(byte[] keyBytes) { X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory; try { keyFactory = KeyFactory.getInstance("ECDSA", "SC"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } catch (NoSuchProviderException e) { e.printStackTrace(); return null; } PublicKey key; try { key = keyFactory.generatePublic(spec); } catch (InvalidKeySpecException e) { e.printStackTrace(); return null; } return key; } /** * Reads in bytes from an input stream * @param is * @return */ public static byte[] readInputStreamToBytes(InputStream is) { byte[] bytes = new byte[0]; try { bytes = new byte[is.available()]; is.read(bytes); } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return bytes; } /** * Returns bytes from a file * If there are exceptions while reading the file an empty byte array will be returned. * @param file * @return */ public byte[] readFileToBytes(File file) { try { return readInputStreamToBytes(new FileInputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); return new byte[0]; } } }