1 /* 2 * Copyright (c) 2003, 2013, 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 P11KeyAgreement(Token token, String algorithm, long mechanism) { 73 super(); 74 this.token = token; 75 this.algorithm = algorithm; 76 this.mechanism = mechanism; 77 } 78 79 // see JCE spec 80 protected void engineInit(Key key, SecureRandom random) 81 throws InvalidKeyException { 82 if (key instanceof PrivateKey == false) { 83 throw new InvalidKeyException 84 ("Key must be instance of PrivateKey"); 85 } 86 privateKey = P11KeyFactory.convertKey(token, key, algorithm); 87 publicValue = null; 88 multiPartyAgreement = null; 89 } 90 91 // see JCE spec 92 protected void engineInit(Key key, AlgorithmParameterSpec params, 93 SecureRandom random) throws InvalidKeyException, 94 InvalidAlgorithmParameterException { 95 if (params != null) { 96 throw new InvalidAlgorithmParameterException 97 ("Parameters not supported"); 98 } 99 engineInit(key, random); 100 } 101 102 // see JCE spec 103 protected Key engineDoPhase(Key key, boolean lastPhase) 104 throws InvalidKeyException, IllegalStateException { 105 if (privateKey == null) { 106 throw new IllegalStateException("Not initialized"); 107 } 108 if (publicValue != null) { 109 throw new IllegalStateException("Phase already executed"); 110 } 111 // PKCS#11 only allows key agreement between 2 parties 112 // JCE allows >= 2 parties. To support that case (for compatibility 113 // and to pass JCK), fall back to SunJCE in this case. 114 // NOTE that we initialize using the P11Key, which will fail if it 115 // is sensitive/unextractable. However, this is not an issue in the 116 // compatibility configuration, which is all we are targeting here. 117 if ((multiPartyAgreement != null) || (lastPhase == false)) { 118 if (multiPartyAgreement == null) { 119 try { 120 multiPartyAgreement = KeyAgreement.getInstance 121 ("DH", P11Util.getSunJceProvider()); 122 multiPartyAgreement.init(privateKey); 123 } catch (NoSuchAlgorithmException e) { 124 throw new InvalidKeyException 125 ("Could not initialize multi party agreement", e); 126 } 127 } 128 return multiPartyAgreement.doPhase(key, lastPhase); 129 } 130 if ((key instanceof PublicKey == false) 131 || (key.getAlgorithm().equals(algorithm) == false)) { 132 throw new InvalidKeyException 133 ("Key must be a PublicKey with algorithm DH"); 134 } 135 BigInteger p, g, y; 136 if (key instanceof DHPublicKey) { 137 DHPublicKey dhKey = (DHPublicKey)key; 138 139 // validate the Diffie-Hellman public key 140 KeyUtil.validate(dhKey); 141 142 y = dhKey.getY(); 143 DHParameterSpec params = dhKey.getParams(); 144 p = params.getP(); 145 g = params.getG(); 146 } else { 147 // normally, DH PublicKeys will always implement DHPublicKey 148 // just in case not, attempt conversion 149 P11DHKeyFactory kf = new P11DHKeyFactory(token, "DH"); 150 try { 151 DHPublicKeySpec spec = kf.engineGetKeySpec( 152 key, DHPublicKeySpec.class); 153 154 // validate the Diffie-Hellman public key 155 KeyUtil.validate(spec); 156 157 y = spec.getY(); 158 p = spec.getP(); 159 g = spec.getG(); 160 } catch (InvalidKeySpecException e) { 161 throw new InvalidKeyException("Could not obtain key values", e); 162 } 163 } 164 // if parameters of private key are accessible, verify that 165 // they match parameters of public key 166 // XXX p and g should always be readable, even if the key is sensitive 167 if (privateKey instanceof DHPrivateKey) { 168 DHPrivateKey dhKey = (DHPrivateKey)privateKey; 169 DHParameterSpec params = dhKey.getParams(); 170 if ((p.equals(params.getP()) == false) 171 || (g.equals(params.getG()) == false)) { 172 throw new InvalidKeyException 173 ("PublicKey DH parameters must match PrivateKey DH parameters"); 174 } 175 } 176 publicValue = y; 177 // length of the secret is length of key 178 secretLen = (p.bitLength() + 7) >> 3; 179 return null; 180 } 181 182 // see JCE spec 183 protected byte[] engineGenerateSecret() throws IllegalStateException { 184 if (multiPartyAgreement != null) { 185 byte[] val = multiPartyAgreement.generateSecret(); 186 multiPartyAgreement = null; 187 return val; 188 } 189 if ((privateKey == null) || (publicValue == null)) { 190 throw new IllegalStateException("Not initialized correctly"); 191 } 192 Session session = null; 193 try { 194 session = token.getOpSession(); 195 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { 196 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), 197 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET), 198 }; 199 attributes = token.getAttributes 200 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes); 201 long keyID = token.p11.C_DeriveKey(session.id(), 202 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, 203 attributes); 204 attributes = new CK_ATTRIBUTE[] { 205 new CK_ATTRIBUTE(CKA_VALUE) 206 }; 207 token.p11.C_GetAttributeValue(session.id(), keyID, attributes); 208 byte[] secret = attributes[0].getByteArray(); 209 token.p11.C_DestroyObject(session.id(), keyID); 210 // Some vendors, e.g. NSS, trim off the leading 0x00 byte(s) from 211 // the generated secret. Thus, we need to check the secret length 212 // and trim/pad it so the returned value has the same length as 213 // the modulus size 214 if (secret.length == secretLen) { 215 return secret; 216 } else { 217 if (secret.length > secretLen) { 218 // Shouldn't happen; but check just in case 219 throw new ProviderException("generated secret is out-of-range"); 220 } 221 byte[] newSecret = new byte[secretLen]; 222 System.arraycopy(secret, 0, newSecret, secretLen - secret.length, 223 secret.length); 224 return newSecret; 225 } 226 } catch (PKCS11Exception e) { 227 throw new ProviderException("Could not derive key", e); 228 } finally { 229 publicValue = null; 230 token.releaseSession(session); 231 } 232 } 233 234 // see JCE spec 235 protected int engineGenerateSecret(byte[] sharedSecret, int 236 offset) throws IllegalStateException, ShortBufferException { 237 if (multiPartyAgreement != null) { 238 int n = multiPartyAgreement.generateSecret(sharedSecret, offset); 239 multiPartyAgreement = null; 240 return n; 241 } 242 if (offset + secretLen > sharedSecret.length) { 243 throw new ShortBufferException("Need " + secretLen 244 + " bytes, only " + (sharedSecret.length - offset) + " available"); 245 } 246 byte[] secret = engineGenerateSecret(); 247 System.arraycopy(secret, 0, sharedSecret, offset, secret.length); 248 return secret.length; 249 } 250 251 // see JCE spec 252 protected SecretKey engineGenerateSecret(String algorithm) 253 throws IllegalStateException, NoSuchAlgorithmException, 254 InvalidKeyException { 255 if (multiPartyAgreement != null) { 256 SecretKey key = multiPartyAgreement.generateSecret(algorithm); 257 multiPartyAgreement = null; 258 return key; 259 } 260 if (algorithm == null) { 261 throw new NoSuchAlgorithmException("Algorithm must not be null"); 262 } 263 if (algorithm.equals("TlsPremasterSecret")) { 264 // For now, only perform native derivation for TlsPremasterSecret 265 // as that is required for FIPS compliance. 266 // For other algorithms, there are unresolved issues regarding 267 // how this should work in JCE plus a Solaris truncation bug. 268 // (bug not yet filed). 269 return nativeGenerateSecret(algorithm); 270 } 271 byte[] secret = engineGenerateSecret(); 272 // Maintain compatibility for SunJCE: 273 // verify secret length is sensible for algorithm / truncate 274 // return generated key itself if possible 275 int keyLen; 276 if (algorithm.equalsIgnoreCase("DES")) { 277 keyLen = 8; 278 } else if (algorithm.equalsIgnoreCase("DESede")) { 279 keyLen = 24; 280 } else if (algorithm.equalsIgnoreCase("Blowfish")) { 281 keyLen = Math.min(56, secret.length); 282 } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { 283 keyLen = secret.length; 284 } else { 285 throw new NoSuchAlgorithmException 286 ("Unknown algorithm " + algorithm); 287 } 288 if (secret.length < keyLen) { 289 throw new InvalidKeyException("Secret too short"); 290 } 291 if (algorithm.equalsIgnoreCase("DES") || 292 algorithm.equalsIgnoreCase("DESede")) { 293 for (int i = 0; i < keyLen; i+=8) { 294 P11SecretKeyFactory.fixDESParity(secret, i); 295 } 296 } 297 return new SecretKeySpec(secret, 0, keyLen, algorithm); 298 } 299 300 private SecretKey nativeGenerateSecret(String algorithm) 301 throws IllegalStateException, NoSuchAlgorithmException, 302 InvalidKeyException { 303 if ((privateKey == null) || (publicValue == null)) { 304 throw new IllegalStateException("Not initialized correctly"); 305 } 306 long keyType = CKK_GENERIC_SECRET; 307 Session session = null; 308 try { 309 session = token.getObjSession(); 310 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { 311 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), 312 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), 313 }; 314 attributes = token.getAttributes 315 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); 316 long keyID = token.p11.C_DeriveKey(session.id(), 317 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, 318 attributes); 319 CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] { 320 new CK_ATTRIBUTE(CKA_VALUE_LEN), 321 }; 322 token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes); 323 int keyLen = (int)lenAttributes[0].getLong(); 324 SecretKey key = P11Key.secretKey 325 (session, keyID, algorithm, keyLen << 3, attributes); 326 if ("RAW".equals(key.getFormat())) { 327 // Workaround for Solaris bug 6318543. 328 // Strip leading zeroes ourselves if possible (key not sensitive). 329 // This should be removed once the Solaris fix is available 330 // as here we always retrieve the CKA_VALUE even for tokens 331 // that do not have that bug. 332 byte[] keyBytes = key.getEncoded(); 333 byte[] newBytes = KeyUtil.trimZeroes(keyBytes); 334 if (keyBytes != newBytes) { 335 key = new SecretKeySpec(newBytes, algorithm); 336 } 337 } 338 return key; 339 } catch (PKCS11Exception e) { 340 throw new InvalidKeyException("Could not derive key", e); 341 } finally { 342 publicValue = null; 343 token.releaseSession(session); 344 } 345 } 346 347 }