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  */
  29 
  30 import java.io.*;
  31 import java.util.*;
  32 import javax.naming.*;
  33 import javax.naming.directory.*;
  34 
  35 /*
  36  * RFC 2713 specifies an encoding for Java objects stored in an LDAP directory.
  37  * Section 3.6 specifies how a binary-valued JNDI RefAddr object is encoded
  38  * in the value of a javaReferenceAttribute LDAP attribute: first the RefAddr
  39  * object is serialized and then it is encoded using Base64.
  40  *
  41  * Since JDK 9, the JNDI/LDAP provider uses the public Base64 encoder which
  42  * adheres strictly to the MIME encoding rules. The encoder inserts '\r\n'
  43  * as the line separator at intervals of 76 characters. Previously the
  44  * JNDI/LDAP provider used a private Base64 encoder which inserted '\n'
  45  * as the line separator. It is a compatible change.
  46  *
  47  * This test demonstrates that there is no compatability problem when
  48  * decoding using the new Base64 coder:
  49  *
  50  *   encoded bytes captured from s.m.BASE64Encoder, decode with j.u.Base64.Decoder => OK
  51  *   encoded bytes captured from j.u.Base64.Encoder, decode with j.u.Base64.Decoder => OK
  52  *
  53  *
  54  * NOTE: The two Base64 encodings used in this test were captured from
  55  *       LDAP protocol exchanges during attempts by the JNDI/LDAP provider
  56  *       to store a JNDI Reference test object.
  57  */
  58 
  59 public class Base64Test {
  60     /*
  61      * The old Base64 encoding uses '\n' as the line separator at 76 character
  62      * intervals:
  63      *
  64      * 0000: 72 4F 30 41 42 58 4E 79 41 42 70 71 59 58 5A 68  rO0ABXNyABpqYXZh
  65      * 0010: 65 43 35 75 59 57 31 70 62 6D 63 75 51 6D 6C 75  eC5uYW1pbmcuQmlu
  66      * 0020: 59 58 4A 35 55 6D 56 6D 51 57 52 6B 63 74 43 61  YXJ5UmVmQWRkctCa
  67      * 0030: 6B 37 4C 65 73 34 68 48 41 67 41 42 57 77 41 44  k7Les4hHAgABWwAD
  68      * 0040: 59 6E 56 6D 64 41 41 43 57 30 4A 34 0A 63 67 41  YnVmdAACW0J4.cgA <
  69      * 0050: 55 61 6D 46 32 59 58 67 75 62 6D 46 74 61 57 35  UamF2YXgubmFtaW5
  70      * 0060: 6E 4C 6C 4A 6C 5A 6B 46 6B 5A 48 4C 72 6F 41 65  nLlJlZkFkZHLroAe
  71      * 0070: 61 41 6A 69 76 53 67 49 41 41 55 77 41 43 47 46  aAjivSgIAAUwACGF
  72      * 0080: 6B 5A 48 4A 55 65 58 42 6C 64 41 41 53 54 47 70  kZHJUeXBldAASTGp
  73      * 0090: 68 64 6D 45 76 62 47 46 75 0A 5A 79 39 54 64 48  hdmEvbGFu.Zy9TdH <
  74      * 00A0: 4A 70 62 6D 63 37 65 48 42 30 41 41 52 30 5A 58  Jpbmc7eHB0AAR0ZX
  75      * 00B0: 4E 30 64 58 49 41 41 6C 74 43 72 50 4D 58 2B 41  N0dXIAAltCrPMX+A
  76      * 00C0: 59 49 56 4F 41 43 41 41 42 34 63 41 41 41 41 49  YIVOACAAB4cAAAAI
  77      * 00D0: 41 41 41 51 49 44 42 41 55 47 42 77 67 4A 43 67  AAAQIDBAUGBwgJCg
  78      * 00E0: 73 4D 44 51 34 50 0A 45 42 45 53 45 78 51 56 46  sMDQ4P.EBESExQVF <
  79      * 00F0: 68 63 59 47 52 6F 62 48 42 30 65 48 79 41 68 49  hcYGRobHB0eHyAhI
  80      * 0100: 69 4D 6B 4A 53 59 6E 4B 43 6B 71 4B 79 77 74 4C  iMkJSYnKCkqKywtL
  81      * 0110: 69 38 77 4D 54 49 7A 4E 44 55 32 4E 7A 67 35 4F  i8wMTIzNDU2Nzg5O
  82      * 0120: 6A 73 38 50 54 34 2F 51 45 46 43 51 30 52 46 52  js8PT4/QEFCQ0RFR
  83      * 0130: 6B 64 49 0A 53 55 70 4C 54 45 31 4F 54 31 42 52  kdI.SUpLTE1OT1BR <
  84      * 0140: 55 6C 4E 55 56 56 5A 58 57 46 6C 61 57 31 78 64  UlNUVVZXWFlaW1xd
  85      * 0150: 58 6C 39 67 59 57 4A 6A 5A 47 56 6D 5A 32 68 70  Xl9gYWJjZGVmZ2hp
  86      * 0160: 61 6D 74 73 62 57 35 76 63 48 46 79 63 33 52 31  amtsbW5vcHFyc3R1
  87      * 0170: 64 6E 64 34 65 58 70 37 66 48 31 2B 66 77 3D 3D  dnd4eXp7fH1+fw==
  88      * 0180: 0A                                                                <
  89      */
  90     private static final String OLD_ENCODING = "rO0ABXNyABpqYXZheC5uYW1pbmcuQmluYXJ5UmVmQWRkctCak7Les4hHAgABWwADYnVmdAACW0J4\ncgAUamF2YXgubmFtaW5nLlJlZkFkZHLroAeaAjivSgIAAUwACGFkZHJUeXBldAASTGphdmEvbGFu\nZy9TdHJpbmc7eHB0AAR0ZXN0dXIAAltCrPMX+AYIVOACAAB4cAAAAIAAAQIDBAUGBwgJCgsMDQ4P\nEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdI\nSUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+fw==\n";
  91 
  92     /*
  93      * The new Base64 encoding uses '\r\n' as the line separator at 76 character
  94      * intervals:
  95      *
  96      * 0000: 72 4F 30 41 42 58 4E 79 41 42 70 71 59 58 5A 68  rO0ABXNyABpqYXZh
  97      * 0010: 65 43 35 75 59 57 31 70 62 6D 63 75 51 6D 6C 75  eC5uYW1pbmcuQmlu
  98      * 0020: 59 58 4A 35 55 6D 56 6D 51 57 52 6B 63 74 43 61  YXJ5UmVmQWRkctCa
  99      * 0030: 6B 37 4C 65 73 34 68 48 41 67 41 42 57 77 41 44  k7Les4hHAgABWwAD
 100      * 0040: 59 6E 56 6D 64 41 41 43 57 30 4A 34 0D 0A 63 67  YnVmdAACW0J4..cg <
 101      * 0050: 41 55 61 6D 46 32 59 58 67 75 62 6D 46 74 61 57  AUamF2YXgubmFtaW
 102      * 0060: 35 6E 4C 6C 4A 6C 5A 6B 46 6B 5A 48 4C 72 6F 41  5nLlJlZkFkZHLroA
 103      * 0070: 65 61 41 6A 69 76 53 67 49 41 41 55 77 41 43 47  eaAjivSgIAAUwACG
 104      * 0080: 46 6B 5A 48 4A 55 65 58 42 6C 64 41 41 53 54 47  FkZHJUeXBldAASTG
 105      * 0090: 70 68 64 6D 45 76 62 47 46 75 0D 0A 5A 79 39 54  phdmEvbGFu..Zy9T <
 106      * 00A0: 64 48 4A 70 62 6D 63 37 65 48 42 30 41 41 52 30  dHJpbmc7eHB0AAR0
 107      * 00B0: 5A 58 4E 30 64 58 49 41 41 6C 74 43 72 50 4D 58  ZXN0dXIAAltCrPMX
 108      * 00C0: 2B 41 59 49 56 4F 41 43 41 41 42 34 63 41 41 41  +AYIVOACAAB4cAAA
 109      * 00D0: 41 49 41 41 41 51 49 44 42 41 55 47 42 77 67 4A  AIAAAQIDBAUGBwgJ
 110      * 00E0: 43 67 73 4D 44 51 34 50 0D 0A 45 42 45 53 45 78  CgsMDQ4P..EBESEx <
 111      * 00F0: 51 56 46 68 63 59 47 52 6F 62 48 42 30 65 48 79  QVFhcYGRobHB0eHy
 112      * 0100: 41 68 49 69 4D 6B 4A 53 59 6E 4B 43 6B 71 4B 79  AhIiMkJSYnKCkqKy
 113      * 0110: 77 74 4C 69 38 77 4D 54 49 7A 4E 44 55 32 4E 7A  wtLi8wMTIzNDU2Nz
 114      * 0120: 67 35 4F 6A 73 38 50 54 34 2F 51 45 46 43 51 30  g5Ojs8PT4/QEFCQ0
 115      * 0130: 52 46 52 6B 64 49 0D 0A 53 55 70 4C 54 45 31 4F  RFRkdI..SUpLTE1O <
 116      * 0140: 54 31 42 52 55 6C 4E 55 56 56 5A 58 57 46 6C 61  T1BRUlNUVVZXWFla
 117      * 0150: 57 31 78 64 58 6C 39 67 59 57 4A 6A 5A 47 56 6D  W1xdXl9gYWJjZGVm
 118      * 0160: 5A 32 68 70 61 6D 74 73 62 57 35 76 63 48 46 79  Z2hpamtsbW5vcHFy
 119      * 0170: 63 33 52 31 64 6E 64 34 65 58 70 37 66 48 31 2B  c3R1dnd4eXp7fH1+
 120      * 0180: 66 77 3D 3D
 121      */
 122     private static final String NEW_ENCODING = "rO0ABXNyABpqYXZheC5uYW1pbmcuQmluYXJ5UmVmQWRkctCak7Les4hHAgABWwADYnVmdAACW0J4\r\ncgAUamF2YXgubmFtaW5nLlJlZkFkZHLroAeaAjivSgIAAUwACGFkZHJUeXBldAASTGphdmEvbGFu\r\nZy9TdHJpbmc7eHB0AAR0ZXN0dXIAAltCrPMX+AYIVOACAAB4cAAAAIAAAQIDBAUGBwgJCgsMDQ4P\r\nEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdI\r\nSUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+fw==";
 123 
 124     /*
 125      * Binary-valued JNDI RefAddr test object
 126      */
 127     private static final RefAddr BINARY_REF_ADDR =
 128         new BinaryRefAddr("test", new byte[] {
 129         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
 130         0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
 131         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
 132         0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
 133         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
 134         0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
 135         0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
 136         0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
 137         0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
 138         0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
 139         0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
 140     });
 141 
 142     public static void main(String[] args) throws Exception {
 143 
 144         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 145         System.out.println("Old Base64 encoded serialized RefAddr object:\n" +
 146                 OLD_ENCODING + "\n");
 147         System.out.println("Decode using new Base64 decoder...");
 148         deserialize(Base64.getMimeDecoder().decode(OLD_ENCODING));
 149 
 150         System.out.println("----");
 151 
 152         System.out.println("\nOriginal RefAddr object:\n" + BINARY_REF_ADDR);
 153         System.out.println("New Base64 encoded serialized RefAddr object:\n" +
 154             NEW_ENCODING + "\n");
 155         System.out.println("Decode using new Base64 decoder...");
 156         deserialize(Base64.getMimeDecoder().decode(NEW_ENCODING));
 157 
 158         System.out.println("----");
 159     }
 160 
 161     /*
 162      * Deserialize the decoded Base64 bytes to recover the BinaryRefAddr object.
 163      */
 164     private static void deserialize(byte[] bytes) throws Exception {
 165 
 166         //System.out.println("\nSerialized RefAddr object: ");
 167         //System.out.println(new sun.security.util.HexDumpEncoder().encode(bytes));
 168 
 169         ObjectInputStream objectStream =
 170             new ObjectInputStream(new ByteArrayInputStream(bytes));
 171         Object object = objectStream.readObject();
 172         if (!BINARY_REF_ADDR.equals(object)) {
 173             throw new Exception("Recovered object does not match the original");
 174         }
 175         System.out.println("Recovered RefAddr object:\n" + object);
 176     }
 177 
 178     /*
 179      * Dumps the encoding of a JNDI Reference object during an attempt to store
 180      * in an LDAP directory.
 181      */
 182     private static void storeObjectInLDAP() {
 183         Hashtable env = new Hashtable();
 184         env.put(Context.REFERRAL, "follow"); // omit an LDAP control
 185         env.put("java.naming.ldap.version", "3"); // omit LDAP bind operation
 186         env.put("com.sun.jndi.ldap.trace.ber", System.err); // dump protocol
 187         try {
 188             DirContext ctx = new InitialDirContext(env);
 189             Reference reference = new Reference("test", BINARY_REF_ADDR);
 190             ctx.bind("ldap://ldap.example.com/cn=test", reference);
 191         } catch (NamingException ignore) {
 192         }
 193     }
 194 }