1 /* 2 * Copyright (c) 2003, 2017, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.pkcs11; 27 28 import java.math.BigInteger; 29 30 import java.security.*; 31 import java.security.spec.*; 32 33 import javax.crypto.*; 34 import javax.crypto.interfaces.*; 35 import javax.crypto.spec.*; 36 37 import static sun.security.pkcs11.TemplateManager.*; 38 import sun.security.pkcs11.wrapper.*; 39 import static sun.security.pkcs11.wrapper.PKCS11Constants.*; 40 import sun.security.util.KeyUtil; 41 42 /** 43 * KeyAgreement implementation class. This class currently supports 44 * DH. 45 * 46 * @author Andreas Sterbenz 47 * @since 1.5 48 */ 49 final class P11KeyAgreement extends KeyAgreementSpi { 50 51 // token instance 52 private final Token token; 53 54 // algorithm name 55 private final String algorithm; 56 57 // mechanism id 58 private final long mechanism; 59 60 // private key, if initialized 61 private P11Key privateKey; 62 63 // other sides public value ("y"), if doPhase() already called 64 private BigInteger publicValue; 65 66 // length of the secret to be derived 67 private int secretLen; 68 69 // KeyAgreement from SunJCE as fallback for > 2 party agreement 70 private KeyAgreement multiPartyAgreement; 71 72 private static class AllowKDF { 73 74 private static final boolean VALUE = getValue(); 75 76 private static boolean getValue() { 77 return AccessController.doPrivileged( 78 (PrivilegedAction<Boolean>) 79 () -> Boolean.getBoolean("jdk.crypto.KeyAgreement.legacyKDF")); 80 } 81 } 82 83 P11KeyAgreement(Token token, String algorithm, long mechanism) { 84 super(); 85 this.token = token; 86 this.algorithm = algorithm; 87 this.mechanism = mechanism; 88 } 89 90 // see JCE spec 91 protected void engineInit(Key key, SecureRandom random) 92 throws InvalidKeyException { 93 if (key instanceof PrivateKey == false) { 94 throw new InvalidKeyException 95 ("Key must be instance of PrivateKey"); 96 } 97 privateKey = P11KeyFactory.convertKey(token, key, algorithm); 98 publicValue = null; 99 multiPartyAgreement = null; 100 } 101 102 // see JCE spec 103 protected void engineInit(Key key, AlgorithmParameterSpec params, 104 SecureRandom random) throws InvalidKeyException, 105 InvalidAlgorithmParameterException { 106 if (params != null) { 107 throw new InvalidAlgorithmParameterException 108 ("Parameters not supported"); 109 } 110 engineInit(key, random); 111 } 112 113 // see JCE spec 114 protected Key engineDoPhase(Key key, boolean lastPhase) 115 throws InvalidKeyException, IllegalStateException { 116 if (privateKey == null) { 117 throw new IllegalStateException("Not initialized"); 118 } 119 if (publicValue != null) { 120 throw new IllegalStateException("Phase already executed"); 121 } 122 // PKCS#11 only allows key agreement between 2 parties 123 // JCE allows >= 2 parties. To support that case (for compatibility 124 // and to pass JCK), fall back to SunJCE in this case. 125 // NOTE that we initialize using the P11Key, which will fail if it 126 // is sensitive/unextractable. However, this is not an issue in the 127 // compatibility configuration, which is all we are targeting here. 128 if ((multiPartyAgreement != null) || (lastPhase == false)) { 129 if (multiPartyAgreement == null) { 130 try { 131 multiPartyAgreement = KeyAgreement.getInstance 132 ("DH", P11Util.getSunJceProvider()); 133 multiPartyAgreement.init(privateKey); 134 } catch (NoSuchAlgorithmException e) { 135 throw new InvalidKeyException 136 ("Could not initialize multi party agreement", e); 137 } 138 } 139 return multiPartyAgreement.doPhase(key, lastPhase); 140 } 141 if ((key instanceof PublicKey == false) 142 || (key.getAlgorithm().equals(algorithm) == false)) { 143 throw new InvalidKeyException 144 ("Key must be a PublicKey with algorithm DH"); 145 } 146 BigInteger p, g, y; 147 if (key instanceof DHPublicKey) { 148 DHPublicKey dhKey = (DHPublicKey)key; 149 150 // validate the Diffie-Hellman public key 151 KeyUtil.validate(dhKey); 152 153 y = dhKey.getY(); 154 DHParameterSpec params = dhKey.getParams(); 155 p = params.getP(); 156 g = params.getG(); 157 } else { 158 // normally, DH PublicKeys will always implement DHPublicKey 159 // just in case not, attempt conversion 160 P11DHKeyFactory kf = new P11DHKeyFactory(token, "DH"); 161 try { 162 DHPublicKeySpec spec = kf.engineGetKeySpec( 163 key, DHPublicKeySpec.class); 164 165 // validate the Diffie-Hellman public key 166 KeyUtil.validate(spec); 167 168 y = spec.getY(); 169 p = spec.getP(); 170 g = spec.getG(); 171 } catch (InvalidKeySpecException e) { 172 throw new InvalidKeyException("Could not obtain key values", e); 173 } 174 } 175 // if parameters of private key are accessible, verify that 176 // they match parameters of public key 177 // XXX p and g should always be readable, even if the key is sensitive 178 if (privateKey instanceof DHPrivateKey) { 179 DHPrivateKey dhKey = (DHPrivateKey)privateKey; 180 DHParameterSpec params = dhKey.getParams(); 181 if ((p.equals(params.getP()) == false) 182 || (g.equals(params.getG()) == false)) { 183 throw new InvalidKeyException 184 ("PublicKey DH parameters must match PrivateKey DH parameters"); 185 } 186 } 187 publicValue = y; 188 // length of the secret is length of key 189 secretLen = (p.bitLength() + 7) >> 3; 190 return null; 191 } 192 193 // see JCE spec 194 protected byte[] engineGenerateSecret() throws IllegalStateException { 195 if (multiPartyAgreement != null) { 196 byte[] val = multiPartyAgreement.generateSecret(); 197 multiPartyAgreement = null; 198 return val; 199 } 200 if ((privateKey == null) || (publicValue == null)) { 201 throw new IllegalStateException("Not initialized correctly"); 202 } 203 Session session = null; 204 try { 205 session = token.getOpSession(); 206 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { 207 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), 208 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET), 209 }; 210 attributes = token.getAttributes 211 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes); 212 privateKey.incNativeKeyRef(); 213 long keyID; 214 try { 215 keyID = token.p11.C_DeriveKey(session.id(), 216 new CK_MECHANISM(mechanism, publicValue), 217 privateKey.keyID, attributes); 218 } finally { 219 privateKey.decNativeKeyRef(); 220 } 221 attributes = new CK_ATTRIBUTE[] { 222 new CK_ATTRIBUTE(CKA_VALUE) 223 }; 224 token.p11.C_GetAttributeValue(session.id(), keyID, attributes); 225 byte[] secret = attributes[0].getByteArray(); 226 token.p11.C_DestroyObject(session.id(), keyID); 227 // Some vendors, e.g. NSS, trim off the leading 0x00 byte(s) from 228 // the generated secret. Thus, we need to check the secret length 229 // and trim/pad it so the returned value has the same length as 230 // the modulus size 231 if (secret.length == secretLen) { 232 return secret; 233 } else { 234 if (secret.length > secretLen) { 235 // Shouldn't happen; but check just in case 236 throw new ProviderException("generated secret is out-of-range"); 237 } 238 byte[] newSecret = new byte[secretLen]; 239 System.arraycopy(secret, 0, newSecret, secretLen - secret.length, 240 secret.length); 241 return newSecret; 242 } 243 } catch (PKCS11Exception e) { 244 throw new ProviderException("Could not derive key", e); 245 } finally { 246 publicValue = null; 247 token.releaseSession(session); 248 } 249 } 250 251 // see JCE spec 252 protected int engineGenerateSecret(byte[] sharedSecret, int 253 offset) throws IllegalStateException, ShortBufferException { 254 if (multiPartyAgreement != null) { 255 int n = multiPartyAgreement.generateSecret(sharedSecret, offset); 256 multiPartyAgreement = null; 257 return n; 258 } 259 if (offset + secretLen > sharedSecret.length) { 260 throw new ShortBufferException("Need " + secretLen 261 + " bytes, only " + (sharedSecret.length - offset) + " available"); 262 } 263 byte[] secret = engineGenerateSecret(); 264 System.arraycopy(secret, 0, sharedSecret, offset, secret.length); 265 return secret.length; 266 } 267 268 // see JCE spec 269 protected SecretKey engineGenerateSecret(String algorithm) 270 throws IllegalStateException, NoSuchAlgorithmException, 271 InvalidKeyException { 272 if (multiPartyAgreement != null) { 273 SecretKey key = multiPartyAgreement.generateSecret(algorithm); 274 multiPartyAgreement = null; 275 return key; 276 } 277 if (algorithm == null) { 278 throw new NoSuchAlgorithmException("Algorithm must not be null"); 279 } 280 281 if (algorithm.equals("TlsPremasterSecret")) { 282 // For now, only perform native derivation for TlsPremasterSecret 283 // as that is required for FIPS compliance. 284 // For other algorithms, there are unresolved issues regarding 285 // how this should work in JCE plus a Solaris truncation bug. 286 // (bug not yet filed). 287 return nativeGenerateSecret(algorithm); 288 } 289 290 if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && 291 !AllowKDF.VALUE) { 292 293 throw new NoSuchAlgorithmException("Unsupported secret key " 294 + "algorithm: " + algorithm); 295 } 296 297 byte[] secret = engineGenerateSecret(); 298 // Maintain compatibility for SunJCE: 299 // verify secret length is sensible for algorithm / truncate 300 // return generated key itself if possible 301 int keyLen; 302 if (algorithm.equalsIgnoreCase("DES")) { 303 keyLen = 8; 304 } else if (algorithm.equalsIgnoreCase("DESede")) { 305 keyLen = 24; 306 } else if (algorithm.equalsIgnoreCase("Blowfish")) { 307 keyLen = Math.min(56, secret.length); 308 } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { 309 keyLen = secret.length; 310 } else { 311 throw new NoSuchAlgorithmException 312 ("Unknown algorithm " + algorithm); 313 } 314 if (secret.length < keyLen) { 315 throw new InvalidKeyException("Secret too short"); 316 } 317 if (algorithm.equalsIgnoreCase("DES") || 318 algorithm.equalsIgnoreCase("DESede")) { 319 for (int i = 0; i < keyLen; i+=8) { 320 P11SecretKeyFactory.fixDESParity(secret, i); 321 } 322 } 323 return new SecretKeySpec(secret, 0, keyLen, algorithm); 324 } 325 326 private SecretKey nativeGenerateSecret(String algorithm) 327 throws IllegalStateException, NoSuchAlgorithmException, 328 InvalidKeyException { 329 if ((privateKey == null) || (publicValue == null)) { 330 throw new IllegalStateException("Not initialized correctly"); 331 } 332 long keyType = CKK_GENERIC_SECRET; 333 Session session = null; 334 try { 335 session = token.getObjSession(); 336 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { 337 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), 338 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), 339 }; 340 attributes = token.getAttributes 341 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); 342 privateKey.incNativeKeyRef(); 343 long keyID; 344 try { 345 keyID = token.p11.C_DeriveKey(session.id(), 346 new CK_MECHANISM(mechanism, publicValue), 347 privateKey.keyID, attributes); 348 } finally { 349 privateKey.decNativeKeyRef(); 350 } 351 CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] { 352 new CK_ATTRIBUTE(CKA_VALUE_LEN), 353 }; 354 token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes); 355 int keyLen = (int)lenAttributes[0].getLong(); 356 SecretKey key = P11Key.secretKey(session, keyID, algorithm, 357 keyLen << 3, attributes, true); 358 if ("RAW".equals(key.getFormat())) { 359 // Workaround for Solaris bug 6318543. 360 // Strip leading zeroes ourselves if possible (key not sensitive). 361 // This should be removed once the Solaris fix is available 362 // as here we always retrieve the CKA_VALUE even for tokens 363 // that do not have that bug. 364 byte[] keyBytes = key.getEncoded(); 365 byte[] newBytes = KeyUtil.trimZeroes(keyBytes); 366 if (keyBytes != newBytes) { 367 key = new SecretKeySpec(newBytes, algorithm); 368 } 369 } 370 return key; 371 } catch (PKCS11Exception e) { 372 throw new InvalidKeyException("Could not derive key", e); 373 } finally { 374 publicValue = null; 375 token.releaseSession(session); 376 } 377 } 378 379 }