2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.impl.membership.pse;
60 import net.jxta.impl.util.BASE64InputStream;
61 import net.jxta.impl.util.BASE64OutputStream;
62 import net.jxta.logging.Logging;
63 import org.bouncycastle.asn1.x509.X509NameTokenizer;
64 import org.bouncycastle.asn1.DERObjectIdentifier;
65 import org.bouncycastle.jce.X509Principal;
66 import org.bouncycastle.jce.provider.BouncyCastleProvider;
67 import org.bouncycastle.x509.X509V3CertificateGenerator;
69 import javax.crypto.Cipher;
70 import javax.crypto.EncryptedPrivateKeyInfo;
71 import javax.crypto.SecretKey;
72 import javax.crypto.SecretKeyFactory;
73 import javax.crypto.spec.PBEKeySpec;
74 import javax.crypto.spec.PBEParameterSpec;
75 import javax.security.auth.x500.X500Principal;
76 import java.io.BufferedReader;
77 import java.io.BufferedWriter;
78 import java.io.ByteArrayOutputStream;
79 import java.io.IOException;
80 import java.io.InputStream;
81 import java.io.Reader;
82 import java.io.StringReader;
83 import java.io.StringWriter;
84 import java.math.BigInteger;
85 import java.security.AlgorithmParameters;
86 import java.security.InvalidKeyException;
87 import java.security.KeyFactory;
88 import java.security.KeyPair;
89 import java.security.KeyPairGenerator;
90 import java.security.MessageDigest;
91 import java.security.NoSuchAlgorithmException;
92 import java.security.PrivateKey;
93 import java.security.Provider;
94 import java.security.SecureRandom;
95 import java.security.Security;
96 import java.security.Signature;
97 import java.security.SignatureException;
98 import java.security.cert.Certificate;
99 import java.security.cert.X509Certificate;
100 import java.security.spec.InvalidKeySpecException;
101 import java.security.spec.KeySpec;
102 import java.util.Calendar;
103 import java.util.Date;
104 import java.util.Hashtable;
105 import java.util.logging.Level;
106 import java.util.logging.Logger;
110 * Singleton class of static utility methods.
112 public final class PSEUtils {
117 private static final transient Logger LOG = Logger.getLogger(PSEUtils.class.getName());
120 * Singleton instance.
122 private static final PSEUtils UTILS = new PSEUtils();
125 * A SecureRandom for generating keys.
127 final transient SecureRandom srng = new SecureRandom();
130 * Singleton utility class
135 ClassLoader sysloader = ClassLoader.getSystemClassLoader();
137 Class<?> loaded = sysloader.loadClass(BouncyCastleProvider.class.getName());
139 Provider provider = (Provider) loaded.newInstance();
141 Security.addProvider(provider);
143 if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
144 LOG.info("Loaded Security Providers into system class loader");
146 } catch (Exception disallowed) {
147 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
148 LOG.log(Level.WARNING,
149 "Failed loading Security Providers into System Class Loader. Will try local class loader (which may not work)",
153 // Add the providers we use.
154 Security.addProvider(new BouncyCastleProvider());
156 if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
157 LOG.info("Loaded Security Providers into local class loader");
161 // Provider [] providers = Security.getProviders();
162 // Iterator eachProvider = Arrays.asList(providers).iterator();
164 // while (eachProvider.hasNext()) {
165 // Provider aProvider = (Provider) eachProvider.next();
167 // System.out.println("\n\n" + aProvider.getName() + " - " + aProvider.getVersion() + " - " + aProvider.getInfo());
169 // Iterator allMappings = aProvider.entrySet().iterator();
171 // while (allMappings.hasNext()) {
172 // Map.Entry aMapping = (Map.Entry) allMappings.next();
174 // Object key = aMapping.getKey();
175 // System.out.println(key + " (" + key.getClass().getName() + ") --> " + aMapping.getValue() + " (" + key.getClass().getName() + ")");
183 public static class IssuerInfo {
184 public X509Certificate cert; // subject Cert
185 public PrivateKey subjectPkey; // subject private key
186 public X509Certificate issuer; // issuer Cert
187 public PrivateKey issuerPkey; // issuer private key
193 * @param cn subject cn for the certificate
194 * @param issuerinfo the cert issuer or null if self-signed root cert.
195 * @return the details of the generated cert.
196 * @throws SecurityException if the cert could not be generated.
198 public static IssuerInfo genCert(String cn, IssuerInfo issuerinfo) throws SecurityException {
202 if (null == issuerinfo) {
203 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
204 LOG.fine("Generating Self Signed Cert ...");
207 if (!cn.endsWith("-CA")) {
213 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
214 LOG.fine("Generating Client Cert ...");
220 // set name attribute
221 Hashtable<DERObjectIdentifier, String> attrs = new Hashtable<DERObjectIdentifier, String>();
223 attrs.put(X509Principal.CN, useCN);
224 attrs.put(X509Principal.O, "www.jxta.org");
226 // XXX bondolo 20040405 wouldn't SN or UID be a better choice?
227 // set ou to 20 random digits
228 byte[] ou = new byte[10];
230 UTILS.srng.nextBytes(ou);
231 String ouStr = toHexDigits(ou);
233 attrs.put(X509Principal.OU, ouStr);
235 X509Principal subject = new X509Principal(attrs);
236 X500Principal samesubject = new X500Principal(subject.getEncoded());
237 KeyPairGenerator g = KeyPairGenerator.getInstance("RSA");
239 g.initialize(1024, UTILS.srng);
241 KeyPair keypair = g.generateKeyPair();
243 return genCert(samesubject, keypair, issuerinfo);
244 } catch (NoSuchAlgorithmException e) {
245 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
246 LOG.log(Level.SEVERE, "Could not generate certificate", e);
248 SecurityException failure = new SecurityException("Could not generate certificate");
250 failure.initCause(e);
256 * Generate a Cert given a keypair
258 * @param subject subjectDN for the certificate
259 * @param keypair the keypair to use.
260 * @param issuerinfo the cert issuer or null if self-signed root cert.
261 * @return the details of the generated cert.
262 * @throws SecurityException if the cert could not be generated.
264 public static IssuerInfo genCert(X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo) throws SecurityException {
268 X509Principal issuer;
270 if (null == issuerinfo) { // self-signed root cert
271 signer = keypair.getPrivate();
272 issuer = new X509Principal(subject.getEncoded());
273 } else { // issuer signed service sert
274 signer = issuerinfo.subjectPkey;
275 X500Principal issuer_subject = issuerinfo.cert.getSubjectX500Principal();
277 issuer = new X509Principal(issuer_subject.getEncoded());
280 // set validity 10 years from today
281 Date today = new Date();
282 Calendar cal = Calendar.getInstance();
285 cal.add(Calendar.YEAR, 10);
286 Date until = cal.getTime();
289 X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
291 certGen.setIssuerDN(issuer);
292 certGen.setSubjectDN(new X509Principal(subject.getEncoded()));
293 certGen.setNotBefore(today);
294 certGen.setNotAfter(until);
295 certGen.setPublicKey(keypair.getPublic());
296 // certGen.setSignatureAlgorithm("SHA1withDSA");
297 certGen.setSignatureAlgorithm("SHA1WITHRSA");
298 // FIXME bondolo 20040317 needs fixing.
299 certGen.setSerialNumber(BigInteger.valueOf(1));
301 // return issuer info for generating service cert
302 IssuerInfo info = new IssuerInfo();
305 info.cert = certGen.generateX509Certificate(signer, UTILS.srng);
307 // For saving service cert private key
308 info.subjectPkey = keypair.getPrivate();
310 // for signing service cert
311 info.issuer = (null == issuerinfo) ? info.cert : issuerinfo.cert;
313 // for signing service cert
314 info.issuerPkey = signer;
316 // dump the certificate?
317 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
318 if (null == issuer) {
319 LOG.fine("Root Cert : \n" + info.cert.toString());
321 LOG.fine("Client Cert : \n" + info.cert.toString());
326 } catch (SignatureException e) {
327 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
328 LOG.log(Level.FINE, "Could not generate certificate", e);
331 SecurityException failure = new SecurityException("Could not generate certificate");
333 failure.initCause(e);
335 } catch (InvalidKeyException e) {
336 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
337 LOG.log(Level.FINE, "Could not generate certificate", e);
340 SecurityException failure = new SecurityException("Could not generate certificate");
342 failure.initCause(e);
344 } catch (IOException e) {
345 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
346 LOG.log(Level.FINE, "Could not generate certificate", e);
349 SecurityException failure = new SecurityException("Could not generate certificate");
351 failure.initCause(e);
357 * return the CN token from the provided cert's subjectDN
359 * @param cert the certificate to examine
360 * @return the CN name or null if none could be found.
362 public static String getCertSubjectCName(X509Certificate cert) {
364 // get the subject dname
365 X500Principal subject = cert.getSubjectX500Principal();
367 X509NameTokenizer tokens = new X509NameTokenizer(subject.getName());
369 // iterate over the attributes of the dname
370 while (tokens.hasMoreTokens()) {
371 String aToken = tokens.nextToken();
373 if (aToken.length() < 3) {
377 String attribute = aToken.substring(0, 3);
379 if ("CN=".equalsIgnoreCase(attribute)) {
380 return aToken.substring(3);
388 * return the CN token from the provided cert's issuerDN
390 * @param cert the certificate to examine
391 * @return the CN name or null if none could be found.
393 public static String getCertIssuerCName(X509Certificate cert) {
395 // get the subject dname
396 X500Principal issuer = cert.getIssuerX500Principal();
398 X509NameTokenizer tokens = new X509NameTokenizer(issuer.getName());
400 // iterate over the attributes of the dname
401 while (tokens.hasMoreTokens()) {
402 String aToken = tokens.nextToken();
404 if (aToken.length() < 3) {
408 String attribute = aToken.substring(0, 3);
410 if ("CN=".equalsIgnoreCase(attribute)) {
411 return aToken.substring(3);
419 * Compute the signature of a stream.
421 * @param key the private key used to sign the stream
422 * @param stream the stream to sign.
423 * @return byte[] the signature
425 public static byte[] computeSignature(String algorithm, PrivateKey key, InputStream stream) throws InvalidKeyException, SignatureException, IOException {
429 sign = Signature.getInstance(algorithm);
430 } catch (NoSuchAlgorithmException badsigner) {
431 throw new IOException("Could not initialize signer with algorithm " + algorithm);
433 sign.initSign(key, UTILS.srng);
435 byte[] buffer = new byte[1024];
438 int read = stream.read(buffer);
444 sign.update(buffer, 0, read);
451 * Verify a signature of a stream.
453 * @param cert The certificate containing the public key which will be used
454 * to verify the signature.
455 * @param signature The signature to verify.
456 * @param stream The stream to verify.
457 * @return boolean true if the signature was valid otherwise false.
459 public static boolean verifySignature(String algorithm, Certificate cert, byte[] signature, InputStream stream) throws InvalidKeyException, SignatureException, IOException {
463 sign = Signature.getInstance(algorithm);
464 } catch (NoSuchAlgorithmException badsigner) {
465 throw new IOException("Could not initialize signer with algorithm " + algorithm);
468 sign.initVerify(cert);
470 byte[] buffer = new byte[1024];
473 int read = stream.read(buffer);
479 sign.update(buffer, 0, read);
482 return sign.verify(signature);
486 * returns a hash SHA-1 of the given byte array
488 * @param data the data to be hashed
489 * @return byte[] the hash of the data
491 public static byte[] hash(String algorithm, byte[] data) {
493 MessageDigest digest = MessageDigest.getInstance(algorithm);
495 return digest.digest(data);
496 } catch (NoSuchAlgorithmException e) {
502 * We are trying to use : PBEWITHMD5ANDDES
504 static final String PKCS5_PBSE1_ALGO = "PBEWITHMD5ANDDES";
507 * Given a private key and a password, encrypt the private key using the
510 * @param password The password which will be used.
511 * @param privkey The private key to be encrypted.
512 * @param iterations Number of iterations.
513 * @return An encrypted private key info or null if the key could not be
516 public static EncryptedPrivateKeyInfo pkcs5_Encrypt_pbePrivateKey(char[] password, PrivateKey privkey, int iterations) {
517 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
518 LOG.fine("Encrypting " + privkey + " with \'" + new String(password) + "\'");
521 PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
522 byte[] salt = new byte[8];
524 UTILS.srng.nextBytes(salt);
527 PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, iterations);
529 // convert password into a SecretKey object, using a PBE key factory.
530 SecretKeyFactory keyFac = SecretKeyFactory.getInstance(PKCS5_PBSE1_ALGO);
531 SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
534 Cipher pbeCipher = Cipher.getInstance(PKCS5_PBSE1_ALGO);
536 // Initialize PBE Cipher with key and parameters
537 pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
539 byte[] encryptedPrivKey = pbeCipher.doFinal(privkey.getEncoded());
541 AlgorithmParameters algo = AlgorithmParameters.getInstance(PKCS5_PBSE1_ALGO);
543 algo.init(pbeParamSpec);
545 EncryptedPrivateKeyInfo result = new EncryptedPrivateKeyInfo(algo, encryptedPrivKey);
548 } catch (Exception failed) {
549 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
550 LOG.log(Level.WARNING, "Encrypt failed", failed);
557 * Given an encrypted private key and a password, decrypt the private key
558 * using the PBESE1 algorithm.
560 * @param password The password which will be used.
561 * @param encryptedPrivKey The private key to be encrypted.
562 * @return The decrypted private key or null if the key could not be decrpyted.
564 public static PrivateKey pkcs5_Decrypt_pbePrivateKey(char[] password, String algorithm, EncryptedPrivateKeyInfo encryptedPrivKey) {
565 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
566 LOG.fine("Decrypting " + encryptedPrivKey + "/" + algorithm + " with \'" + new String(password) + "\'");
569 PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
572 AlgorithmParameters algo = encryptedPrivKey.getAlgParameters();
575 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
576 LOG.warning("Could not get algo parameters from " + encryptedPrivKey);
579 throw new IllegalStateException("Could not get algo parameters from " + encryptedPrivKey);
582 PBEParameterSpec pbeParamSpec = algo.getParameterSpec(PBEParameterSpec.class);
584 // convert password into a SecretKey object, using a PBE key factory.
586 SecretKeyFactory keyFac = SecretKeyFactory.getInstance(PKCS5_PBSE1_ALGO);
587 SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
590 Cipher pbeCipher = Cipher.getInstance(PKCS5_PBSE1_ALGO);
592 // Initialize PBE Cipher with key and parameters
593 pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
597 key_spec = encryptedPrivKey.getKeySpec(pbeCipher);
599 KeyFactory kf = KeyFactory.getInstance(algorithm);
601 return kf.generatePrivate(key_spec);
602 } catch (InvalidKeySpecException failed) {
603 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
604 LOG.warning("Incorrect key for " + encryptedPrivKey + " : " + failed);
608 } catch (Exception failed) {
609 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
610 LOG.log(Level.WARNING, "Decrypt failed", failed);
616 // Load a wrapped object in base64 format:
617 // The following three methods were modified
618 // from similar pureTLS methods.
622 * Copyright (C) 1999, Claymore Systems, Inc.
623 * All Rights Reserved.
625 * ekr@rtfm.com Fri Jun 4 09:11:27 1999
627 * This package is a SSLv3/TLS implementation written by Eric Rescorla
628 * <ekr@rtfm.com> and licensed by Claymore Systems, Inc.
630 * Redistribution and use in source and binary forms, with or without
631 * modification, are permitted provided that the following conditions
633 * 1. Redistributions of source code must retain the above copyright
634 * notice, this list of conditions and the following disclaimer.
635 * 2. Redistributions in binary form must reproduce the above copyright
636 * notice, this list of conditions and the following disclaimer in the
637 * documentation and/or other materials provided with the distribution.
638 * 3. All advertising materials mentioning features or use of this software
639 * must display the following acknowledgement:
640 * This product includes software developed by Claymore Systems, Inc.
641 * 4. Neither the name of Claymore Systems, Inc. nor the name of Eric
642 * Rescorla may be used to endorse or promote products derived from this
643 * software without specific prior written permission.
645 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
646 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
647 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
648 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
649 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
650 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
651 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
652 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
653 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
654 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
658 public static String loadBase64Object(BufferedReader rdr, String type) throws IOException {
659 if (null != findObject(rdr, type)) {
660 return readBase64Object(rdr, type);
666 public static byte[] loadObject(BufferedReader rdr, String type) throws IOException {
667 if (null != findObject(rdr, type)) {
668 return readObject(rdr, type);
674 public static String findObject(BufferedReader br, String type) throws IOException {
675 String prefix = "-----BEGIN ";
676 String suffix = (type == null) ? "-----" : type + "-----";
681 String line = br.readLine();
687 if (!line.startsWith(prefix)) {
691 if (!line.endsWith(suffix)) {
697 return line.substring(prefix.length(), line.length() - 5);
702 * We read a block of n-lines (\n terminated) and return a String of n-lines
703 * concatenated together. This keeps the format consistent with the pureTLS
706 public static String readBase64Object(BufferedReader br, String type) throws IOException {
707 String line = br.readLine();
709 String prefix = "-----BEGIN ";
710 String suffix = (type == null) ? "-----" : type + "-----";
712 if (!line.startsWith(prefix) || !line.endsWith(suffix)) {
713 throw new IOException("Not at begining of object");
716 StringBuilder block = new StringBuilder();
719 line = br.readLine();
725 if (line.startsWith("-----END ")) {
733 return block.toString();
739 public static byte[] readObject(BufferedReader br, String type) throws IOException {
740 String base64 = readBase64Object(br, type);
742 return base64Decode(new StringReader(base64));
750 * Write an object that is already base64 encoded.
752 public static void writeBase64Object(BufferedWriter bw, String type, String object) throws IOException {
754 bw.write("-----BEGIN ");
761 char lastChar = object.charAt(object.length() - 1);
763 if (('\n' != lastChar) && ('\r' != lastChar)) {
767 bw.write("-----END ");
775 public static void writeObject(BufferedWriter out, String type, byte[] object) throws IOException {
776 String base64 = base64Encode(object);
778 writeBase64Object(out, type, base64);
782 * Convert a byte array into a BASE64 encoded String.
784 * @param in The bytes to be converted
785 * @return the BASE64 encoded String.
787 public static String base64Encode(byte[] in) throws IOException {
788 return base64Encode(in, true);
792 * Convert a byte array into a BASE64 encoded String.
794 * @param in the bytes to be converted
795 * @return the BASE64 encoded String.
797 public static String base64Encode(byte[] in, boolean wrap) throws IOException {
798 StringWriter base64 = new StringWriter();
800 BASE64OutputStream b64os;
803 b64os = new BASE64OutputStream(base64, 72);
805 b64os = new BASE64OutputStream(base64);
810 String encoded = base64.toString();
812 if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) {
813 LOG.finer("Encoded " + in.length + " bytes -> " + encoded.length() + " characters.");
820 * Convert a BASE64 Encoded String into byte array.
822 * @param in BASE64 encoded String
823 * @return the decoded bytes.
825 public static byte[] base64Decode(Reader in) throws IOException {
826 BASE64InputStream b64is = new BASE64InputStream(in);
827 ByteArrayOutputStream bos = new ByteArrayOutputStream();
830 int c = b64is.read();
839 byte[] result = bos.toByteArray();
841 if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) {
842 LOG.finer("Decoded " + result.length + " bytes.");
849 * Private replacement for toHexString since we need the leading 0 digits.
850 * Returns a String containing byte value encoded as 2 hex characters.
852 * @param theByte a byte containing the value to be encoded.
853 * @return String containing byte value encoded as 2 hex characters.
855 private static String toHexDigits(byte theByte) {
856 final char[] HEXDIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
857 StringBuilder result = new StringBuilder(2);
859 result.append(HEXDIGITS[(theByte >>> 4) & 15]);
860 result.append(HEXDIGITS[theByte & 15]);
862 return result.toString();
865 private static String toHexDigits(byte[] bytes) {
866 StringBuilder encoded = new StringBuilder(bytes.length * 2);
869 for (byte aByte : bytes) {
870 encoded.append(toHexDigits(aByte).toUpperCase());
872 return encoded.toString();