1 /*
2  * @(#) $Id: PEMData.java,v 1.5 2003/07/08 08:13:53 pankaj Exp $
3  *
4  * Copyright (c) 2002-03 by Pankaj Kumar (http://www.pankaj-k.net). 
5  * All rights reserved.
6  *
7  * The license governing the use of this file can be found in the 
8  * root directory of the containing software.
9  */
10package org.jstk.pem;
11
12import java.io.BufferedReader;
13import java.io.FileReader;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.FileInputStream;
17import java.io.BufferedInputStream;
18import java.io.ByteArrayInputStream;
19import java.io.ByteArrayOutputStream;
20
21public class PEMData {
22    private String preEB = null;
23    private String postEB = null;
24    private String text = null;
25    private byte[] raw = null;
26
27    private static final int BEGIN = 0;
28    private static final int GOT_PREEB = 1;
29    private static final int GOT_POSTEB = 2;
30
31    private static final String MARKER = "-----";
32
33    public PEMData(String text){
34        this.text = text;
35    }
36
37    public PEMData(BufferedReader reader) throws IOException, InvalidPEMFormatException {
38        String curLine;
39        int state = BEGIN;
40        StringBuffer sb = new StringBuffer();
41        while ((curLine = reader.readLine()) != null){
42            if (state == BEGIN){
43                if (curLine.startsWith(MARKER) && curLine.endsWith(MARKER)){
44                    preEB = curLine;
45                    state = GOT_PREEB;
46                } else {
47                    throw new InvalidPEMFormatException("no PreEB");
48                }
49            } else if (state == GOT_PREEB){
50                if (curLine.startsWith(MARKER) && curLine.endsWith(MARKER)){
51                    postEB = curLine;
52                    state = GOT_POSTEB;
53                } else {
54                    sb.append(curLine);
55                }
56            } else if (state == GOT_POSTEB){
57                throw new InvalidPEMFormatException("data after PostEB");
58            }
59        }
60        if (state != GOT_POSTEB){
61            throw new InvalidPEMFormatException("no PostEB");
62        }
63        text = sb.toString();
64    }
65
66    public PEMData(byte[] raw){
67        this(raw, "", "");
68    }
69
70    public PEMData(byte[] raw, String preEB, String postEB){
71        this.raw = raw;
72        this.preEB = preEB;
73        this.postEB = postEB;
74    }
75
76    public String getPreEB(){
77        return preEB;
78    }
79    public String getPostEB(){
80        return postEB;
81    }
82    public String getText(){
83        return text;
84    }
85    public byte[] decode() throws InvalidPEMFormatException {
86        if (text != null)
87            return decode(text);
88        return null;
89    }
90    public byte[] decode(String base64) throws InvalidPEMFormatException {
91        try {
92            int pad = 0;
93            for (int i = base64.length() - 1; (i > 0) && (base64.charAt(i) == '='); i--){
94                pad++;
95            }
96
97        int length = base64.length() / 4 * 3 - pad;
98        raw = new byte[length];
99
00        for (int i = 0, rawIndex = 0; i < base64.length(); i += 4, rawIndex += 3){
01            int block = (getValue(base64.charAt(i)) << 18)
02                        + (getValue(base64.charAt(i + 1)) << 12)
03                        + (getValue(base64.charAt(i + 2)) << 6)
04                        + (getValue(base64.charAt(i + 3)));
05
06            for (int j = 2; j >= 0; j--) {
07               if (rawIndex + j < raw.length) {
08                  raw[rawIndex + j] = (byte) (block & 0xff);
09               }
10
11               block >>= 8;
12            }
13         }
14
15         return raw;
16      } catch (IndexOutOfBoundsException ex) {
17         throw new InvalidPEMFormatException("illegal bit length");
18      }
19    }
20
21    public String encode(){
22        if (raw != null)
23            return encode(raw);
24        return null;
25    }
26    public String encode(byte[] raw){
27        StringBuffer sb = new StringBuffer();
28        int n24bits = raw.length/3;
29        int rem = raw.length - (n24bits*3);
30        int niters = n24bits + (rem > 0 ? 1 : 0);
31
32        for (int i = 0; i < niters; i++){
33            int block = 0;
34            for (int j = 0; j < 3; j++){
35                int val = (i*3 + j < raw.length ? raw[i*3 + j] : 0);
36                block |= (0x000000ff & val);
37                if (j < 2)
38                    block <<= 8;
39            }
40
41            for (int j = 0; j < 4; j++){
42                int bb = (block >> ((3-j)*6)) & 0x0000003f;
43                char ch = getChar(bb);
44                if (i == n24bits){
45                    if ((rem == 1 && j > 1) || (rem == 2 && j > 2))
46                        ch = '=';
47                }
48                sb.append(ch);
49            }
50            if (((i + 1) & 0x0000000f) == 0)
51                sb.append("\n");
52        }
53        text = sb.toString();
54        return text;
55    }
56
57    private int getValue(char c){
58        if ((c >= 'A') && (c <= 'Z'))
59            return c - 'A';
60        else if ((c >= 'a') && (c <= 'z'))
61            return c - 'a' + 26;
62        else if ((c >= '0') && (c <= '9'))
63            return c - '0' + 52;
64        else if (c == '+')
65            return 62;
66        else if (c == '/')
67            return 63;
68        else if (c == '=')
69            return 0;
70
71        return -1;
72    }
73
74    private char getChar(int b6bit){
75        if (b6bit >= 0 && b6bit < 26)
76            return (char)('A' + b6bit);
77        else if (b6bit < 52)
78            return (char)('a' + (b6bit - 26));
79        else if (b6bit < 62)
80            return (char)('0' + (b6bit - 52));
81        else if (b6bit == 62)
82            return '+';
83        else if (b6bit == 63)
84            return '/';
85        else {
86            System.err.println("ERROR: Invalid Input to PEMData.getChar(): " + b6bit);
87            return '\0';
88        }
89    }
90
91    public String toString(){
92        return ("PRE_EB: " + preEB + "\nTEXT: " + text + "\nPOST-EB: " + postEB);
93    }
94
95    public static InputStream getDERInputStream(String file) throws IOException {
96        InputStream is = null;
97        try {                                       // Try PEM format
98            BufferedReader  reader = new BufferedReader(new FileReader(file));
99            PEMData x = new PEMData(reader);
00            is = new ByteArrayInputStream(x.decode());
01        } catch (InvalidPEMFormatException exc){    // Assume DER format
02            is = new FileInputStream(file);
03        }
04        return is;
05    }
06
07    private static void printusageAndExit(){
08        System.out.println("Usage:: java org.jstk.pem.PEMData (encode|decode) (-infile <file>|-text <text>)");
09        System.exit(0);
10    }
11    public static void main(String[] args) throws Exception {
12        PEMData pemData = null;
13        String text = null;
14        String infile = null;
15
16        if (args.length == 3){
17            if (args[1].equals("-infile")){
18                infile = args[2];
19            } else if (args[1].equals("-text")){
20                text = args[2];
21            } else {
22                printusageAndExit();
23            }
24
25            if (args[0].equals("decode")){
26                if (infile != null){
27                    BufferedReader reader = new BufferedReader(new FileReader(infile));
28                    pemData = new PEMData(reader);
29                } else {
30                    pemData = new PEMData(text);
31                }
32                byte[] raw = pemData.decode();
33                System.out.println(new String(raw));
34            } else if (args[0].equals("encode")){
35                if (infile != null){
36                    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(infile));
37                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
38                    byte[] buf = new byte[1024];
39                    int n;
40                    while ((n = bis.read(buf, 0, buf.length)) > 0){
41                        baos.write(buf, 0, n);
42                    }
43                    pemData = new PEMData(baos.toByteArray());
44                } else {
45                    pemData = new PEMData(text.getBytes());
46                }
47                text = pemData.encode();
48                System.out.println(text);
49            } else {
50                printusageAndExit();
51            }
52        } else {
53            printusageAndExit();
54        }
55    }
56}