blob: a062d62ffd5c139e0994d9c94e608ccafae7d7fc [file] [log] [blame]
/*
* $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/PEMUtil.java $
* $Revision: 153 $
* $Date: 2009-09-15 22:40:53 -0700 (Tue, 15 Sep 2009) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.ssl;
import org.apache.kerby.util.Base64;
import org.apache.kerby.util.ByteArrayReadLine;
import org.apache.kerby.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author Credit Union Central of British Columbia
* @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
* @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
* @since 13-Aug-2006
*/
public class PEMUtil {
static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static byte[] encode(Collection items) throws IOException {
final byte[] lineSeparatorBytes = LINE_SEPARATOR.getBytes("UTF-8");
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
Iterator it = items.iterator();
while (it.hasNext()) {
PEMItem item = (PEMItem) it.next();
out.write("-----BEGIN ".getBytes("UTF-8"));
out.write(item.pemType.getBytes("UTF-8"));
out.write("-----".getBytes("UTF-8"));
out.write(lineSeparatorBytes);
byte[] derBytes = item.getDerBytes();
ByteArrayInputStream bin = new ByteArrayInputStream(derBytes);
byte[] line = Util.streamToBytes(bin, 48);
while (line.length == 48) {
byte[] base64Line = Base64.encodeBase64(line);
out.write(base64Line);
out.write(lineSeparatorBytes);
line = Util.streamToBytes(bin, 48);
}
if (line.length > 0) {
byte[] base64Line = Base64.encodeBase64(line);
out.write(base64Line);
out.write(lineSeparatorBytes);
}
out.write("-----END ".getBytes("UTF-8"));
out.write(item.pemType.getBytes("UTF-8"));
out.write("-----".getBytes("UTF-8"));
out.write(lineSeparatorBytes);
}
return out.toByteArray();
}
public static List decode(byte[] pemBytes) {
LinkedList pemItems = new LinkedList();
ByteArrayInputStream in = new ByteArrayInputStream(pemBytes);
ByteArrayReadLine readLine = new ByteArrayReadLine(in);
String line = readLine.next();
while (line != null) {
int len = 0;
byte[] decoded;
ArrayList listOfByteArrays = new ArrayList(64);
Map properties = new HashMap();
String type = "[unknown]";
while (line != null && !beginBase64(line)) {
line = readLine.next();
}
if (line != null) {
String upperLine = line.toUpperCase();
int x = upperLine.indexOf("-BEGIN") + "-BEGIN".length();
int y = upperLine.indexOf("-", x);
type = upperLine.substring(x, y).trim();
line = readLine.next();
}
while (line != null && !endBase64(line)) {
line = Util.trim(line);
if (!"".equals(line)) {
int x = line.indexOf(':');
if (x > 0) {
String k = line.substring(0, x).trim();
String v = "";
if (line.length() > x + 1) {
v = line.substring(x + 1).trim();
}
properties.put(k.toLowerCase(), v.toLowerCase());
} else {
byte[] base64 = line.getBytes();
byte[] rawBinary = Base64.decodeBase64(base64);
listOfByteArrays.add(rawBinary);
len += rawBinary.length;
}
}
line = readLine.next();
}
if (line != null) {
line = readLine.next();
}
if (!listOfByteArrays.isEmpty()) {
decoded = new byte[len];
int pos = 0;
Iterator it = listOfByteArrays.iterator();
while (it.hasNext()) {
byte[] oneLine = (byte[]) it.next();
System.arraycopy(oneLine, 0, decoded, pos, oneLine.length);
pos += oneLine.length;
}
PEMItem item = new PEMItem(decoded, type, properties);
pemItems.add(item);
}
}
// closing ByteArrayInputStream is a NO-OP
// in.close();
return pemItems;
}
private static boolean beginBase64(String line) {
line = line != null ? line.trim().toUpperCase() : "";
int x = line.indexOf("-BEGIN");
return x > 0 && startsAndEndsWithDashes(line);
}
private static boolean endBase64(String line) {
line = line != null ? line.trim().toUpperCase() : "";
int x = line.indexOf("-END");
return x > 0 && startsAndEndsWithDashes(line);
}
private static boolean startsAndEndsWithDashes(String line) {
line = Util.trim(line);
char c = line.charAt(0);
char d = line.charAt(line.length() - 1);
return c == '-' && d == '-';
}
public static String formatRSAPrivateKey(RSAPrivateCrtKey key) {
StringBuffer buf = new StringBuffer(2048);
buf.append("Private-Key:");
buf.append(LINE_SEPARATOR);
buf.append("modulus:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getModulus(), 129 * 2));
buf.append(LINE_SEPARATOR);
buf.append("publicExponent: ");
buf.append(key.getPublicExponent());
buf.append(LINE_SEPARATOR);
buf.append("privateExponent:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getPrivateExponent(), 128 * 2));
buf.append(LINE_SEPARATOR);
buf.append("prime1:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getPrimeP(), 65 * 2));
buf.append(LINE_SEPARATOR);
buf.append("prime2:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getPrimeQ(), 65 * 2));
buf.append(LINE_SEPARATOR);
buf.append("exponent1:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getPrimeExponentP(), 65 * 2));
buf.append(LINE_SEPARATOR);
buf.append("exponent2:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getPrimeExponentQ(), 65 * 2));
buf.append(LINE_SEPARATOR);
buf.append("coefficient:");
buf.append(LINE_SEPARATOR);
buf.append(formatBigInteger(key.getCrtCoefficient(), 65 * 2));
return buf.toString();
}
public static String formatBigInteger(BigInteger bi, int length) {
String s = bi.toString(16);
StringBuffer buf = new StringBuffer(s.length());
int zeroesToAppend = length - s.length();
int count = 0;
buf.append(" ");
for (int i = 0; i < zeroesToAppend; i++) {
count++;
buf.append('0');
if (i % 2 == 1) {
buf.append(':');
}
}
for (int i = 0; i < s.length() - 2; i++) {
count++;
buf.append(s.charAt(i));
if (i % 2 == 1) {
buf.append(':');
}
if (count % 30 == 0) {
buf.append(LINE_SEPARATOR);
buf.append(" ");
}
}
buf.append(s.substring(s.length() - 2));
return buf.toString();
}
public static String toPem(PublicKey key) throws IOException {
PEMItem item = null;
if (key instanceof RSAPublicKey) {
item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
} else if (key instanceof DSAPublicKey) {
item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
} else {
throw new IOException("Not an RSA or DSA key");
}
byte[] pem = encode(Collections.singleton(item));
return new String(pem, "UTF-8");
}
}