1 /* 2 * Copyright (c) 2018, 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 8184359 27 * @summary Standard tests on KeySpec, KeyFactory, KeyPairs and Keys. 28 * Arguments order <KeyExchangeAlgorithm> <Provider> <KeyGenAlgorithm> <Curve*> 29 * @run main KeySpecTest DiffieHellman SunJCE DiffieHellman 30 * @run main KeySpecTest ECDH SunEC EC 31 * @run main KeySpecTest XDH SunEC XDH X25519 32 * @run main KeySpecTest XDH SunEC XDH X448 33 */ 34 package test.java.security.KeyAgreement; 35 36 import java.io.ByteArrayInputStream; 37 import java.io.ByteArrayOutputStream; 38 import java.io.IOException; 39 import java.io.ObjectInputStream; 40 import java.io.ObjectOutputStream; 41 import java.security.KeyFactory; 42 import java.security.KeyPair; 43 import java.security.KeyPairGenerator; 44 import java.security.PrivateKey; 45 import java.security.PublicKey; 46 import java.security.spec.ECPrivateKeySpec; 47 import java.security.spec.ECPublicKeySpec; 48 import java.security.spec.PKCS8EncodedKeySpec; 49 import java.security.spec.X509EncodedKeySpec; 50 import java.security.spec.NamedParameterSpec; 51 import java.security.spec.XECPublicKeySpec; 52 import java.security.spec.XECPrivateKeySpec; 53 import java.security.spec.KeySpec; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Arrays; 57 import javax.crypto.KeyAgreement; 58 import javax.crypto.spec.DHPrivateKeySpec; 59 import javax.crypto.spec.DHPublicKeySpec; 60 61 import org.testng.annotations.Test; 62 import static org.junit.Assert.fail; 63 64 public class KeySpecTest { 65 66 @Test testDHKeySpecs()67 public void testDHKeySpecs() throws Exception { 68 KeyPair kp = genKeyPair("BC","DiffieHellman", 69 "DiffieHellman"); 70 testKeySpecs("BC", "DiffieHellman", "DiffieHellman", kp); 71 testEncodedKeySpecs("BC", "DiffieHellman", "DiffieHellman", kp); 72 } 73 74 @Test testECDHKeySpecs()75 public void testECDHKeySpecs() throws Exception { 76 KeyPair kp = genKeyPair("AndroidOpenSSL","EC", 77 "EC"); 78 testKeySpecs("AndroidOpenSSL", "ECDH", "EC", kp); 79 testEncodedKeySpecs("AndroidOpenSSL", "ECDH", "EC", kp); 80 } 81 82 // BEGIN Android-removed: XDH is not yet supported 83 /* 84 @Test 85 public void testXDHKeySpecs() throws Exception { 86 KeyPair kp1 = genKeyPair("AndroidOpenSSL","XDH", 87 "X25519"); 88 KeyPair kp2 = genKeyPair("AndroidOpenSSL","XDH", 89 "X25519"); 90 testKeySpecs("AndroidOpenSSL", "XDH", "XDH", kp1); 91 testEncodedKeySpecs("AndroidOpenSSL", "XDH", "XDH", kp1); 92 testKeySpecs("AndroidOpenSSL", "XDH", "XDH", kp2); 93 testEncodedKeySpecs("AndroidOpenSSL", "XDH", "XDH", kp2); 94 } 95 */ 96 // END Android-removed: XDH is not yet supported 97 98 /** 99 * Generate keyPair based on KeyPairGenerator algorithm. 100 */ genKeyPair(String provider, String kpgAlgo, String kpgInit)101 private static KeyPair genKeyPair(String provider, String kpgAlgo, 102 String kpgInit) throws Exception { 103 104 KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgo, provider); 105 switch (kpgInit) { 106 case "DiffieHellman": 107 kpg.initialize(512); 108 break; 109 case "EC": 110 kpg.initialize(256); 111 break; 112 case "X25519": 113 kpg.initialize(255); 114 break; 115 case "X448": 116 kpg.initialize(448); 117 break; 118 default: 119 fail("Invalid Algo name " + kpgInit); 120 } 121 return kpg.generateKeyPair(); 122 } 123 124 /** 125 * Standard Test with Keys and the one generated through Spec. 126 */ testKeySpecs(String provider, String kaAlgo, String kpgAlgo, KeyPair kp)127 private static void testKeySpecs(String provider, String kaAlgo, 128 String kpgAlgo, KeyPair kp) throws Exception { 129 130 KeyFactory kf = KeyFactory.getInstance(kpgAlgo, provider); 131 // For each public KeySpec supported by KeyPairGenerator 132 for (Class pubSpecType 133 : getCompatibleKeySpecs(kpgAlgo, KeyType.PUBLIC)) { 134 //For each private KeySpec supported by KeyPairGenerator 135 for (Class priSpecType 136 : getCompatibleKeySpecs(kpgAlgo, KeyType.PRIVATE)) { 137 // Transform original PublicKey through KeySpec 138 KeySpec pubSpec = kf.getKeySpec(kp.getPublic(), pubSpecType); 139 PublicKey pubKey = kf.generatePublic(pubSpec); 140 // Transform original PrivateKey through KeySpec 141 KeySpec priSpec = kf.getKeySpec(kp.getPrivate(), priSpecType); 142 PrivateKey priKey = kf.generatePrivate(priSpec); 143 144 // Test the keys are equal after transformation through KeySpec. 145 testKeyEquals(kp, pubKey, priKey); 146 // Test the keys are serializable. 147 testSerialize(kp); 148 // Test Parameter name. 149 if (!kaAlgo.equals("DiffieHellman")) { 150 testParamName(priSpec, pubSpec); 151 } 152 // Compare KeyAgreement secret generated from original keys 153 // and by the keys after transformed through KeySpec. 154 if (!Arrays.equals(getKeyAgreementSecret(provider, kaAlgo, 155 kp.getPublic(), kp.getPrivate()), 156 getKeyAgreementSecret(provider, kaAlgo, 157 pubKey, priKey))) { 158 fail("KeyAgreement secret mismatch."); 159 } 160 } 161 } 162 } 163 164 /** 165 * Standard Test with Keys and the one generated from encoded bytes. 166 */ testEncodedKeySpecs(String provider, String kaAlgo, String kpgAlgo, KeyPair kp)167 private static void testEncodedKeySpecs(String provider, String kaAlgo, 168 String kpgAlgo, KeyPair kp) throws Exception { 169 170 KeyFactory kf = KeyFactory.getInstance(kpgAlgo, provider); 171 PKCS8EncodedKeySpec priSpec 172 = new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()); 173 PrivateKey priKey = kf.generatePrivate(priSpec); 174 175 X509EncodedKeySpec pubSpec 176 = new X509EncodedKeySpec(kp.getPublic().getEncoded()); 177 PublicKey pubKey = kf.generatePublic(pubSpec); 178 179 // Test the keys are equal after transformation through KeySpec. 180 testKeyEquals(kp, pubKey, priKey); 181 // Test the keys are serializable. 182 testSerialize(kp); 183 // Test Parameter name. 184 if (!kaAlgo.equals("DiffieHellman")) { 185 testParamName(priSpec, pubSpec); 186 } 187 // Compare KeyAgreement secret generated from original keys 188 // and by the keys after transformed through KeySpec. 189 if (!Arrays.equals(getKeyAgreementSecret(provider, kaAlgo, 190 kp.getPublic(), kp.getPrivate()), 191 getKeyAgreementSecret(provider, kaAlgo, pubKey, priKey))) { 192 fail("KeyAgreement secret mismatch."); 193 } 194 } 195 196 private enum KeyType { 197 PUBLIC, PRIVATE; 198 } 199 200 /** 201 * Provides Compatible KeySpec Type list for KeyPairGenerator algorithm. 202 */ getCompatibleKeySpecs(String kpgAlgo, KeyType type)203 private static List<Class> getCompatibleKeySpecs(String kpgAlgo, 204 KeyType type) { 205 206 List<Class> specs = new ArrayList<>(); 207 switch (kpgAlgo) { 208 case "DiffieHellman": 209 if (type == KeyType.PUBLIC) { 210 return Arrays.asList(X509EncodedKeySpec.class, 211 DHPublicKeySpec.class); 212 } else { 213 return Arrays.asList(PKCS8EncodedKeySpec.class, 214 DHPrivateKeySpec.class); 215 } 216 case "EC": 217 if (type == KeyType.PUBLIC) { 218 return Arrays.asList(X509EncodedKeySpec.class, 219 ECPublicKeySpec.class); 220 } else { 221 return Arrays.asList(PKCS8EncodedKeySpec.class, 222 ECPrivateKeySpec.class); 223 } 224 case "XDH": 225 if (type == KeyType.PUBLIC) { 226 return Arrays.asList(X509EncodedKeySpec.class, 227 XECPublicKeySpec.class); 228 } else { 229 return Arrays.asList(PKCS8EncodedKeySpec.class, 230 XECPrivateKeySpec.class); 231 } 232 } 233 return specs; 234 } 235 236 /** 237 * Generate KeyAgreement Secret. 238 */ getKeyAgreementSecret(String provider, String kaAlgo, PublicKey pubKey, PrivateKey priKey)239 private static byte[] getKeyAgreementSecret(String provider, String kaAlgo, 240 PublicKey pubKey, PrivateKey priKey) throws Exception { 241 242 KeyAgreement ka = KeyAgreement.getInstance(kaAlgo, provider); 243 ka.init(priKey); 244 ka.doPhase(pubKey, true); 245 return ka.generateSecret(); 246 } 247 248 /** 249 * Compare original KeyPair with transformed ones. 250 */ testKeyEquals(KeyPair kp, PublicKey pubKey, PrivateKey priKey)251 private static void testKeyEquals(KeyPair kp, PublicKey pubKey, 252 PrivateKey priKey) { 253 254 if (!kp.getPrivate().equals(priKey) 255 && kp.getPrivate().hashCode() != priKey.hashCode()) { 256 fail("PrivateKey is not equal with PrivateKey" 257 + " generated through KeySpec"); 258 } 259 if (!kp.getPublic().equals(pubKey) 260 && kp.getPublic().hashCode() != pubKey.hashCode()) { 261 fail("PublicKey is not equal with PublicKey" 262 + " generated through KeySpec"); 263 } 264 } 265 266 /** 267 * Compare the parameter names of Public/Private KeySpec from a pair. 268 */ testParamName(KeySpec priSpec, KeySpec pubSpec)269 private static void testParamName(KeySpec priSpec, KeySpec pubSpec) { 270 271 if (priSpec instanceof XECPrivateKeySpec 272 && pubSpec instanceof XECPublicKeySpec) { 273 if (((NamedParameterSpec) ((XECPrivateKeySpec) priSpec) 274 .getParams()).getName() 275 != ((NamedParameterSpec) ((XECPublicKeySpec) pubSpec) 276 .getParams()).getName()) { 277 fail("Curve name mismatch found"); 278 } 279 } 280 } 281 282 /** 283 * Test serialization of KeyPair and Keys it holds. 284 */ testSerialize(KeyPair keyPair)285 private static void testSerialize(KeyPair keyPair) throws Exception { 286 287 // Verify Serialized PrivateKey instance. 288 if (!keyPair.getPrivate().equals( 289 deserializedCopy(keyPair.getPrivate(), PrivateKey.class))) { 290 fail("PrivateKey is not equal with PrivateKey" 291 + " generated through Serialization"); 292 } 293 // Verify Serialized PublicKey instance. 294 if (!keyPair.getPublic().equals( 295 deserializedCopy(keyPair.getPublic(), PublicKey.class))) { 296 fail("PublicKey is not equal with PublicKey" 297 + " generated through Serialization"); 298 } 299 // Verify Serialized KeyPair instance. 300 KeyPair copy = deserializedCopy(keyPair, KeyPair.class); 301 if (!keyPair.getPrivate().equals(copy.getPrivate())) { 302 fail("PrivateKey is not equal with PrivateKey" 303 + " generated through Serialized KeyPair"); 304 } 305 if (!keyPair.getPublic().equals(copy.getPublic())) { 306 fail("PublicKey is not equal with PublicKey" 307 + " generated through Serialized KeyPair"); 308 } 309 } 310 deserializedCopy(T orig, Class<T> type)311 private static <T extends Object> T deserializedCopy(T orig, Class<T> type) 312 throws IOException, ClassNotFoundException { 313 return deserialize(serialize(orig), type); 314 } 315 316 /** 317 * Deserialize the Key object. 318 */ deserialize(byte[] serialized, Class<T> type)319 private static <T extends Object> T deserialize(byte[] serialized, 320 Class<T> type) throws IOException, ClassNotFoundException { 321 322 T key = null; 323 try (ByteArrayInputStream bis = new ByteArrayInputStream(serialized); 324 ObjectInputStream ois = new ObjectInputStream(bis)) { 325 key = (T) ois.readObject(); 326 } 327 return key; 328 } 329 330 /** 331 * Serialize the given Key object. 332 */ serialize(T key)333 private static <T extends Object> byte[] serialize(T key) 334 throws IOException { 335 336 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 337 ObjectOutputStream oos = new ObjectOutputStream(bos)) { 338 oos.writeObject(key); 339 return bos.toByteArray(); 340 } 341 } 342 }