1package org.bouncycastle.util.encoders; 2 3import java.io.IOException; 4import java.io.OutputStream; 5 6public class Base64Encoder 7 implements Encoder 8{ 9 protected final byte[] encodingTable = 10 { 11 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 12 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 13 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 14 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 15 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 16 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 17 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 18 (byte)'v', 19 (byte)'w', (byte)'x', (byte)'y', (byte)'z', 20 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', 21 (byte)'7', (byte)'8', (byte)'9', 22 (byte)'+', (byte)'/' 23 }; 24 25 protected byte padding = (byte)'='; 26 27 /* 28 * set up the decoding table. 29 */ 30 protected final byte[] decodingTable = new byte[128]; 31 32 protected void initialiseDecodingTable() 33 { 34 for (int i = 0; i < decodingTable.length; i++) 35 { 36 decodingTable[i] = (byte)0xff; 37 } 38 39 for (int i = 0; i < encodingTable.length; i++) 40 { 41 decodingTable[encodingTable[i]] = (byte)i; 42 } 43 } 44 45 public Base64Encoder() 46 { 47 initialiseDecodingTable(); 48 } 49 50 /** 51 * encode the input data producing a base 64 output stream. 52 * 53 * @return the number of bytes produced. 54 */ 55 public int encode( 56 byte[] data, 57 int off, 58 int length, 59 OutputStream out) 60 throws IOException 61 { 62 int modulus = length % 3; 63 int dataLength = (length - modulus); 64 int a1, a2, a3; 65 66 for (int i = off; i < off + dataLength; i += 3) 67 { 68 a1 = data[i] & 0xff; 69 a2 = data[i + 1] & 0xff; 70 a3 = data[i + 2] & 0xff; 71 72 out.write(encodingTable[(a1 >>> 2) & 0x3f]); 73 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 74 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); 75 out.write(encodingTable[a3 & 0x3f]); 76 } 77 78 /* 79 * process the tail end. 80 */ 81 int b1, b2, b3; 82 int d1, d2; 83 84 switch (modulus) 85 { 86 case 0: /* nothing left to do */ 87 break; 88 case 1: 89 d1 = data[off + dataLength] & 0xff; 90 b1 = (d1 >>> 2) & 0x3f; 91 b2 = (d1 << 4) & 0x3f; 92 93 out.write(encodingTable[b1]); 94 out.write(encodingTable[b2]); 95 out.write(padding); 96 out.write(padding); 97 break; 98 case 2: 99 d1 = data[off + dataLength] & 0xff; 100 d2 = data[off + dataLength + 1] & 0xff; 101 102 b1 = (d1 >>> 2) & 0x3f; 103 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; 104 b3 = (d2 << 2) & 0x3f; 105 106 out.write(encodingTable[b1]); 107 out.write(encodingTable[b2]); 108 out.write(encodingTable[b3]); 109 out.write(padding); 110 break; 111 } 112 113 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); 114 } 115 116 private boolean ignore( 117 char c) 118 { 119 return (c == '\n' || c =='\r' || c == '\t' || c == ' '); 120 } 121 122 /** 123 * decode the base 64 encoded byte data writing it to the given output stream, 124 * whitespace characters will be ignored. 125 * 126 * @return the number of bytes produced. 127 */ 128 public int decode( 129 byte[] data, 130 int off, 131 int length, 132 OutputStream out) 133 throws IOException 134 { 135 byte b1, b2, b3, b4; 136 int outLen = 0; 137 138 int end = off + length; 139 140 while (end > off) 141 { 142 if (!ignore((char)data[end - 1])) 143 { 144 break; 145 } 146 147 end--; 148 } 149 150 int i = off; 151 int finish = end - 4; 152 153 i = nextI(data, i, finish); 154 155 while (i < finish) 156 { 157 b1 = decodingTable[data[i++]]; 158 159 i = nextI(data, i, finish); 160 161 b2 = decodingTable[data[i++]]; 162 163 i = nextI(data, i, finish); 164 165 b3 = decodingTable[data[i++]]; 166 167 i = nextI(data, i, finish); 168 169 b4 = decodingTable[data[i++]]; 170 171 if ((b1 | b2 | b3 | b4) < 0) 172 { 173 throw new IOException("invalid characters encountered in base64 data"); 174 } 175 176 out.write((b1 << 2) | (b2 >> 4)); 177 out.write((b2 << 4) | (b3 >> 2)); 178 out.write((b3 << 6) | b4); 179 180 outLen += 3; 181 182 i = nextI(data, i, finish); 183 } 184 185 outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]); 186 187 return outLen; 188 } 189 190 private int nextI(byte[] data, int i, int finish) 191 { 192 while ((i < finish) && ignore((char)data[i])) 193 { 194 i++; 195 } 196 return i; 197 } 198 199 /** 200 * decode the base 64 encoded String data writing it to the given output stream, 201 * whitespace characters will be ignored. 202 * 203 * @return the number of bytes produced. 204 */ 205 public int decode( 206 String data, 207 OutputStream out) 208 throws IOException 209 { 210 byte b1, b2, b3, b4; 211 int length = 0; 212 213 int end = data.length(); 214 215 while (end > 0) 216 { 217 if (!ignore(data.charAt(end - 1))) 218 { 219 break; 220 } 221 222 end--; 223 } 224 225 int i = 0; 226 int finish = end - 4; 227 228 i = nextI(data, i, finish); 229 230 while (i < finish) 231 { 232 b1 = decodingTable[data.charAt(i++)]; 233 234 i = nextI(data, i, finish); 235 236 b2 = decodingTable[data.charAt(i++)]; 237 238 i = nextI(data, i, finish); 239 240 b3 = decodingTable[data.charAt(i++)]; 241 242 i = nextI(data, i, finish); 243 244 b4 = decodingTable[data.charAt(i++)]; 245 246 if ((b1 | b2 | b3 | b4) < 0) 247 { 248 throw new IOException("invalid characters encountered in base64 data"); 249 } 250 251 out.write((b1 << 2) | (b2 >> 4)); 252 out.write((b2 << 4) | (b3 >> 2)); 253 out.write((b3 << 6) | b4); 254 255 length += 3; 256 257 i = nextI(data, i, finish); 258 } 259 260 length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1)); 261 262 return length; 263 } 264 265 private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4) 266 throws IOException 267 { 268 byte b1, b2, b3, b4; 269 270 if (c3 == padding) 271 { 272 b1 = decodingTable[c1]; 273 b2 = decodingTable[c2]; 274 275 if ((b1 | b2) < 0) 276 { 277 throw new IOException("invalid characters encountered at end of base64 data"); 278 } 279 280 out.write((b1 << 2) | (b2 >> 4)); 281 282 return 1; 283 } 284 else if (c4 == padding) 285 { 286 b1 = decodingTable[c1]; 287 b2 = decodingTable[c2]; 288 b3 = decodingTable[c3]; 289 290 if ((b1 | b2 | b3) < 0) 291 { 292 throw new IOException("invalid characters encountered at end of base64 data"); 293 } 294 295 out.write((b1 << 2) | (b2 >> 4)); 296 out.write((b2 << 4) | (b3 >> 2)); 297 298 return 2; 299 } 300 else 301 { 302 b1 = decodingTable[c1]; 303 b2 = decodingTable[c2]; 304 b3 = decodingTable[c3]; 305 b4 = decodingTable[c4]; 306 307 if ((b1 | b2 | b3 | b4) < 0) 308 { 309 throw new IOException("invalid characters encountered at end of base64 data"); 310 } 311 312 out.write((b1 << 2) | (b2 >> 4)); 313 out.write((b2 << 4) | (b3 >> 2)); 314 out.write((b3 << 6) | b4); 315 316 return 3; 317 } 318 } 319 320 private int nextI(String data, int i, int finish) 321 { 322 while ((i < finish) && ignore(data.charAt(i))) 323 { 324 i++; 325 } 326 return i; 327 } 328} 329