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 long keyID = token.p11.C_DeriveKey(session.id(), 213 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, 214 attributes); 215 attributes = new CK_ATTRIBUTE[] { 216 new CK_ATTRIBUTE(CKA_VALUE) 217 }; 218 token.p11.C_GetAttributeValue(session.id(), keyID, attributes); 219 byte[] secret = attributes[0].getByteArray(); 220 token.p11.C_DestroyObject(session.id(), keyID); 221 // Some vendors, e.g. NSS, trim off the leading 0x00 byte(s) from 222 // the generated secret. Thus, we need to check the secret length 223 // and trim/pad it so the returned value has the same length as 224 // the modulus size 225 if (secret.length == secretLen) { 226 return secret; 227 } else { 228 if (secret.length > secretLen) { 229 // Shouldn't happen; but check just in case 230 throw new ProviderException("generated secret is out-of-range"); 231 } 232 byte[] newSecret = new byte[secretLen]; 233 System.arraycopy(secret, 0, newSecret, secretLen - secret.length, 234 secret.length); 235 return newSecret; 236 } 237 } catch (PKCS11Exception e) { 238 throw new ProviderException("Could not derive key", e); 239 } finally { 240 publicValue = null; 241 token.releaseSession(session); 242 } 243 } 244 245 // see JCE spec 246 protected int engineGenerateSecret(byte[] sharedSecret, int 247 offset) throws IllegalStateException, ShortBufferException { 248 if (multiPartyAgreement != null) { 249 int n = multiPartyAgreement.generateSecret(sharedSecret, offset); 250 multiPartyAgreement = null; 251 return n; 252 } 253 if (offset + secretLen > sharedSecret.length) { 254 throw new ShortBufferException("Need " + secretLen 255 + " bytes, only " + (sharedSecret.length - offset) + " available"); 256 } 257 byte[] secret = engineGenerateSecret(); 258 System.arraycopy(secret, 0, sharedSecret, offset, secret.length); 259 return secret.length; 260 } 261 262 // see JCE spec 263 protected SecretKey engineGenerateSecret(String algorithm) 264 throws IllegalStateException, NoSuchAlgorithmException, 265 InvalidKeyException { 266 if (multiPartyAgreement != null) { 267 SecretKey key = multiPartyAgreement.generateSecret(algorithm); 268 multiPartyAgreement = null; 269 return key; 270 } 271 if (algorithm == null) { 272 throw new NoSuchAlgorithmException("Algorithm must not be null"); 273 } 274 275 if (algorithm.equals("TlsPremasterSecret")) { 276 // For now, only perform native derivation for TlsPremasterSecret 277 // as that is required for FIPS compliance. 278 // For other algorithms, there are unresolved issues regarding 279 // how this should work in JCE plus a Solaris truncation bug. 280 // (bug not yet filed). 281 return nativeGenerateSecret(algorithm); 282 } 283 284 if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && 285 !AllowKDF.VALUE) { 286 287 throw new NoSuchAlgorithmException("Unsupported secret key " 288 + "algorithm: " + algorithm); 289 } 290 291 byte[] secret = engineGenerateSecret(); 292 // Maintain compatibility for SunJCE: 293 // verify secret length is sensible for algorithm / truncate 294 // return generated key itself if possible 295 int keyLen; 296 if (algorithm.equalsIgnoreCase("DES")) { 297 keyLen = 8; 298 } else if (algorithm.equalsIgnoreCase("DESede")) { 299 keyLen = 24; 300 } else if (algorithm.equalsIgnoreCase("Blowfish")) { 301 keyLen = Math.min(56, secret.length); 302 } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { 303 keyLen = secret.length; 304 } else { 305 throw new NoSuchAlgorithmException 306 ("Unknown algorithm " + algorithm); 307 } 308 if (secret.length < keyLen) { 309 throw new InvalidKeyException("Secret too short"); 310 } 311 if (algorithm.equalsIgnoreCase("DES") || 312 algorithm.equalsIgnoreCase("DESede")) { 313 for (int i = 0; i < keyLen; i+=8) { 314 P11SecretKeyFactory.fixDESParity(secret, i); 315 } 316 } 317 return new SecretKeySpec(secret, 0, keyLen, algorithm); 318 } 319 320 private SecretKey nativeGenerateSecret(String algorithm) 321 throws IllegalStateException, NoSuchAlgorithmException, 322 InvalidKeyException { 323 if ((privateKey == null) || (publicValue == null)) { 324 throw new IllegalStateException("Not initialized correctly"); 325 } 326 long keyType = CKK_GENERIC_SECRET; 327 Session session = null; 328 try { 329 session = token.getObjSession(); 330 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { 331 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), 332 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), 333 }; 334 attributes = token.getAttributes 335 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); 336 long keyID = token.p11.C_DeriveKey(session.id(), 337 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, 338 attributes); 339 CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] { 340 new CK_ATTRIBUTE(CKA_VALUE_LEN), 341 }; 342 token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes); 343 int keyLen = (int)lenAttributes[0].getLong(); 344 SecretKey key = P11Key.secretKey 345 (session, keyID, algorithm, keyLen << 3, attributes); 346 if ("RAW".equals(key.getFormat())) { 347 // Workaround for Solaris bug 6318543. 348 // Strip leading zeroes ourselves if possible (key not sensitive). 349 // This should be removed once the Solaris fix is available 350 // as here we always retrieve the CKA_VALUE even for tokens 351 // that do not have that bug. 352 byte[] keyBytes = key.getEncoded(); 353 byte[] newBytes = KeyUtil.trimZeroes(keyBytes); 354 if (keyBytes != newBytes) { 355 key = new SecretKeySpec(newBytes, algorithm); 356 } 357 } 358 return key; 359 } catch (PKCS11Exception e) { 360 throw new InvalidKeyException("Could not derive key", e); 361 } finally { 362 publicValue = null; 363 token.releaseSession(session); 364 } 365 } 366 367 }