1/*
2 * Copyright (c) 1997, 2015, 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 7146728
27 * @summary DHKeyAgreement2
28 * @author Jan Luehe
29 */
30
31import java.io.*;
32import java.math.BigInteger;
33import java.security.*;
34import java.security.spec.*;
35import java.security.interfaces.*;
36import javax.crypto.*;
37import javax.crypto.spec.*;
38import javax.crypto.interfaces.*;
39
40/**
41 * This test utility executes the Diffie-Hellman key agreement protocol
42 * between 2 parties: Alice and Bob.
43 *
44 * By default, preconfigured parameters (1024 bit prime modulus and base
45 * generator used by SKIP) are used.
46 * If this program is called with the "-gen" option, a new set of parameters
47 * are created.
48 */
49
50public class DHKeyAgreement2 {
51
52    private static final String SUNJCE = "SunJCE";
53    private DHKeyAgreement2() {}
54
55    public static void main(String argv[]) throws Exception {
56            String mode = "USE_SKIP_DH_PARAMS";
57
58            DHKeyAgreement2 keyAgree = new DHKeyAgreement2();
59
60            if (argv.length > 1) {
61                keyAgree.usage();
62                throw new Exception("Wrong number of command options");
63            } else if (argv.length == 1) {
64                if (!(argv[0].equals("-gen"))) {
65                    keyAgree.usage();
66                    throw new Exception("Unrecognized flag: " + argv[0]);
67                }
68                mode = "GENERATE_DH_PARAMS";
69            }
70
71            keyAgree.run(mode);
72            System.out.println("Test Passed");
73    }
74
75    private void run(String mode) throws Exception {
76
77        DHParameterSpec dhSkipParamSpec;
78
79        if (mode.equals("GENERATE_DH_PARAMS")) {
80            // Some central authority creates new DH parameters
81            System.err.println("Creating Diffie-Hellman parameters ...");
82            AlgorithmParameterGenerator paramGen
83                = AlgorithmParameterGenerator.getInstance("DH", SUNJCE);
84            paramGen.init(512);
85            AlgorithmParameters params = paramGen.generateParameters();
86            dhSkipParamSpec = (DHParameterSpec)params.getParameterSpec
87                (DHParameterSpec.class);
88        } else {
89            // use some pre-generated, default DH parameters
90            System.err.println("Using SKIP Diffie-Hellman parameters");
91            dhSkipParamSpec = new DHParameterSpec(skip1024Modulus,
92                                                  skip1024Base);
93        }
94
95        /*
96         * Alice creates her own DH key pair, using the DH parameters from
97         * above
98         */
99        System.err.println("ALICE: Generate DH keypair ...");
100        KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", SUNJCE);
101        aliceKpairGen.initialize(dhSkipParamSpec);
102        KeyPair aliceKpair = aliceKpairGen.generateKeyPair();
103        System.out.println("Alice DH public key:\n" +
104                           aliceKpair.getPublic().toString());
105        System.out.println("Alice DH private key:\n" +
106                           aliceKpair.getPrivate().toString());
107        DHParameterSpec dhParamSpec =
108            ((DHPublicKey)aliceKpair.getPublic()).getParams();
109        AlgorithmParameters algParams = AlgorithmParameters.getInstance("DH", SUNJCE);
110        algParams.init(dhParamSpec);
111        System.out.println("Alice DH parameters:\n"
112                           + algParams.toString());
113
114        // Alice executes Phase1 of her version of the DH protocol
115        System.err.println("ALICE: Execute PHASE1 ...");
116        KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH", SUNJCE);
117        aliceKeyAgree.init(aliceKpair.getPrivate());
118
119        // Alice encodes her public key, and sends it over to Bob.
120        byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
121
122        /*
123         * Let's turn over to Bob. Bob has received Alice's public key
124         * in encoded format.
125         * He instantiates a DH public key from the encoded key material.
126         */
127        KeyFactory bobKeyFac = KeyFactory.getInstance("DH", SUNJCE);
128        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec
129            (alicePubKeyEnc);
130        PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
131
132        /*
133         * Bob gets the DH parameters associated with Alice's public key.
134         * He must use the same parameters when he generates his own key
135         * pair.
136         */
137        dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
138
139        // Bob creates his own DH key pair
140        System.err.println("BOB: Generate DH keypair ...");
141        KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH", SUNJCE);
142        bobKpairGen.initialize(dhParamSpec);
143        KeyPair bobKpair = bobKpairGen.generateKeyPair();
144        System.out.println("Bob DH public key:\n" +
145                           bobKpair.getPublic().toString());
146        System.out.println("Bob DH private key:\n" +
147                           bobKpair.getPrivate().toString());
148
149        // Bob executes Phase1 of his version of the DH protocol
150        System.err.println("BOB: Execute PHASE1 ...");
151        KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH", SUNJCE);
152        bobKeyAgree.init(bobKpair.getPrivate());
153
154        // Bob encodes his public key, and sends it over to Alice.
155        byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
156
157        /*
158         * Alice uses Bob's public key for Phase2 of her version of the DH
159         * protocol.
160         * Before she can do so, she has to instanticate a DH public key
161         * from Bob's encoded key material.
162         */
163        KeyFactory aliceKeyFac = KeyFactory.getInstance("DH", SUNJCE);
164        x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
165        PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
166        System.err.println("ALICE: Execute PHASE2 ...");
167        aliceKeyAgree.doPhase(bobPubKey, true);
168
169        /*
170         * Bob uses Alice's public key for Phase2 of his version of the DH
171         * protocol.
172         */
173        System.err.println("BOB: Execute PHASE2 ...");
174        bobKeyAgree.doPhase(alicePubKey, true);
175
176        /*
177         * At this stage, both Alice and Bob have completed the DH key
178         * agreement protocol.
179         * Each generates the (same) shared secret.
180         */
181        byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
182        int aliceLen = aliceSharedSecret.length;
183
184        // check if alice's key agreement has been reset afterwards
185        try {
186            aliceKeyAgree.generateSecret();
187            throw new Exception("Error: alice's KeyAgreement not reset");
188        } catch (IllegalStateException e) {
189            System.out.println("EXPECTED:  " + e.getMessage());
190        }
191
192        byte[] bobSharedSecret = new byte[aliceLen];
193        int bobLen;
194        try {
195            // provide output buffer that is too short
196            bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 1);
197        } catch (ShortBufferException e) {
198            System.out.println("EXPECTED:  " + e.getMessage());
199        }
200        // retry w/ output buffer of required size
201        bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 0);
202
203        // check if bob's key agreement has been reset afterwards
204        try {
205            bobKeyAgree.generateSecret(bobSharedSecret, 0);
206            throw new Exception("Error: bob's KeyAgreement not reset");
207        } catch (IllegalStateException e) {
208            System.out.println("EXPECTED:  " + e.getMessage());
209        }
210
211        System.out.println("Alice secret: " + toHexString(aliceSharedSecret));
212        System.out.println("Bob secret: " + toHexString(bobSharedSecret));
213
214        if (aliceLen != bobLen) {
215            throw new Exception("Shared secrets have different lengths");
216        }
217        for (int i=0; i<aliceLen; i++) {
218            if (aliceSharedSecret[i] != bobSharedSecret[i]) {
219                throw new Exception("Shared secrets differ");
220            }
221        }
222        System.err.println("Shared secrets are the same");
223
224        // Now let's return the shared secret as a SecretKey object
225        // and use it for encryption
226        System.out.println("Return shared secret as SecretKey object ...");
227        bobKeyAgree.doPhase(alicePubKey, true);
228        SecretKey desKey = bobKeyAgree.generateSecret("DES");
229
230        Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
231        desCipher.init(Cipher.ENCRYPT_MODE, desKey);
232
233        byte[] cleartext = "This is just an example".getBytes();
234        byte[] ciphertext = desCipher.doFinal(cleartext);
235
236        desCipher.init(Cipher.DECRYPT_MODE, desKey);
237        byte[] cleartext1 = desCipher.doFinal(ciphertext);
238
239        int clearLen = cleartext.length;
240        int clear1Len = cleartext1.length;
241        if (clearLen != clear1Len) {
242            throw new Exception("DIFFERENT");
243        }
244        for (int i=0; i < clear1Len; i++) {
245            if (cleartext[i] != cleartext1[i]) {
246                throw new Exception("DIFFERENT");
247            }
248        }
249        System.err.println("SAME");
250    }
251
252    /*
253     * Converts a byte to hex digit and writes to the supplied buffer
254     */
255    private void byte2hex(byte b, StringBuffer buf) {
256        char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
257                            '9', 'A', 'B', 'C', 'D', 'E', 'F' };
258        int high = ((b & 0xf0) >> 4);
259        int low = (b & 0x0f);
260        buf.append(hexChars[high]);
261        buf.append(hexChars[low]);
262    }
263
264    /*
265     * Converts a byte array to hex string
266     */
267    private String toHexString(byte[] block) {
268        StringBuffer buf = new StringBuffer();
269
270        int len = block.length;
271
272        for (int i = 0; i < len; i++) {
273             byte2hex(block[i], buf);
274             if (i < len-1) {
275                 buf.append(":");
276             }
277        }
278        return buf.toString();
279    }
280
281    /*
282     * Prints the usage of this test.
283     */
284    private void usage() {
285        System.err.print("DHKeyAgreement usage: ");
286        System.err.println("[-gen]");
287    }
288
289    // The 1024 bit Diffie-Hellman modulus values used by SKIP
290    private static final byte skip1024ModulusBytes[] = {
291        (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
292        (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,
293        (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,
294        (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,
295        (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,
296        (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,
297        (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,
298        (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,
299        (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,
300        (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,
301        (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,
302        (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,
303        (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,
304        (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,
305        (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,
306        (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,
307        (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,
308        (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,
309        (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,
310        (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,
311        (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,
312        (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,
313        (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,
314        (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,
315        (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,
316        (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,
317        (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,
318        (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,
319        (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,
320        (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,
321        (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,
322        (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
323    };
324
325    // The SKIP 1024 bit modulus
326    private static final BigInteger skip1024Modulus
327    = new BigInteger(1, skip1024ModulusBytes);
328
329    // The base used with the SKIP 1024 bit modulus
330    private static final BigInteger skip1024Base = BigInteger.valueOf(2);
331}
332