001/* 002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/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 032package org.apache.commons.ssl; 033 034import org.apache.commons.ssl.util.ByteArrayReadLine; 035 036import java.io.ByteArrayInputStream; 037import java.io.ByteArrayOutputStream; 038import java.io.IOException; 039import java.math.BigInteger; 040import java.security.interfaces.RSAPrivateCrtKey; 041import java.security.interfaces.RSAPublicKey; 042import java.security.interfaces.DSAPublicKey; 043import java.security.PublicKey; 044import 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 */ 052public 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}