1 package org.bouncycastle.util.io.pem;
2 
3 import java.io.BufferedWriter;
4 import java.io.IOException;
5 import java.io.Writer;
6 import java.util.Iterator;
7 
8 import org.bouncycastle.util.encoders.Base64;
9 
10 /**
11  * A generic PEM writer, based on RFC 1421
12  */
13 public class PemWriter
14     extends BufferedWriter
15 {
16     private static final int LINE_LENGTH = 64;
17 
18     private final int nlLength;
19     private char[]  buf = new char[LINE_LENGTH];
20 
21     /**
22      * Base constructor.
23      *
24      * @param out output stream to use.
25      */
PemWriter(Writer out)26     public PemWriter(Writer out)
27     {
28         super(out);
29 
30         String nl = System.getProperty("line.separator");
31         if (nl != null)
32         {
33             nlLength = nl.length();
34         }
35         else
36         {
37             nlLength = 2;
38         }
39     }
40 
41     /**
42      * Return the number of bytes or characters required to contain the
43      * passed in object if it is PEM encoded.
44      *
45      * @param obj pem object to be output
46      * @return an estimate of the number of bytes
47      */
getOutputSize(PemObject obj)48     public int getOutputSize(PemObject obj)
49     {
50         // BEGIN and END boundaries.
51         int size = (2 * (obj.getType().length() + 10 + nlLength)) + 6 + 4;
52 
53         if (!obj.getHeaders().isEmpty())
54         {
55             for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
56             {
57                 PemHeader hdr = (PemHeader)it.next();
58 
59                 size += hdr.getName().length() + ": ".length() + hdr.getValue().length() + nlLength;
60             }
61 
62             size += nlLength;
63         }
64 
65         // base64 encoding
66         int dataLen = ((obj.getContent().length + 2) / 3) * 4;
67 
68         size += dataLen + (((dataLen + LINE_LENGTH - 1) / LINE_LENGTH) * nlLength);
69 
70         return size;
71     }
72 
writeObject(PemObjectGenerator objGen)73     public void writeObject(PemObjectGenerator objGen)
74         throws IOException
75     {
76         PemObject obj = objGen.generate();
77 
78         writePreEncapsulationBoundary(obj.getType());
79 
80         if (!obj.getHeaders().isEmpty())
81         {
82             for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
83             {
84                 PemHeader hdr = (PemHeader)it.next();
85 
86                 this.write(hdr.getName());
87                 this.write(": ");
88                 this.write(hdr.getValue());
89                 this.newLine();
90             }
91 
92             this.newLine();
93         }
94 
95         writeEncoded(obj.getContent());
96         writePostEncapsulationBoundary(obj.getType());
97     }
98 
writeEncoded(byte[] bytes)99     private void writeEncoded(byte[] bytes)
100         throws IOException
101     {
102         bytes = Base64.encode(bytes);
103 
104         for (int i = 0; i < bytes.length; i += buf.length)
105         {
106             int index = 0;
107 
108             while (index != buf.length)
109             {
110                 if ((i + index) >= bytes.length)
111                 {
112                     break;
113                 }
114                 buf[index] = (char)bytes[i + index];
115                 index++;
116             }
117             this.write(buf, 0, index);
118             this.newLine();
119         }
120     }
121 
writePreEncapsulationBoundary( String type)122     private void writePreEncapsulationBoundary(
123         String type)
124         throws IOException
125     {
126         this.write("-----BEGIN " + type + "-----");
127         this.newLine();
128     }
129 
writePostEncapsulationBoundary( String type)130     private void writePostEncapsulationBoundary(
131         String type)
132         throws IOException
133     {
134         this.write("-----END " + type + "-----");
135         this.newLine();
136     }
137 }
138