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
30import java.io.*;
31import java.util.*;
32import javax.naming.*;
33import 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
59public 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}
195