1 /*
2  * Copyright (c) 2021, Amazon.com, Inc. 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 8254717 8263404
27  * @summary isAssignableFrom checks in KeyFactorySpi.engineGetKeySpec appear to be backwards.
28  * @author Greg Rubin, Ziyi Luo
29  */
30 
31 import java.math.BigInteger;
32 import java.security.KeyFactory;
33 import java.security.KeyPair;
34 import java.security.KeyPairGenerator;
35 import java.security.interfaces.RSAPrivateCrtKey;
36 import java.security.interfaces.RSAPrivateKey;
37 import java.security.spec.*;
38 
39 public class KeyFactoryGetKeySpecForInvalidSpec {
40 
41     // Test for 8263404: This method generates RSAPrivateKey (without Crt info) from a RSAPrivateCrtKey
privateCrtToPrivate(RSAPrivateCrtKey crtKey)42     public static RSAPrivateKey privateCrtToPrivate(RSAPrivateCrtKey crtKey) {
43         return new RSAPrivateKey() {
44             @Override
45             public BigInteger getPrivateExponent() {
46                 return crtKey.getPrivateExponent();
47             }
48 
49             @Override
50             public String getAlgorithm() {
51                 return crtKey.getAlgorithm();
52             }
53 
54             @Override
55             public String getFormat() {
56                 return crtKey.getFormat();
57             }
58 
59             @Override
60             public byte[] getEncoded() {
61                 return crtKey.getEncoded();
62             }
63 
64             @Override
65             public BigInteger getModulus() {
66                 return crtKey.getModulus();
67             }
68         };
69     }
70 
71     public static void main(String[] args) throws Exception {
72         KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA", "SunRsaSign");
73         kg.initialize(2048);
74         KeyPair pair = kg.generateKeyPair();
75 
76         KeyFactory factory = KeyFactory.getInstance("RSA");
77 
78         // === Case 1: private key is RSAPrivateCrtKey, keySpec is RSAPrivateKeySpec
79         // === Expected: return RSAPrivateCrtKeySpec
80         // Since RSAPrivateCrtKeySpec inherits from RSAPrivateKeySpec, we'd expect this next line to return an instance of RSAPrivateKeySpec
81         // (because the private key has CRT parts).
82         KeySpec spec = factory.getKeySpec(pair.getPrivate(), RSAPrivateKeySpec.class);
83         if (!(spec instanceof RSAPrivateCrtKeySpec)) {
84             throw new Exception("Spec should be an instance of RSAPrivateCrtKeySpec");
85         }
86 
87         // === Case 2: private key is RSAPrivateCrtKey, keySpec is RSAPrivateCrtKeySpec
88         // === Expected: return RSAPrivateCrtKeySpec
89         spec = factory.getKeySpec(pair.getPrivate(), RSAPrivateCrtKeySpec.class);
90         if (!(spec instanceof RSAPrivateCrtKeySpec)) {
91             throw new Exception("Spec should be an instance of RSAPrivateCrtKeySpec");
92         }
93 
94         // === Case 3: private key is RSAPrivateKey, keySpec is RSAPrivateKeySpec
95         // === Expected: return RSAPrivateKeySpec not RSAPrivateCrtKeySpec
96         RSAPrivateKey notCrtKey = privateCrtToPrivate((RSAPrivateCrtKey)pair.getPrivate());
97         // InvalidKeySpecException should not be thrown
98         KeySpec notCrtSpec = factory.getKeySpec(notCrtKey, RSAPrivateKeySpec.class);
99         if (notCrtSpec instanceof RSAPrivateCrtKeySpec) {
100             throw new Exception("Spec should be an instance of RSAPrivateKeySpec not RSAPrivateCrtKeySpec");
101         }
102         if (!(notCrtSpec instanceof RSAPrivateKeySpec)) {
103             throw new Exception("Spec should be an instance of RSAPrivateKeySpec");
104         }
105 
106         // === Case 4: private key is RSAPrivateKey, keySpec is RSAPrivateCrtKeySpec
107         // === Expected: throw InvalidKeySpecException
108         try {
109             factory.getKeySpec(notCrtKey, RSAPrivateCrtKeySpec.class);
110             throw new Exception("InvalidKeySpecException is expected but not thrown");
111         } catch (InvalidKeySpecException e) {
112             // continue;
113         }
114 
115         // This next line should give an InvalidKeySpec exception
116         try {
117             spec = factory.getKeySpec(pair.getPublic(), FakeX509Spec.class);
118             throw new Exception("InvalidKeySpecException is expected but not thrown");
119         } catch (final ClassCastException ex) {
120             throw new Exception("InvalidKeySpecException is expected ClassCastException is thrown", ex);
121         } catch (final InvalidKeySpecException ex) {
122             // Pass
123         }
124     }
125 
126     public static class FakeX509Spec extends X509EncodedKeySpec {
127         public FakeX509Spec(byte[] encodedKey) {
128             super(encodedKey);
129         }
130 
131         public FakeX509Spec(byte[] encodedKey, String algorithm) {
132             super(encodedKey, algorithm);
133         }
134     }
135 }
136