1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8035807
  27  * @summary Confirm that old and new Base64 encodings are compatible.
  28  * @modules java.base/sun.misc
  29  */
  30 
  31 import java.io.*;
  32 import java.util.*;
  33 import javax.naming.*;
  34 import javax.naming.directory.*;
  35 
  36 import sun.misc.BASE64Decoder;
  37 
  38 /*
  39  * RFC 2713 specifies an encoding for Java objects stored in an LDAP directory.
  40  * Section 3.6 specifies how a binary-valued JNDI RefAddr object is encoded
  41  * in the value of a javaReferenceAttribute LDAP attribute: first the RefAddr
  42  * object is serialized and then it is encoded using Base64.
  43  *
  44  * Since JDK 9, the JNDI/LDAP provider uses the public Base64 encoder which
  45  * adheres strictly to the MIME encoding rules. The encoder inserts '\r\n'
  46  * as the line separator at intervals of 76 characters. Previously the
  47  * JNDI/LDAP provider used a private Base64 encoder which inserted '\n'
  48  * as the line separator. It is a compatible change.
  49  *
  50  * This test demonstrates that there is no compatability problem when
  51  * encoding and decoding using either Base64 coder:
  52  *
  53  *   encode with s.m.BASE64Encoder,  decode with s.m.BASE64Decoder  => OK
  54  *   encode with s.m.BASE64Encoder,  decode with j.u.Base64.Decoder => OK
  55  *   encode with j.u.Base64.Encoder, decode with s.m.BASE64Decoder  => OK
  56  *   encode with j.u.Base64.Encoder, decode with j.u.Base64.Decoder => OK
  57  *
  58  *
  59  * NOTE: The two Base64 encodings used in this test were captured from
  60  *       LDAP protocol exchanges during attempts by the JNDI/LDAP provider
  61  *       to store a JNDI Reference test object.
  62  */
  63 
  64 public class Base64Test {
  65     /*
  66      * The old Base64 encoding uses '\n' as the line separator at 76 character
  67      * intervals:
  68      *
  69      * 0000: 72 4F 30 41 42 58 4E 79 41 42 70 71 59 58 5A 68  rO0ABXNyABpqYXZh
  70      * 0010: 65 43 35 75 59 57 31 70 62 6D 63 75 51 6D 6C 75  eC5uYW1pbmcuQmlu
  71      * 0020: 59 58 4A 35 55 6D 56 6D 51 57 52 6B 63 74 43 61  YXJ5UmVmQWRkctCa
  72      * 0030: 6B 37 4C 65 73 34 68 48 41 67 41 42 57 77 41 44  k7Les4hHAgABWwAD
  73      * 0040: 59 6E 56 6D 64 41 41 43 57 30 4A 34 0A 63 67 41  YnVmdAACW0J4.cgA <
  74      * 0050: 55 61 6D 46 32 59 58 67 75 62 6D 46 74 61 57 35  UamF2YXgubmFtaW5
  75      * 0060: 6E 4C 6C 4A 6C 5A 6B 46 6B 5A 48 4C 72 6F 41 65  nLlJlZkFkZHLroAe
  76      * 0070: 61 41 6A 69 76 53 67 49 41 41 55 77 41 43 47 46  aAjivSgIAAUwACGF
  77      * 0080: 6B 5A 48 4A 55 65 58 42 6C 64 41 41 53 54 47 70  kZHJUeXBldAASTGp
  78      * 0090: 68 64 6D 45 76 62 47 46 75 0A 5A 79 39 54 64 48  hdmEvbGFu.Zy9TdH <
  79      * 00A0: 4A 70 62 6D 63 37 65 48 42 30 41 41 52 30 5A 58  Jpbmc7eHB0AAR0ZX
  80      * 00B0: 4E 30 64 58 49 41 41 6C 74 43 72 50 4D 58 2B 41  N0dXIAAltCrPMX+A
  81      * 00C0: 59 49 56 4F 41 43 41 41 42 34 63 41 41 41 41 49  YIVOACAAB4cAAAAI
  82      * 00D0: 41 41 41 51 49 44 42 41 55 47 42 77 67 4A 43 67  AAAQIDBAUGBwgJCg
  83      * 00E0: 73 4D 44 51 34 50 0A 45 42 45 53 45 78 51 56 46  sMDQ4P.EBESExQVF <
  84      * 00F0: 68 63 59 47 52 6F 62 48 42 30 65 48 79 41 68 49  hcYGRobHB0eHyAhI
  85      * 0100: 69 4D 6B 4A 53 59 6E 4B 43 6B 71 4B 79 77 74 4C  iMkJSYnKCkqKywtL
  86      * 0110: 69 38 77 4D 54 49 7A 4E 44 55 32 4E 7A 67 35 4F  i8wMTIzNDU2Nzg5O
  87      * 0120: 6A 73 38 50 54 34 2F 51 45 46 43 51 30 52 46 52  js8PT4/QEFCQ0RFR
  88      * 0130: 6B 64 49 0A 53 55 70 4C 54 45 31 4F 54 31 42 52  kdI.SUpLTE1OT1BR <
  89      * 0140: 55 6C 4E 55 56 56 5A 58 57 46 6C 61 57 31 78 64  UlNUVVZXWFlaW1xd
  90      * 0150: 58 6C 39 67 59 57 4A 6A 5A 47 56 6D 5A 32 68 70  Xl9gYWJjZGVmZ2hp
  91      * 0160: 61 6D 74 73 62 57 35 76 63 48 46 79 63 33 52 31  amtsbW5vcHFyc3R1
  92      * 0170: 64 6E 64 34 65 58 70 37 66 48 31 2B 66 77 3D 3D  dnd4eXp7fH1+fw==
  93      * 0180: 0A                                                                <
  94      */
  95     private static final String OLD_ENCODING = "rO0ABXNyABpqYXZheC5uYW1pbmcuQmluYXJ5UmVmQWRkctCak7Les4hHAgABWwADYnVmdAACW0J4\ncgAUamF2YXgubmFtaW5nLlJlZkFkZHLroAeaAjivSgIAAUwACGFkZHJUeXBldAASTGphdmEvbGFu\nZy9TdHJpbmc7eHB0AAR0ZXN0dXIAAltCrPMX+AYIVOACAAB4cAAAAIAAAQIDBAUGBwgJCgsMDQ4P\nEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdI\nSUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+fw==\n";
  96 
  97     /*
  98      * The new Base64 encoding uses '\r\n' as the line separator at 76 character
  99      * intervals:
 100      *
 101      * 0000: 72 4F 30 41 42 58 4E 79 41 42 70 71 59 58 5A 68  rO0ABXNyABpqYXZh
 102      * 0010: 65 43 35 75 59 57 31 70 62 6D 63 75 51 6D 6C 75  eC5uYW1pbmcuQmlu
 103      * 0020: 59 58 4A 35 55 6D 56 6D 51 57 52 6B 63 74 43 61  YXJ5UmVmQWRkctCa
 104      * 0030: 6B 37 4C 65 73 34 68 48 41 67 41 42 57 77 41 44  k7Les4hHAgABWwAD
 105      * 0040: 59 6E 56 6D 64 41 41 43 57 30 4A 34 0D 0A 63 67  YnVmdAACW0J4..cg <
 106      * 0050: 41 55 61 6D 46 32 59 58 67 75 62 6D 46 74 61 57  AUamF2YXgubmFtaW
 107      * 0060: 35 6E 4C 6C 4A 6C 5A 6B 46 6B 5A 48 4C 72 6F 41  5nLlJlZkFkZHLroA
 108      * 0070: 65 61 41 6A 69 76 53 67 49 41 41 55 77 41 43 47  eaAjivSgIAAUwACG
 109      * 0080: 46 6B 5A 48 4A 55 65 58 42 6C 64 41 41 53 54 47  FkZHJUeXBldAASTG
 110      * 0090: 70 68 64 6D 45 76 62 47 46 75 0D 0A 5A 79 39 54  phdmEvbGFu..Zy9T <
 111      * 00A0: 64 48 4A 70 62 6D 63 37 65 48 42 30 41 41 52 30  dHJpbmc7eHB0AAR0
 112      * 00B0: 5A 58 4E 30 64 58 49 41 41 6C 74 43 72 50 4D 58  ZXN0dXIAAltCrPMX
 113      * 00C0: 2B 41 59 49 56 4F 41 43 41 41 42 34 63 41 41 41  +AYIVOACAAB4cAAA
 114      * 00D0: 41 49 41 41 41 51 49 44 42 41 55 47 42 77 67 4A  AIAAAQIDBAUGBwgJ
 115      * 00E0: 43 67 73 4D 44 51 34 50 0D 0A 45 42 45 53 45 78  CgsMDQ4P..EBESEx <
 116      * 00F0: 51 56 46 68 63 59 47 52 6F 62 48 42 30 65 48 79  QVFhcYGRobHB0eHy
 117      * 0100: 41 68 49 69 4D 6B 4A 53 59 6E 4B 43 6B 71 4B 79  AhIiMkJSYnKCkqKy
 118      * 0110: 77 74 4C 69 38 77 4D 54 49 7A 4E 44 55 32 4E 7A  wtLi8wMTIzNDU2Nz
 119      * 0120: 67 35 4F 6A 73 38 50 54 34 2F 51 45 46 43 51 30  g5Ojs8PT4/QEFCQ0
 120      * 0130: 52 46 52 6B 64 49 0D 0A 53 55 70 4C 54 45 31 4F  RFRkdI..SUpLTE1O <
 121      * 0140: 54 31 42 52 55 6C 4E 55 56 56 5A 58 57 46 6C 61  T1BRUlNUVVZXWFla
 122      * 0150: 57 31 78 64 58 6C 39 67 59 57 4A 6A 5A 47 56 6D  W1xdXl9gYWJjZGVm
 123      * 0160: 5A 32 68 70 61 6D 74 73 62 57 35 76 63 48 46 79  Z2hpamtsbW5vcHFy
 124      * 0170: 63 33 52 31 64 6E 64 34 65 58 70 37 66 48 31 2B  c3R1dnd4eXp7fH1+
 125      * 0180: 66 77 3D 3D
 126      */
 127     private static final String NEW_ENCODING = "rO0ABXNyABpqYXZheC5uYW1pbmcuQmluYXJ5UmVmQWRkctCak7Les4hHAgABWwADYnVmdAACW0J4\r\ncgAUamF2YXgubmFtaW5nLlJlZkFkZHLroAeaAjivSgIAAUwACGFkZHJUeXBldAASTGphdmEvbGFu\r\nZy9TdHJpbmc7eHB0AAR0ZXN0dXIAAltCrPMX+AYIVOACAAB4cAAAAIAAAQIDBAUGBwgJCgsMDQ4P\r\nEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdI\r\nSUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+fw==";
 128 
 129     /*
 130      * Binary-valued JNDI RefAddr test object
 131      */
 132     private static final RefAddr BINARY_REF_ADDR =
 133         new BinaryRefAddr("test", new byte[] {
 134         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
 135         0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
 136         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
 137         0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
 138         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
 139         0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
 140         0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
 141         0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
 142         0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
 143         0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
 144         0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
 145     });
 146 
 147     public static void main(String[] args) throws Exception {
 148 
 149         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 150         System.out.println("Old Base64 encoded serialized RefAddr object:\n" +
 151             OLD_ENCODING);
 152         System.out.println("Decode using old Base64 decoder...");
 153         deserialize(new BASE64Decoder().decodeBuffer(OLD_ENCODING));
 154 
 155         System.out.println("----");
 156 
 157         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 158         System.out.println("Old Base64 encoded serialized RefAddr object:\n" +
 159             OLD_ENCODING);
 160         System.out.println("Decode using new Base64 decoder...");
 161         deserialize(new BASE64Decoder().decodeBuffer(OLD_ENCODING));
 162 
 163         System.out.println("----");
 164 
 165         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 166         System.out.println("New Base64 encoded serialized RefAddr object:\n" +
 167             NEW_ENCODING + "\n");
 168         System.out.println("Decode using old Base64 decoder...");
 169         deserialize(new BASE64Decoder().decodeBuffer(OLD_ENCODING));
 170 
 171         System.out.println("----");
 172 
 173         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 174         System.out.println("New Base64 encoded serialized RefAddr object:\n" +
 175             NEW_ENCODING + "\n");
 176         System.out.println("Decode using new Base64 decoder...");
 177         deserialize(Base64.getMimeDecoder().decode(NEW_ENCODING));
 178 
 179         System.out.println("----");
 180     }
 181 
 182     /*
 183      * Deserialize the decoded Base64 bytes to recover the BinaryRefAddr object.
 184      */
 185     private static void deserialize(byte[] bytes) throws Exception {
 186 
 187         //System.out.println("\nSerialized RefAddr object: ");
 188         //System.out.println(new sun.misc.HexDumpEncoder().encode(bytes));
 189 
 190         ObjectInputStream objectStream =
 191             new ObjectInputStream(new ByteArrayInputStream(bytes));
 192         Object object = objectStream.readObject();
 193         if (!BINARY_REF_ADDR.equals(object)) {
 194             throw new Exception("Recovered object does not match the original");
 195         }
 196         System.out.println("Recovered RefAddr object:\n" + object);
 197     }
 198 
 199     /*
 200      * Dumps the encoding of a JNDI Reference object during an attempt to store
 201      * in an LDAP directory.
 202      */
 203     private static void storeObjectInLDAP() {
 204         Hashtable env = new Hashtable();
 205         env.put(Context.REFERRAL, "follow"); // omit an LDAP control
 206         env.put("java.naming.ldap.version", "3"); // omit LDAP bind operation
 207         env.put("com.sun.jndi.ldap.trace.ber", System.err); // dump protocol
 208         try {
 209             DirContext ctx = new InitialDirContext(env);
 210             Reference reference = new Reference("test", BINARY_REF_ADDR);
 211             ctx.bind("ldap://ldap.example.com/cn=test", reference);
 212         } catch (NamingException ignore) {
 213         }
 214     }
 215 }