/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sshtools.publickey;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.Hashtable;
import com.sshtools.ssh.components.SshCipher;
import com.sshtools.ssh.components.jce.AES128Cbc;
import com.sshtools.ssh.components.jce.TripleDesCbc;
import com.sshtools.util.Base64;
class PEMReader extends PEM {
private LineNumberReader reader;
private String type;
private Hashtable<String, String> header;
private byte[] payload;
/**
* Creates a new PEMReader object.
*
* @param r
*/
public PEMReader(Reader r) throws IOException {
reader = new LineNumberReader(r);
read();
}
private void read() throws IOException {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith(PEM_BOUNDARY) && line.endsWith(PEM_BOUNDARY)) {
if (line.startsWith(PEM_BEGIN)) {
type = line.substring(PEM_BEGIN.length(), line.length()
- PEM_BOUNDARY.length());
break;
}
throw new IOException("Invalid PEM boundary at line "
+ reader.getLineNumber() + ": " + line);
}
}
header = new Hashtable<String, String>();
while ((line = reader.readLine()) != null) {
int colon = line.indexOf(':');
if (colon == -1) {
break;
}
String key = line.substring(0, colon).trim();
if (line.endsWith("\\")) {
String v = line.substring(colon + 1, line.length() - 1).trim();
// multi-line value
StringBuffer value = new StringBuffer(v);
while ((line = reader.readLine()) != null) {
if (line.endsWith("\\")) {
value.append(" ").append(
line.substring(0, line.length() - 1).trim());
} else {
value.append(" ").append(line.trim());
break;
}
}
} else {
String value = line.substring(colon + 1).trim();
header.put(key, value);
}
}
// first line that is not part of the header
// could be an empty line, but if there is no header and the body begins
// straight after the -----
// then this line contains data
if (line == null) {
throw new IOException(
"The key format is invalid! OpenSSH formatted keys must begin with -----BEGIN RSA or -----BEGIN DSA");
}
StringBuffer body = new StringBuffer(line);
while ((line = reader.readLine()) != null) {
if (line.startsWith(PEM_BOUNDARY) && line.endsWith(PEM_BOUNDARY)) {
if (line.startsWith(PEM_END + type)) {
break;
}
throw new IOException("Invalid PEM end boundary at line "
+ reader.getLineNumber() + ": " + line);
}
body.append(line);
}
payload = Base64.decode(body.toString());
}
/**
*
*
* @return Hashtable
*/
public Hashtable<String, String> getHeader() {
return header;
}
/**
*
*
* @return byte[]
*/
public byte[] getPayload() {
return payload;
}
/**
*
*
* @return String
*/
public String getType() {
return type;
}
/**
*
*
* @param passphrase
*
* @return byte[]
*
*/
public byte[] decryptPayload(String passphrase) throws IOException {
String dekInfo = (String) header.get("DEK-Info");
if (dekInfo != null) {
int comma = dekInfo.indexOf(',');
String keyAlgorithm = dekInfo.substring(0, comma);
if (!"DES-EDE3-CBC".equalsIgnoreCase(keyAlgorithm)
&& !"AES-128-CBC".equalsIgnoreCase(keyAlgorithm)) {
throw new IOException("Unsupported passphrase algorithm: "
+ keyAlgorithm);
}
String ivString = dekInfo.substring(comma + 1);
byte[] iv = new byte[ivString.length() / 2];
for (int i = 0; i < ivString.length(); i += 2) {
iv[i / 2] = (byte) Integer.parseInt(
ivString.substring(i, i + 2), 16);
}
byte[] keydata = null;
SshCipher cipher = null;
if ("DES-EDE3-CBC".equalsIgnoreCase(keyAlgorithm)) {
keydata = getKeyFromPassphrase(passphrase, iv, 24);
cipher = new TripleDesCbc();
} else if ("AES-128-CBC".equalsIgnoreCase(keyAlgorithm)) {
keydata = getKeyFromPassphrase(passphrase, iv, 16);
cipher = new AES128Cbc();
}
cipher.init(SshCipher.DECRYPT_MODE, iv, keydata);
byte[] plain = new byte[payload.length];
cipher.transform(payload, 0, plain, 0, plain.length);
return plain;
}
return payload;
}
}