001/* 002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.9/src/java/org/apache/commons/ssl/PEMUtil.java $ 003 * $Revision: 121 $ 004 * $Date: 2007-11-13 21:26:57 -0800 (Tue, 13 Nov 2007) $ 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 032package org.apache.commons.ssl; 033 034import java.io.ByteArrayInputStream; 035import java.io.ByteArrayOutputStream; 036import java.io.IOException; 037import java.math.BigInteger; 038import java.security.interfaces.RSAPrivateCrtKey; 039import java.util.ArrayList; 040import java.util.Collection; 041import java.util.HashMap; 042import java.util.Iterator; 043import java.util.LinkedList; 044import java.util.List; 045import java.util.Map; 046 047/** 048 * @author Credit Union Central of British Columbia 049 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a> 050 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a> 051 * @since 13-Aug-2006 052 */ 053public class PEMUtil { 054 final static String LINE_SEPARATOR = System.getProperty("line.separator"); 055 056 public static byte[] encode(Collection items) throws IOException { 057 final byte[] LINE_SEPARATOR_BYTES = LINE_SEPARATOR.getBytes("UTF-8"); 058 ByteArrayOutputStream out = new ByteArrayOutputStream(4096); 059 Iterator it = items.iterator(); 060 while (it.hasNext()) { 061 PEMItem item = (PEMItem) it.next(); 062 out.write("-----BEGIN ".getBytes("UTF-8")); 063 out.write(item.pemType.getBytes("UTF-8")); 064 out.write("-----".getBytes("UTF-8")); 065 out.write(LINE_SEPARATOR_BYTES); 066 067 byte[] derBytes = item.getDerBytes(); 068 ByteArrayInputStream bin = new ByteArrayInputStream(derBytes); 069 byte[] line = Util.streamToBytes(bin, 48); 070 while (line.length == 48) { 071 byte[] base64Line = Base64.encodeBase64(line); 072 out.write(base64Line); 073 out.write(LINE_SEPARATOR_BYTES); 074 line = Util.streamToBytes(bin, 48); 075 } 076 if (line.length > 0) { 077 byte[] base64Line = Base64.encodeBase64(line); 078 out.write(base64Line); 079 out.write(LINE_SEPARATOR_BYTES); 080 } 081 out.write("-----END ".getBytes("UTF-8")); 082 out.write(item.pemType.getBytes("UTF-8")); 083 out.write("-----".getBytes("UTF-8")); 084 out.write(LINE_SEPARATOR_BYTES); 085 } 086 return out.toByteArray(); 087 } 088 089 public static List decode(byte[] pemBytes) { 090 LinkedList pemItems = new LinkedList(); 091 ByteArrayInputStream in = new ByteArrayInputStream(pemBytes); 092 String line = Util.readLine(in); 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 = Util.readLine(in); 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 = Util.readLine(in); 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 = Util.readLine(in); 128 } 129 if (line != null) { 130 line = Util.readLine(in); 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 238}