package org.primftpd.util; import android.util.Base64; import org.apache.ftpserver.util.IoUtils; import org.primftpd.pojo.Base64Decoder; import org.primftpd.pojo.KeyParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.List; import java.util.Locale; public class KeyInfoProvider { protected final Logger logger = LoggerFactory.getLogger(getClass()); public String fingerprint(byte[] pubKeyEnc, String hashAlgo) { try { MessageDigest md = MessageDigest.getInstance(hashAlgo); md.update(pubKeyEnc); byte[] fingerPrintBytes = md.digest(); String base64 = Base64.encodeToString(fingerPrintBytes, Base64.NO_PADDING); String beautified = beautify(fingerPrintBytes); return beautified + "\nBase 64\n" + base64; } catch (Exception e) { logger.error("could not read key: " + e.getMessage(), e); } return null; } private static final int BUFFER_SIZE = 4096; public PublicKey readPublicKey(FileInputStream fis) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IoUtils.copy(fis, baos, BUFFER_SIZE); byte[] pubKeyBytes = baos.toByteArray(); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KeyGenerator.KEY_ALGO); PublicKey publicKey = keyFactory.generatePublic(pubKeySpec); return publicKey; } public PrivateKey readPrivatekey(FileInputStream fis) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IoUtils.copy(fis, baos, BUFFER_SIZE); byte[] privKeyBytes = baos.toByteArray(); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KeyGenerator.KEY_ALGO); PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); return privateKey; } protected String beautify(byte[] fingerPrintBytes) { StringBuilder fingerPrint = new StringBuilder(); for (int i=0; i<fingerPrintBytes.length; i++) { byte b = fingerPrintBytes[i]; String hexString = Integer.toHexString(b); if (hexString.length() > 2) { hexString = hexString.substring( hexString.length() - 2, hexString.length()); } else if (hexString.length() < 2) { hexString = "0" + hexString; } fingerPrint.append(hexString.toUpperCase(Locale.ENGLISH)); if (i != fingerPrintBytes.length -1) { fingerPrint.append(":"); if ((i + 1) % 10 == 0) { // force line breaks in UI fingerPrint.append("\n"); } } } return fingerPrint.toString(); } public byte[] encodeAsSsh(RSAPublicKey pubKey) throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); byte[] name = "ssh-rsa".getBytes("US-ASCII"); writeKeyPart(name, buf); writeKeyPart(pubKey.getPublicExponent().toByteArray(), buf); writeKeyPart(pubKey.getModulus().toByteArray(), buf); return buf.toByteArray(); } private void writeKeyPart(byte[] bytes, OutputStream os) throws IOException { for (int shift = 24; shift >= 0; shift -= 8) { os.write((bytes.length >>> shift) & 0xFF); } os.write(bytes); } public List<PublicKey> readKeyAuthKeys(String path, boolean ignoreErrors) { List<PublicKey> keys = null; FileInputStream fis = null; try { fis = new FileInputStream(path); keys = KeyParser.parsePublicKeys( fis, new Base64Decoder() { @Override public byte[] decode(String str) { return Base64.decode(str, Base64.DEFAULT); } }); } catch (Exception e) { if (!ignoreErrors) { logger.error("could not read key auth keys", e); } } finally { try { if (fis != null) { fis.close(); } } catch (IOException e) { if (!ignoreErrors) { logger.error("could not close key auth keys file", e); } } } // there might be more keys added to this list, so don't use emptyList() // see GH issue #68 return keys != null ? keys : new ArrayList<PublicKey>(); } }