148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/* 248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. 348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Please refer to the LICENSE.txt for licensing details. 448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpackage ch.ethz.ssh2.crypto; 648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.BufferedReader; 848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.CharArrayReader; 948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.IOException; 1048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.math.BigInteger; 1148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 1248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.AES; 1348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.BlockCipher; 1448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.CBCMode; 1548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.DES; 1648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.DESede; 1748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.digest.MD5; 1848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.signature.DSAPrivateKey; 1948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.signature.RSAPrivateKey; 2048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.util.StringEncoder; 2148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 2248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/** 2348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * PEM Support. 2448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * 2548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @author Christian Plattner 2648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @version $Id: PEMDecoder.java 37 2011-05-28 22:31:46Z dkocher@sudo.ch $ 2748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */ 2848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpublic class PEMDecoder 2948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood{ 3048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static final int PEM_RSA_PRIVATE_KEY = 1; 3148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static final int PEM_DSA_PRIVATE_KEY = 2; 3248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 3348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static int hexToInt(char c) 3448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 3548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((c >= 'a') && (c <= 'f')) 3648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 3748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return (c - 'a') + 10; 3848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 3948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 4048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((c >= 'A') && (c <= 'F')) 4148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 4248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return (c - 'A') + 10; 4348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 4448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 4548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((c >= '0') && (c <= '9')) 4648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 4748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return (c - '0'); 4848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 4948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 5048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IllegalArgumentException("Need hex char"); 5148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 5248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 5348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static byte[] hexToByteArray(String hex) 5448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 5548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (hex == null) 5648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IllegalArgumentException("null argument"); 5748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 5848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((hex.length() % 2) != 0) 5948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IllegalArgumentException("Uneven string length in hex encoding."); 6048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 6148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte decoded[] = new byte[hex.length() / 2]; 6248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 6348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (int i = 0; i < decoded.length; i++) 6448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 6548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int hi = hexToInt(hex.charAt(i * 2)); 6648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int lo = hexToInt(hex.charAt((i * 2) + 1)); 6748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 6848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood decoded[i] = (byte) (hi * 16 + lo); 6948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 7048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return decoded; 7248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 7348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 7448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen) 7548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throws IOException 7648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 7748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (salt.length < 8) 7848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation."); 7948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood MD5 md5 = new MD5(); 8148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] key = new byte[keyLen]; 8348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] tmp = new byte[md5.getDigestLength()]; 8448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 8548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 8648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 8748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood md5.update(password, 0, password.length); 8848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the salt in this step. 8948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // This took me two hours until I got AES-xxx running. 9048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 9148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int copy = (keyLen < tmp.length) ? keyLen : tmp.length; 9248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 9348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood md5.digest(tmp, 0); 9448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 9548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood System.arraycopy(tmp, 0, key, key.length - keyLen, copy); 9648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 9748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood keyLen -= copy; 9848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 9948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (keyLen == 0) 10048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return key; 10148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood md5.update(tmp, 0, tmp.length); 10348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 10448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 10548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 10648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static byte[] removePadding(byte[] buff, int blockSize) throws IOException 10748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 10848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Removes RFC 1423/PKCS #7 padding */ 10948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 11048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int rfc_1423_padding = buff[buff.length - 1] & 0xff; 11148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 11248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize)) 11348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?"); 11448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 11548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (int i = 2; i <= rfc_1423_padding; i++) 11648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 11748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (buff[buff.length - i] != rfc_1423_padding) 11848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?"); 11948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 12048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 12148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] tmp = new byte[buff.length - rfc_1423_padding]; 12248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding); 12348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return tmp; 12448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 12548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 12648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static PEMStructure parsePEM(char[] pem) throws IOException 12748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 12848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood PEMStructure ps = new PEMStructure(); 12948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String line = null; 13148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BufferedReader br = new BufferedReader(new CharArrayReader(pem)); 13348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String endLine = null; 13548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 13648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 13748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 13848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = br.readLine(); 13948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 14048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line == null) 14148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Invalid PEM structure, '-----BEGIN...' missing"); 14248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 14348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = line.trim(); 14448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 14548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) 14648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 14748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood endLine = "-----END DSA PRIVATE KEY-----"; 14848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.pemType = PEM_DSA_PRIVATE_KEY; 14948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 15048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 15148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 15248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----")) 15348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 15448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood endLine = "-----END RSA PRIVATE KEY-----"; 15548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.pemType = PEM_RSA_PRIVATE_KEY; 15648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 15748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 15848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 15948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 16048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 16148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 16248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = br.readLine(); 16348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 16448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line == null) 16548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Invalid PEM structure, " + endLine + " missing"); 16648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 16748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = line.trim(); 16848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 16948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood int sem_idx = line.indexOf(':'); 17048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 17148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (sem_idx == -1) 17248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 17348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 17448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String name = line.substring(0, sem_idx + 1); 17548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String value = line.substring(sem_idx + 1); 17648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 17748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String values[] = value.split(","); 17848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 17948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (int i = 0; i < values.length; i++) 18048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood values[i] = values[i].trim(); 18148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 18248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // Proc-Type: 4,ENCRYPTED 18348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483 18448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 18548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ("Proc-Type:".equals(name)) 18648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 18748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.procType = values; 18848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood continue; 18948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 19148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ("DEK-Info:".equals(name)) 19248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 19348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.dekInfo = values; 19448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood continue; 19548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Ignore line */ 19748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 19848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 19948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood StringBuilder keyData = new StringBuilder(); 20048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood while (true) 20248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 20348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line == null) 20448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Invalid PEM structure, " + endLine + " missing"); 20548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = line.trim(); 20748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 20848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (line.startsWith(endLine)) 20948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood break; 21048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood keyData.append(line); 21248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood line = br.readLine(); 21448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 21548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood char[] pem_chars = new char[keyData.length()]; 21748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood keyData.getChars(0, pem_chars.length, pem_chars, 0); 21848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 21948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.data = Base64.decode(pem_chars); 22048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 22148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.data.length == 0) 22248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Invalid PEM structure, no data available"); 22348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 22448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return ps; 22548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 22648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 22748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood private static void decryptPEM(PEMStructure ps, byte[] pw) throws IOException 22848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 22948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.dekInfo == null) 23048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Broken PEM, no mode and salt given, but encryption enabled"); 23148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 23248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.dekInfo.length != 2) 23348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Broken PEM, DEK-Info is incomplete!"); 23448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 23548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood String algo = ps.dekInfo[0]; 23648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] salt = hexToByteArray(ps.dekInfo[1]); 23748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 23848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BlockCipher bc = null; 23948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 24048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (algo.equals("DES-EDE3-CBC")) 24148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 24248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood DESede des3 = new DESede(); 24348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); 24448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc = new CBCMode(des3, salt, false); 24548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 24648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else if (algo.equals("DES-CBC")) 24748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 24848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood DES des = new DES(); 24948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8)); 25048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc = new CBCMode(des, salt, false); 25148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 25248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else if (algo.equals("AES-128-CBC")) 25348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 25448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood AES aes = new AES(); 25548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16)); 25648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc = new CBCMode(aes, salt, false); 25748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 25848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else if (algo.equals("AES-192-CBC")) 25948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 26048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood AES aes = new AES(); 26148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24)); 26248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc = new CBCMode(aes, salt, false); 26348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 26448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else if (algo.equals("AES-256-CBC")) 26548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 26648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood AES aes = new AES(); 26748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32)); 26848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc = new CBCMode(aes, salt, false); 26948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 27048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood else 27148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 27248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo); 27348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 27448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 27548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((ps.data.length % bc.getBlockSize()) != 0) 27648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of " 27748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood + bc.getBlockSize()); 27848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 27948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Now decrypt the content */ 28048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 28148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] dz = new byte[ps.data.length]; 28248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 28348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++) 28448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 28548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize()); 28648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 28748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 28848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood /* Now check and remove RFC 1423/PKCS #7 padding */ 28948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dz = removePadding(dz, bc.getBlockSize()); 29148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.data = dz; 29348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.dekInfo = null; 29448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood ps.procType = null; 29548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 29648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 29748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException 29848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 29948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.procType == null) 30048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return false; 30148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 30248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.procType.length != 2) 30348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Unknown Proc-Type field."); 30448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 30548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ("4".equals(ps.procType[0]) == false) 30648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")"); 30748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 30848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ("ENCRYPTED".equals(ps.procType[1])) 30948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return true; 31048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 31148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return false; 31248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 31348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 31448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static final boolean isPEMEncrypted(char[] pem) throws IOException 31548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 31648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return isPEMEncrypted(parsePEM(pem)); 31748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 31848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 31948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood public static Object decode(char[] pem, String password) throws IOException 32048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 32148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood PEMStructure ps = parsePEM(pem); 32248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 32348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (isPEMEncrypted(ps)) 32448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 32548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (password == null) 32648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("PEM is encrypted, but no password was specified"); 32748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 32848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood decryptPEM(ps, StringEncoder.GetBytes(password)); 32948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 33048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 33148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.pemType == PEM_DSA_PRIVATE_KEY) 33248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 33348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood SimpleDERReader dr = new SimpleDERReader(ps.data); 33448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 33548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] seq = dr.readSequenceAsByteArray(); 33648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 33748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (dr.available() != 0) 33848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Padding in DSA PRIVATE KEY DER stream."); 33948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dr.resetInput(seq); 34148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger version = dr.readInt(); 34348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (version.compareTo(BigInteger.ZERO) != 0) 34548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream."); 34648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 34748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger p = dr.readInt(); 34848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger q = dr.readInt(); 34948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger g = dr.readInt(); 35048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger y = dr.readInt(); 35148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger x = dr.readInt(); 35248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (dr.available() != 0) 35448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Padding in DSA PRIVATE KEY DER stream."); 35548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return new DSAPrivateKey(p, q, g, y, x); 35748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 35848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 35948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (ps.pemType == PEM_RSA_PRIVATE_KEY) 36048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood { 36148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood SimpleDERReader dr = new SimpleDERReader(ps.data); 36248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 36348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood byte[] seq = dr.readSequenceAsByteArray(); 36448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 36548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if (dr.available() != 0) 36648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Padding in RSA PRIVATE KEY DER stream."); 36748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 36848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood dr.resetInput(seq); 36948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 37048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger version = dr.readInt(); 37148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 37248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0)) 37348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream."); 37448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 37548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger n = dr.readInt(); 37648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger e = dr.readInt(); 37748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood BigInteger d = dr.readInt(); 37848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 37948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood return new RSAPrivateKey(d, e, n); 38048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 38148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood throw new IOException("PEM problem: it is of unknown type"); 38348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood } 38448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood 38548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood} 386