PemWriter.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.util.io.pem;
2
3import java.io.BufferedWriter;
4import java.io.IOException;
5import java.io.Writer;
6import java.util.Iterator;
7
8import org.bouncycastle.util.encoders.Base64;
9
10/**
11 * A generic PEM writer, based on RFC 1421
12 */
13public 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     */
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     */
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
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
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
122    private void writePreEncapsulationBoundary(
123        String type)
124        throws IOException
125    {
126        this.write("-----BEGIN " + type + "-----");
127        this.newLine();
128    }
129
130    private void writePostEncapsulationBoundary(
131        String type)
132        throws IOException
133    {
134        this.write("-----END " + type + "-----");
135        this.newLine();
136    }
137}
138