/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.verity; import java.lang.reflect.Constructor; import java.io.File; import java.io.ByteArrayInputStream; import java.io.Console; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.KeyFactory; import java.security.Provider; import java.security.Security; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECPublicKeySpec; import java.security.spec.ECPrivateKeySpec; import java.security.spec.X509EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.util.encoders.Base64; public class Utils { private static final Map ID_TO_ALG; private static final Map ALG_TO_ID; static { ID_TO_ALG = new HashMap(); ALG_TO_ID = new HashMap(); ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA"); ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA"); ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA"); ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA"); ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA"); ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA"); ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId()); ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId()); ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId()); ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId()); ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId()); ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId()); } private static void loadProviderIfNecessary(String providerClassName) { if (providerClassName == null) { return; } final Class klass; try { final ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); if (sysLoader != null) { klass = sysLoader.loadClass(providerClassName); } else { klass = Class.forName(providerClassName); } } catch (ClassNotFoundException e) { e.printStackTrace(); System.exit(1); return; } Constructor constructor = null; for (Constructor c : klass.getConstructors()) { if (c.getParameterTypes().length == 0) { constructor = c; break; } } if (constructor == null) { System.err.println("No zero-arg constructor found for " + providerClassName); System.exit(1); return; } final Object o; try { o = constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); System.exit(1); return; } if (!(o instanceof Provider)) { System.err.println("Not a Provider class: " + providerClassName); System.exit(1); } Security.insertProviderAt((Provider) o, 1); } static byte[] pemToDer(String pem) throws Exception { pem = pem.replaceAll("^-.*", ""); String base64_der = pem.replaceAll("-.*$", ""); return Base64.decode(base64_der); } private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey) throws GeneralSecurityException { EncryptedPrivateKeyInfo epkInfo; try { epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey); } catch (IOException ex) { // Probably not an encrypted key. return null; } char[] password = System.console().readPassword("Password for the private key file: "); SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName()); Key key = skFactory.generateSecret(new PBEKeySpec(password)); Arrays.fill(password, '\0'); Cipher cipher = Cipher.getInstance(epkInfo.getAlgName()); cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters()); try { return epkInfo.getKeySpec(cipher); } catch (InvalidKeySpecException ex) { System.err.println("Password may be bad."); throw ex; } } static PrivateKey loadDERPrivateKey(byte[] der) throws Exception { PKCS8EncodedKeySpec spec = decryptPrivateKey(der); if (spec == null) { spec = new PKCS8EncodedKeySpec(der); } ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded())); PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); return KeyFactory.getInstance(algOid).generatePrivate(spec); } static PrivateKey loadPEMPrivateKey(byte[] pem) throws Exception { byte[] der = pemToDer(new String(pem)); return loadDERPrivateKey(der); } static PrivateKey loadPEMPrivateKeyFromFile(String keyFname) throws Exception { return loadPEMPrivateKey(read(keyFname)); } static PrivateKey loadDERPrivateKeyFromFile(String keyFname) throws Exception { return loadDERPrivateKey(read(keyFname)); } static PublicKey loadDERPublicKey(byte[] der) throws Exception { X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePublic(publicKeySpec); } static PublicKey loadPEMPublicKey(byte[] pem) throws Exception { byte[] der = pemToDer(new String(pem)); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePublic(publicKeySpec); } static PublicKey loadPEMPublicKeyFromFile(String keyFname) throws Exception { return loadPEMPublicKey(read(keyFname)); } static PublicKey loadDERPublicKeyFromFile(String keyFname) throws Exception { return loadDERPublicKey(read(keyFname)); } static X509Certificate loadPEMCertificate(String fname) throws Exception { try (FileInputStream fis = new FileInputStream(fname)) { CertificateFactory cf = CertificateFactory.getInstance("X.509"); return (X509Certificate) cf.generateCertificate(fis); } } private static String getSignatureAlgorithm(Key key) throws Exception { if ("EC".equals(key.getAlgorithm())) { int curveSize; KeyFactory factory = KeyFactory.getInstance("EC"); if (key instanceof PublicKey) { ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class); curveSize = spec.getParams().getCurve().getField().getFieldSize(); } else if (key instanceof PrivateKey) { ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class); curveSize = spec.getParams().getCurve().getField().getFieldSize(); } else { throw new InvalidKeySpecException(); } if (curveSize <= 256) { return "SHA256withECDSA"; } else if (curveSize <= 384) { return "SHA384withECDSA"; } else { return "SHA512withECDSA"; } } else if ("RSA".equals(key.getAlgorithm())) { return "SHA256withRSA"; } else { throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); } } static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception { String id = ALG_TO_ID.get(getSignatureAlgorithm(key)); if (id == null) { throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); } return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id)); } static boolean verify(PublicKey key, byte[] input, byte[] signature, AlgorithmIdentifier algId) throws Exception { String algName = ID_TO_ALG.get(algId.getAlgorithm().getId()); if (algName == null) { throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm()); } Signature verifier = Signature.getInstance(algName); verifier.initVerify(key); verifier.update(input); return verifier.verify(signature); } static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception { Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey)); signer.initSign(privateKey); signer.update(input); return signer.sign(); } static byte[] read(String fname) throws Exception { long offset = 0; File f = new File(fname); long length = f.length(); byte[] image = new byte[(int)length]; FileInputStream fis = new FileInputStream(f); while (offset < length) { offset += fis.read(image, (int)offset, (int)(length - offset)); } fis.close(); return image; } static void write(byte[] data, String fname) throws Exception{ FileOutputStream out = new FileOutputStream(fname); out.write(data); out.close(); } }