KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > cbutil > CBSecurity


1 package com.ca.commons.cbutil;
2
3 /**
4  * <p>This is a grab bag of useful static functions
5  * related to security - mainly doing conversions
6  * between PEM and DER.</p>
7  * <p/>
8  * (nb: PEM = 'Privacy Enhanced Mail' format,<br>
9  * while DER = 'Destinguished Encoding Rules' ASN1 data.<br>
10  * - PEM is usually base64 encoded DER data, with some minor frills.)
11  */

12
13
14 public class CBSecurity
15 {
16
17 // XXX WARNING !!! XXX
18
// Many of the following constants have derived from observation, rather than looking
19
// At standards. ('cause I can't find the standards :-( ). - CB
20

21     /**
22      * Standard header for the base 64 encoded info block of a pem file.
23      */

24
25     public static final byte[] PEM_BEGIN = (new String JavaDoc("-----BEGIN")).getBytes();
26
27     /**
28      * Standard footer for the base 64 encoded info block of a pem file.
29      */

30     public static final byte[] PEM_END = (new String JavaDoc("-----END")).getBytes();
31
32     /**
33      * Standard header for a pem encoded certificate block
34      */

35
36     public static final byte[] PEM_CERT_HEADER = new String JavaDoc("-----BEGIN CERTIFICATE-----").getBytes();
37
38     /**
39      * Standard footer for a pem encoded certificate block
40      */

41
42     public static final byte[] PEM_CERT_FOOTER = new String JavaDoc("-----END CERTIFICATE-----").getBytes();
43
44
45     /**
46      * Standard header for a pem encoded encrypted private key block
47      */

48
49     public static final byte[] PEM_ENC_KEY_HEADER = (new String JavaDoc("-----BEGIN ENCRYPTED PRIVATE KEY-----")).getBytes();
50
51     /**
52      * Standard header for a pem unencoded encrypted private key block
53      */

54
55     public static final byte[] PEM_KEY_HEADER = (new String JavaDoc("-----BEGIN PRIVATE KEY-----")).getBytes();
56
57     /**
58      * Standard footer for a pem encoded encrypted private key block
59      */

60
61     public static final byte[] PEM_ENC_KEY_FOOTER = (new String JavaDoc("-----END ENCRYPTED PRIVATE KEY-----")).getBytes();
62
63     /**
64      * Standard footer for a pem unencoded encrypted private key block
65      */

66
67     public static final byte[] PEM_KEY_FOOTER = (new String JavaDoc("-----END PRIVATE KEY-----")).getBytes();
68
69     /**
70      * Standard header for a pem encoded RSA private key block
71      */

72
73     public static final byte[] PEM_RSA_KEY_HEADER = new String JavaDoc("-----BEGIN RSA PRIVATE KEY-----").getBytes();
74
75     /**
76      * Standard header for a pem encoded RSA private key block
77      */

78
79     public static final byte[] PEM_RSA_KEY_FOOTER = new String JavaDoc("-----END RSA PRIVATE KEY-----").getBytes();
80
81     /**
82      * Returns the position that a searchByte first appears in
83      * a byte array.
84      *
85      * @param mainArray the byte array to search within
86      * @param searchByte the byte to look for
87      */

88
89     public static int indexOf(byte[] mainArray, byte searchByte)
90     {
91         return indexOf(mainArray, searchByte, 0);
92     }
93
94     /**
95      * Returns the first position, greater than a given index,
96      * that a searchByte first appears at within an array.
97      *
98      * @param mainArray the byte array to search within
99      * @param searchByte the byte to look for
100      */

101
102     public static int indexOf(byte[] mainArray, byte searchByte, int fromIndex)
103     {
104         int len = mainArray.length;
105
106         // Some sanity checks...
107

108         if (fromIndex < 0)
109         {
110             fromIndex = 0;
111         }
112         else if (fromIndex >= len)
113         {
114             return -1;
115         }
116
117         // find the byte!
118

119         for (int i = fromIndex; i < len; i++)
120             if (mainArray[i] == searchByte) return i;
121
122         return -1; // didn't find anything...
123
}
124
125     /**
126      * <p>Tries to match a byte sequence within a larger
127      * byte array.</p>
128      * <p/>
129      * <p>Students of Sun's java.lang.String class may
130      * recognise some of this code :-). </p>
131      *
132      * @param mainArray the base array to search within.
133      * @param searchSequence the short sequence to find the position of
134      * within the main array.
135      * @return the index of the searchSequence within the main Array,
136      * or -1 if not found.
137      */

138
139     public static int indexOf(byte[] mainArray, byte[] searchSequence)
140     {
141         return indexOf(mainArray, searchSequence, 0);
142     }
143
144     /**
145      * <p>Tries to match a byte sequence within a larger
146      * byte array.</p>
147      * <p/>
148      * <p>Students of Sun's java.lang.String class may
149      * recognise some of this code :-). </p>
150      *
151      * @param mainArray the base array to search within.
152      * @param searchSequence the short sequence to find the position of
153      * within the main array.
154      * @param fromIndex the position to start searching from.
155      * @return the index of the searchSequence within the main Array,
156      * or -1 if not found.
157      */

158
159     public static int indexOf(byte[] mainArray, byte[] searchSequence, int fromIndex)
160     {
161         byte v1[] = mainArray;
162         byte v2[] = searchSequence;
163
164         int max = mainArray.length;
165
166         // Sanity Check (including empty arrays special condition)
167
if (fromIndex >= max)
168         {
169             if (mainArray.length == 0 && fromIndex == 0 && searchSequence.length == 0)
170             {
171                 /* There is an empty array at index 0 in an empty array. */
172                 return 0;
173             }
174             return -1; // from index too large
175
}
176
177
178         // Sanity check: set negative fromIndexes to 0.
179
if (fromIndex < 0)
180         {
181             fromIndex = 0;
182         }
183
184         // Empty search array is matched immediately
185
if (searchSequence.length == 0)
186         {
187             return fromIndex;
188         }
189
190         byte first = v2[0];
191         int i = fromIndex;
192
193         startSearchForFirstChar:
194
195         while (true)
196         {
197
198             /* Look for first character. */
199             while (i < max && v1[i] != first)
200             {
201                 i++;
202             }
203
204             if (i >= max) // didn't find the sequence: return -1
205
{
206                 return -1;
207             }
208
209             /* Found first character, now look at the rest of v2 */
210             int j = i + 1;
211             int end = j + searchSequence.length - 1;
212             int k = 1;
213             while (j < end)
214             {
215                 if (v1[j++] != v2[k++])
216                 {
217                     i++;
218                     /* Look for str's first char again. */
219                     continue startSearchForFirstChar;
220                 }
221             }
222             return i; // Found whole string!!!
223
}
224     }
225
226     /**
227      * A simple check to see if a file is a PEM file, by
228      * looking for PEM '------BEGIN...' and PEM '-----END'
229      * tags. Note that this is Not Conclusive!
230      */

231
232     public static boolean isPEM(byte[] test)
233     {
234
235         if (indexOf(test, PEM_BEGIN) == -1)
236             return false; // no PEM start string
237

238         if (indexOf(test, PEM_END) == -1)
239             return false; // no PEM end string
240

241         return true; // has PEM begin and end tags - probably a PEM!
242
}
243
244
245     /**
246      * This takes a byte array of PEM (originally rfc 1421-1424, but
247      * has drifted a bit) encoded data, such as might be read as raw
248      * bytes from a text file, and converts it to 'raw' DER binary
249      * data (i.e. a byte array with values from 0x0 to 0xFF).
250      *
251      * @param pem the pem data to convert
252      * @return the converted raw data
253      */

254
255     public static byte[] convertFromPEM(byte[] pem)
256     {
257         return convertFromPEM(pem, PEM_BEGIN, PEM_END);
258     }
259
260     /**
261      * <p>This takes a byte array of PEM (originally rfc 1421-1424, but
262      * has drifted a bit) encoded data, such as might be read as raw
263      * bytes from a text file, and converts it to 'raw' DER binary
264      * data (i.e. a byte array with values from 0x0 to 0xFF).</p>
265      * <p/>
266      * <p>In addition, this method allows the start of the PEM header
267      * tag to be explicitly specified. This is useful when
268      * a single file contains multiple data blocks (e.g. a cert *and*
269      * a private key). Only the beginning of the stard header needs to
270      * be specified; e.g. '-----BEGIN RSA PRIVATE' is sufficient, the
271      * full header is not required. (The footer is assumed to be the
272      * first block starting with '-----END...')</p>
273      *
274      * @param pem the pem data to convert
275      * @return the converted raw data
276      */

277
278     public static byte[] convertFromPEM(byte[] pem, byte[] header)
279     {
280         return convertFromPEM(pem, header, PEM_END);
281     }
282
283     /**
284      * <p>This takes a byte array of PEM (originally rfc 1421-1424, but
285      * has drifted a bit) encoded data representing an X509 certificate
286      * and converts it to 'raw' DER binary
287      * data (i.e. a byte array with values from 0x0 to 0xFF).</p>
288      *
289      * @param pem the pem data containing a certificate to convert
290      * @return the converted raw data
291      */

292
293     public static byte[] convertFromPEMCertificate(byte[] pem)
294     {
295         return convertFromPEM(pem, PEM_CERT_HEADER, PEM_END);
296     }
297
298
299     /**
300      * <p>This takes a byte array of PEM (originally rfc 1421-1424, but
301      * has drifted a bit) encoded data, such as might be read as raw
302      * bytes from a text file, and converts it to 'raw' DER binary
303      * data (i.e. a byte array with values from 0x0 to 0xFF).</p>
304      * <p/>
305      * <p>In addition, this method allows the start of the PEM header
306      * and footer tag to be explicitly specified. This is useful when
307      * a single file contains multiple data blocks (e.g. a cert *and*
308      * a private key). Only the beginning of the headers needs to
309      * be specified; e.g. '-----BEGIN RSA PRIVATE' is sufficient, the
310      * full header/footer is not required.</p>
311      *
312      * @param pem the pem data to convert
313      * @return the converted raw data
314      */

315
316
317     public static byte[] convertFromPEM(byte[] pem, byte[] header, byte[] footer)
318     {
319         int start, end;
320
321         start = indexOf(pem, header);
322
323         end = indexOf(pem, footer);
324
325         if (start == -1 || end == -1) return null; // Something wrong - no headers!
326

327         start = indexOf(pem, (byte) '\n', start) + 1;
328
329         // skip past any more text, by avoiding all lines less than 64 characters long...
330

331         int next;
332         while ((next = indexOf(pem, (byte) '\n', start)) < start + 64)
333         {
334             if (next == -1) // really shouldn't ever happen...
335
break; // - maybe for a very, very short file?
336

337             start = next + 1; // keep looking for short lines...
338
}
339
340         if (start == -1) // something wrong - no end of line after '-----BEGIN...'
341
return null;
342
343         int len = end - start;
344
345         byte[] data = new byte[len];
346
347         System.arraycopy(pem, start, data, 0, len); // remove the PEM fluff from tbe base 64 data, stick in 'data'
348

349         return CBBase64.decode(data); // return the raw binary data
350
}
351
352     /**
353      * This takes an array of raw data representing a DER encoded X509 certificate,
354      * and base64 encodes it, adding PEM style -----BEGIN CERTIFICATE-----
355      * and -----END CERTIFICATE----- tags.
356      *
357      * @param der the DER encoded data
358      */

359
360     public static byte[] convertToPEMCertificate(byte[] der)
361     {
362         return convertToPEM(der, PEM_CERT_HEADER, PEM_CERT_FOOTER);
363     }
364
365     /**
366      * This takes an array of raw data representing a DER encoded RSA private key,
367      * and base64 encodes it, adding PEM style -----BEGIN CERTIFICATE-----
368      * and -----END CERTIFICATE----- tags.
369      *
370      * @param der the DER encoded data
371      */

372
373     public static byte[] convertToPEMRSAPrivateKey(byte[] der)
374     {
375
376         return convertToPEM(der, PEM_RSA_KEY_HEADER, PEM_RSA_KEY_FOOTER);
377     }
378
379     /**
380      * This takes an array of raw data representing an
381      * Encrypted DER encoded private key (probably pkcs 8),
382      * and base64 encodes it, adding PEM style -----BEGIN CERTIFICATE-----
383      * and -----END CERTIFICATE----- tags.
384      *
385      * @param der the DER encoded data
386      */

387
388     public static byte[] convertToPEMEncryptedPrivateKey(byte[] der)
389     {
390         return convertToPEM(der, PEM_ENC_KEY_HEADER, PEM_ENC_KEY_FOOTER);
391     }
392
393     public static byte[] convertToPEMPrivateKey(byte[] der)
394     {
395         return convertToPEM(der, PEM_KEY_HEADER, PEM_KEY_FOOTER);
396     }
397
398     protected static byte[] convertToPEM(byte[] der, byte[] header, byte[] footer)
399     {
400         try
401         {
402             byte[] base64Data = CBBase64.encodeFormatted(der, 0, 64); // theoretically may throw exception (should never happen)...
403

404             int len = header.length + 1 + base64Data.length + footer.length + 1; // '+1's for '\n' chars.
405

406             byte[] pem = new byte[len];
407
408             int pos = 0;
409             System.arraycopy(header, 0, pem, 0, header.length);
410             pos += header.length;
411
412             pem[pos++] = (byte) '\n';
413
414             System.arraycopy(base64Data, 0, pem, pos, base64Data.length);
415             pos += base64Data.length;
416
417             System.arraycopy(footer, 0, pem, pos, footer.length);
418             pos += footer.length;
419
420             pem[pos] = (byte) '\n';
421
422             base64Data = null;
423             der = null;
424
425             return pem;
426         }
427         catch (Exception JavaDoc e)
428         {
429             System.err.println("error decoding pem file: " + e);
430             return null;
431         }
432     }
433 }
434         
Popular Tags