001 /*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/src/java/org/apache/commons/ssl/PEMUtil.java $
003 * $Revision: 153 $
004 * $Date: 2009-09-15 22:40:53 -0700 (Tue, 15 Sep 2009) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements. See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership. The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License. You may obtain a copy of the License at
014 *
015 * http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied. See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation. For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032 package org.apache.commons.ssl;
033
034 import org.apache.commons.ssl.util.ByteArrayReadLine;
035
036 import java.io.ByteArrayInputStream;
037 import java.io.ByteArrayOutputStream;
038 import java.io.IOException;
039 import java.math.BigInteger;
040 import java.security.interfaces.RSAPrivateCrtKey;
041 import java.security.interfaces.RSAPublicKey;
042 import java.security.interfaces.DSAPublicKey;
043 import java.security.PublicKey;
044 import java.util.*;
045
046 /**
047 * @author Credit Union Central of British Columbia
048 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
049 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
050 * @since 13-Aug-2006
051 */
052 public class PEMUtil {
053 final static String LINE_SEPARATOR = System.getProperty("line.separator");
054
055 public static byte[] encode(Collection items) throws IOException {
056 final byte[] LINE_SEPARATOR_BYTES = LINE_SEPARATOR.getBytes("UTF-8");
057 ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
058 Iterator it = items.iterator();
059 while (it.hasNext()) {
060 PEMItem item = (PEMItem) it.next();
061 out.write("-----BEGIN ".getBytes("UTF-8"));
062 out.write(item.pemType.getBytes("UTF-8"));
063 out.write("-----".getBytes("UTF-8"));
064 out.write(LINE_SEPARATOR_BYTES);
065
066 byte[] derBytes = item.getDerBytes();
067 ByteArrayInputStream bin = new ByteArrayInputStream(derBytes);
068 byte[] line = Util.streamToBytes(bin, 48);
069 while (line.length == 48) {
070 byte[] base64Line = Base64.encodeBase64(line);
071 out.write(base64Line);
072 out.write(LINE_SEPARATOR_BYTES);
073 line = Util.streamToBytes(bin, 48);
074 }
075 if (line.length > 0) {
076 byte[] base64Line = Base64.encodeBase64(line);
077 out.write(base64Line);
078 out.write(LINE_SEPARATOR_BYTES);
079 }
080 out.write("-----END ".getBytes("UTF-8"));
081 out.write(item.pemType.getBytes("UTF-8"));
082 out.write("-----".getBytes("UTF-8"));
083 out.write(LINE_SEPARATOR_BYTES);
084 }
085 return out.toByteArray();
086 }
087
088 public static List decode(byte[] pemBytes) {
089 LinkedList pemItems = new LinkedList();
090 ByteArrayInputStream in = new ByteArrayInputStream(pemBytes);
091 ByteArrayReadLine readLine = new ByteArrayReadLine(in);
092 String line = readLine.next();
093 while (line != null) {
094 int len = 0;
095 byte[] decoded;
096 ArrayList listOfByteArrays = new ArrayList(64);
097 Map properties = new HashMap();
098 String type = "[unknown]";
099 while (line != null && !beginBase64(line)) {
100 line = readLine.next();
101 }
102 if (line != null) {
103 String upperLine = line.toUpperCase();
104 int x = upperLine.indexOf("-BEGIN") + "-BEGIN".length();
105 int y = upperLine.indexOf("-", x);
106 type = upperLine.substring(x, y).trim();
107 line = readLine.next();
108 }
109 while (line != null && !endBase64(line)) {
110 line = Util.trim(line);
111 if (!"".equals(line)) {
112 int x = line.indexOf(':');
113 if (x > 0) {
114 String k = line.substring(0, x).trim();
115 String v = "";
116 if (line.length() > x + 1) {
117 v = line.substring(x + 1).trim();
118 }
119 properties.put(k.toLowerCase(), v.toLowerCase());
120 } else {
121 byte[] base64 = line.getBytes();
122 byte[] rawBinary = Base64.decodeBase64(base64);
123 listOfByteArrays.add(rawBinary);
124 len += rawBinary.length;
125 }
126 }
127 line = readLine.next();
128 }
129 if (line != null) {
130 line = readLine.next();
131 }
132
133 if (!listOfByteArrays.isEmpty()) {
134 decoded = new byte[len];
135 int pos = 0;
136 Iterator it = listOfByteArrays.iterator();
137 while (it.hasNext()) {
138 byte[] oneLine = (byte[]) it.next();
139 System.arraycopy(oneLine, 0, decoded, pos, oneLine.length);
140 pos += oneLine.length;
141 }
142 PEMItem item = new PEMItem(decoded, type, properties);
143 pemItems.add(item);
144 }
145 }
146
147 // closing ByteArrayInputStream is a NO-OP
148 // in.close();
149
150 return pemItems;
151 }
152
153 private static boolean beginBase64(String line) {
154 line = line != null ? line.trim().toUpperCase() : "";
155 int x = line.indexOf("-BEGIN");
156 return x > 0 && startsAndEndsWithDashes(line);
157 }
158
159 private static boolean endBase64(String line) {
160 line = line != null ? line.trim().toUpperCase() : "";
161 int x = line.indexOf("-END");
162 return x > 0 && startsAndEndsWithDashes(line);
163 }
164
165 private static boolean startsAndEndsWithDashes(String line) {
166 line = Util.trim(line);
167 char c = line.charAt(0);
168 char d = line.charAt(line.length() - 1);
169 return c == '-' && d == '-';
170 }
171
172 public static String formatRSAPrivateKey(RSAPrivateCrtKey key) {
173 StringBuffer buf = new StringBuffer(2048);
174 buf.append("Private-Key:");
175 buf.append(LINE_SEPARATOR);
176 buf.append("modulus:");
177 buf.append(LINE_SEPARATOR);
178 buf.append(formatBigInteger(key.getModulus(), 129 * 2));
179 buf.append(LINE_SEPARATOR);
180 buf.append("publicExponent: ");
181 buf.append(key.getPublicExponent());
182 buf.append(LINE_SEPARATOR);
183 buf.append("privateExponent:");
184 buf.append(LINE_SEPARATOR);
185 buf.append(formatBigInteger(key.getPrivateExponent(), 128 * 2));
186 buf.append(LINE_SEPARATOR);
187 buf.append("prime1:");
188 buf.append(LINE_SEPARATOR);
189 buf.append(formatBigInteger(key.getPrimeP(), 65 * 2));
190 buf.append(LINE_SEPARATOR);
191 buf.append("prime2:");
192 buf.append(LINE_SEPARATOR);
193 buf.append(formatBigInteger(key.getPrimeQ(), 65 * 2));
194 buf.append(LINE_SEPARATOR);
195 buf.append("exponent1:");
196 buf.append(LINE_SEPARATOR);
197 buf.append(formatBigInteger(key.getPrimeExponentP(), 65 * 2));
198 buf.append(LINE_SEPARATOR);
199 buf.append("exponent2:");
200 buf.append(LINE_SEPARATOR);
201 buf.append(formatBigInteger(key.getPrimeExponentQ(), 65 * 2));
202 buf.append(LINE_SEPARATOR);
203 buf.append("coefficient:");
204 buf.append(LINE_SEPARATOR);
205 buf.append(formatBigInteger(key.getCrtCoefficient(), 65 * 2));
206 return buf.toString();
207 }
208
209 public static String formatBigInteger(BigInteger bi, int length) {
210 String s = bi.toString(16);
211 StringBuffer buf = new StringBuffer(s.length());
212 int zeroesToAppend = length - s.length();
213 int count = 0;
214 buf.append(" ");
215 for (int i = 0; i < zeroesToAppend; i++) {
216 count++;
217 buf.append('0');
218 if (i % 2 == 1) {
219 buf.append(':');
220 }
221 }
222 for (int i = 0; i < s.length() - 2; i++) {
223 count++;
224 buf.append(s.charAt(i));
225 if (i % 2 == 1) {
226 buf.append(':');
227 }
228 if (count % 30 == 0) {
229 buf.append(LINE_SEPARATOR);
230 buf.append(" ");
231 }
232 }
233 buf.append(s.substring(s.length() - 2));
234 return buf.toString();
235 }
236
237 public static String toPem(PublicKey key) throws IOException {
238 PEMItem item = null;
239 if (key instanceof RSAPublicKey) {
240 item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
241 } else if (key instanceof DSAPublicKey) {
242 item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
243 } else {
244 throw new IOException("Not an RSA or DSA key");
245 }
246 byte[] pem = encode(Collections.singleton(item));
247 return new String(pem, "UTF-8");
248 }
249
250 }