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