package org.openstack.atlas.util.ca; import org.openstack.atlas.util.ca.primitives.Debug; import java.util.Set; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMReader; import org.bouncycastle.openssl.PEMWriter; import org.openstack.atlas.util.ca.primitives.RsaConst; import org.openstack.atlas.util.ca.primitives.PemBlock; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.security.Provider; import java.util.logging.Level; import java.util.logging.Logger; import org.openstack.atlas.util.ca.exceptions.PemException; import java.security.Security; import java.util.ArrayList; import java.util.List; import java.nio.charset.Charset; import org.openstack.atlas.util.ca.exceptions.NotAPemObject; import org.openstack.atlas.util.ca.primitives.ByteLineReader; import static org.openstack.atlas.util.ca.primitives.ByteLineReader.cmpBytes; import static org.openstack.atlas.util.ca.primitives.ByteLineReader.appendLF; public class PemUtils { public static final byte[] BEG_PRV; public static final byte[] END_PRV; public static final byte[] BEG_CSR; public static final byte[] END_CSR; public static final byte[] BEG_CRT; public static final byte[] END_CRT; public static final byte[] BEG_RSA; public static final byte[] END_RSA; public static final byte[][] BEG_LINES; public static final byte[][] END_LINES; private static final int CR = 13; private static final int LF = 10; private static final int PAGESIZE = 4096; static { RsaConst.init(); BEG_PRV = StringUtils.asciiBytes("-----BEGIN RSA PRIVATE KEY-----"); END_PRV = StringUtils.asciiBytes("-----END RSA PRIVATE KEY-----"); BEG_CSR = StringUtils.asciiBytes("-----BEGIN CERTIFICATE REQUEST-----"); END_CSR = StringUtils.asciiBytes("-----END CERTIFICATE REQUEST-----"); BEG_CRT = StringUtils.asciiBytes("-----BEGIN CERTIFICATE-----"); END_CRT = StringUtils.asciiBytes("-----END CERTIFICATE-----"); BEG_RSA = StringUtils.asciiBytes("-----BEGIN PRIVATE KEY-----"); END_RSA = StringUtils.asciiBytes("-----END PRIVATE KEY-----"); BEG_LINES = new byte[][]{BEG_PRV, BEG_CSR,BEG_CRT, BEG_RSA}; END_LINES = new byte[][]{END_PRV, END_CSR,END_CRT, END_RSA}; } public static Object fromPemString(String pem) throws PemException { if (pem == null) { throw new NotAPemObject("String parameter in call to PemUtils.fromPemString(String pem) was null"); } try { byte[] pemBytes = pem.getBytes(RsaConst.USASCII); return fromPem(pemBytes); } catch (UnsupportedEncodingException ex) { throw new PemException("Error decodeing PEM", ex); } } public static Object fromPem(byte[] pem) throws PemException { Object out; ByteArrayInputStream bas; InputStreamReader isr; PEMReader pr; if (pem == null) { throw new NotAPemObject("byte[] parameter pem in call to PemUtils.fromPem(byte[] pem) was null"); } bas = new ByteArrayInputStream(pem); isr = new InputStreamReader(bas); pr = new PEMReader(isr); try { out = pr.readObject(); pr.close(); isr.close(); bas.close(); } catch (IOException ex) { throw new PemException("Could not read PEM data", ex); } if (out == null) { throw new NotAPemObject("Returned obj instance was null in call to Object obj = Object fromPem(bytes[] pem)"); } return out; } public static String toPemString(Object obj) throws PemException { byte[] pemBytes = toPem(obj); String out; try { out = new String(pemBytes, RsaConst.USASCII); } catch (UnsupportedEncodingException ex) { System.out.printf("Exception: %s\n",Debug.getEST(ex)); throw new PemException("Could not encode Object to PEM", ex); } return out; } public static byte[] toPem(Object obj) throws PemException { byte[] out; ByteArrayOutputStream bas; OutputStreamWriter osw; PEMWriter pw; bas = new ByteArrayOutputStream(RsaConst.PAGESIZE); osw = new OutputStreamWriter(bas); pw = new PEMWriter(osw); try { pw.writeObject(obj); pw.flush(); pw.close(); } catch (IOException ex) { System.out.printf("Exception: %s\n",Debug.getEST(ex)); throw new PemException("Error encoding object to PEM", ex); } out = bas.toByteArray(); return out; } public static String toMultiPemString(List<? extends Object> objList) throws PemException { byte[] bytes = toMultiPem(objList); return StringUtils.asciiString(bytes); } public static byte[] toMultiPem(List<? extends Object> objList) throws PemException { ByteArrayOutputStream bas = new ByteArrayOutputStream(PAGESIZE); for (int i = 0; i < objList.size(); i++) { Object obj = objList.get(i); byte[] pemBytes; if (obj instanceof PemBlock) { obj = ((PemBlock) obj).getDecodedObject(); } if (obj == null) { continue; } try { pemBytes = toPem(obj); } catch (PemException ex) { continue; } try { bas.write(pemBytes); } catch (IOException ex) { throw new PemException("Error writing pemBytes to byte array", ex); } } return bas.toByteArray(); } public static List<PemBlock> parseMultiPem(String multiPemString) { byte[] multiPemBytes; multiPemBytes = StringUtils.asciiBytes(multiPemString); return parseMultiPem(multiPemBytes); } public static List<PemBlock> parseMultiPem(byte[] multiPemBytes) { List<PemBlock> pemBlocks = new ArrayList<PemBlock>(); ByteLineReader br = new ByteLineReader(multiPemBytes); boolean outsideBlock = true; int lc = 0; int currBytePos; ByteArrayOutputStream bos; PemBlock pemBlock = null; bos = null; Object decodedObject = null; while (br.bytesAvailable() > 0) { currBytePos = br.getBytesRead(); byte[] line = br.readLine(true); line = ByteLineReader.trim(line); // Incase some one whitespaces their crt :( lc++; if (isEmptyLine(line)) { continue; } if (outsideBlock) { if (isBegPemBlock(line)) { bos = new ByteArrayOutputStream(PAGESIZE); pemBlock = new PemBlock(); pemBlock.setStartLine(StringUtils.asciiString(line)); pemBlock.setLineNum(lc); pemBlock.setDecodedObject(null); pemBlock.setPemData(null); pemBlock.setStartByte(currBytePos); writeLine(bos, line); outsideBlock = !outsideBlock; continue; } else { continue; // We are still outside the a block so skip this line } } else { // We are inside a pemBlock if (isEndPemBlock(line)) { outsideBlock = !outsideBlock; writeLine(bos, line); byte[] bytes = bos.toByteArray(); pemBlock.setPemData(bytes); pemBlock.setEndLine(StringUtils.asciiString(line)); try { decodedObject = PemUtils.fromPem(bytes); } catch (PemException ex) { decodedObject = null; } pemBlock.setDecodedObject(decodedObject); currBytePos = br.getBytesRead(); pemBlock.setStopByte(currBytePos); pemBlocks.add(pemBlock); } else { writeLine(bos, line); } } } return pemBlocks; } public static List<Object> getBlockObjects(List<PemBlock> blocks) { List<Object> out = new ArrayList<Object>(); for (PemBlock block : blocks) { Object obj = block.getDecodedObject(); if (obj == null) { continue; } out.add(obj); } return out; } public static void writeLine(ByteArrayOutputStream bos, byte[] line) { for (int i = 0; i < line.length; i++) { int byteInt = (line[i] >= 0) ? (int) line[i] : (int) line[i] + 256; bos.write(byteInt); // Not sure why single byte writes are Exception free. Its annoying. } // attach LF bos.write(LF); } public static boolean isBegPemBlock(byte[] line) { for(int i=0;i<BEG_LINES.length;i++){ if(cmpBytes(line,BEG_LINES[i])){ return true; } } return false; } public static boolean isEndPemBlock(byte[] line) { for(int i=0;i<END_LINES.length;i++){ if(cmpBytes(line,END_LINES[i])){ return true; } } return false; } public static boolean isEmptyLine(byte[] line) { if (line.length <= 0) { return true; } else { return false; } } }