blob: e98a8a680aaa7477211dab867642b499bdaf9729 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.pkcs11;
27
28import java.math.BigInteger;
29
30import java.security.*;
31import java.security.spec.*;
32
33import javax.crypto.*;
34import javax.crypto.interfaces.*;
35import javax.crypto.spec.*;
36
37import static sun.security.pkcs11.TemplateManager.*;
38import sun.security.pkcs11.wrapper.*;
39import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
40
41/**
42 * KeyAgreement implementation class. This class currently supports
43 * DH.
44 *
45 * @author Andreas Sterbenz
46 * @since 1.5
47 */
48final class P11KeyAgreement extends KeyAgreementSpi {
49
50 // token instance
51 private final Token token;
52
53 // algorithm name
54 private final String algorithm;
55
56 // mechanism id
57 private final long mechanism;
58
59 // private key, if initialized
60 private P11Key privateKey;
61
62 // other sides public value ("y"), if doPhase() already called
63 private BigInteger publicValue;
64
65 // length of the secret to be derived
66 private int secretLen;
67
68 // KeyAgreement from SunJCE as fallback for > 2 party agreement
69 private KeyAgreement multiPartyAgreement;
70
71 P11KeyAgreement(Token token, String algorithm, long mechanism) {
72 super();
73 this.token = token;
74 this.algorithm = algorithm;
75 this.mechanism = mechanism;
76 }
77
78 // see JCE spec
79 protected void engineInit(Key key, SecureRandom random)
80 throws InvalidKeyException {
81 if (key instanceof PrivateKey == false) {
82 throw new InvalidKeyException
83 ("Key must be instance of PrivateKey");
84 }
85 privateKey = P11KeyFactory.convertKey(token, key, algorithm);
86 publicValue = null;
87 multiPartyAgreement = null;
88 }
89
90 // see JCE spec
91 protected void engineInit(Key key, AlgorithmParameterSpec params,
92 SecureRandom random) throws InvalidKeyException,
93 InvalidAlgorithmParameterException {
94 if (params != null) {
95 throw new InvalidAlgorithmParameterException
96 ("Parameters not supported");
97 }
98 engineInit(key, random);
99 }
100
101 // see JCE spec
102 protected Key engineDoPhase(Key key, boolean lastPhase)
103 throws InvalidKeyException, IllegalStateException {
104 if (privateKey == null) {
105 throw new IllegalStateException("Not initialized");
106 }
107 if (publicValue != null) {
108 throw new IllegalStateException("Phase already executed");
109 }
110 // PKCS#11 only allows key agreement between 2 parties
111 // JCE allows >= 2 parties. To support that case (for compatibility
112 // and to pass JCK), fall back to SunJCE in this case.
113 // NOTE that we initialize using the P11Key, which will fail if it
114 // is sensitive/unextractable. However, this is not an issue in the
115 // compatibility configuration, which is all we are targeting here.
116 if ((multiPartyAgreement != null) || (lastPhase == false)) {
117 if (multiPartyAgreement == null) {
118 try {
119 multiPartyAgreement = KeyAgreement.getInstance
120 ("DH", P11Util.getSunJceProvider());
121 multiPartyAgreement.init(privateKey);
122 } catch (NoSuchAlgorithmException e) {
123 throw new InvalidKeyException
124 ("Could not initialize multi party agreement", e);
125 }
126 }
127 return multiPartyAgreement.doPhase(key, lastPhase);
128 }
129 if ((key instanceof PublicKey == false)
130 || (key.getAlgorithm().equals(algorithm) == false)) {
131 throw new InvalidKeyException
132 ("Key must be a PublicKey with algorithm DH");
133 }
134 BigInteger p, g, y;
135 if (key instanceof DHPublicKey) {
136 DHPublicKey dhKey = (DHPublicKey)key;
137 y = dhKey.getY();
138 DHParameterSpec params = dhKey.getParams();
139 p = params.getP();
140 g = params.getG();
141 } else {
142 // normally, DH PublicKeys will always implement DHPublicKey
143 // just in case not, attempt conversion
144 P11DHKeyFactory kf = new P11DHKeyFactory(token, "DH");
145 try {
146 DHPublicKeySpec spec = (DHPublicKeySpec)kf.engineGetKeySpec
147 (key, DHPublicKeySpec.class);
148 y = spec.getY();
149 p = spec.getP();
150 g = spec.getG();
151 } catch (InvalidKeySpecException e) {
152 throw new InvalidKeyException("Could not obtain key values", e);
153 }
154 }
155 // if parameters of private key are accessible, verify that
156 // they match parameters of public key
157 // XXX p and g should always be readable, even if the key is sensitive
158 if (privateKey instanceof DHPrivateKey) {
159 DHPrivateKey dhKey = (DHPrivateKey)privateKey;
160 DHParameterSpec params = dhKey.getParams();
161 if ((p.equals(params.getP()) == false)
162 || (g.equals(params.getG()) == false)) {
163 throw new InvalidKeyException
164 ("PublicKey DH parameters must match PrivateKey DH parameters");
165 }
166 }
167 publicValue = y;
168 // length of the secret is length of key
169 secretLen = (p.bitLength() + 7) >> 3;
170 return null;
171 }
172
173 // see JCE spec
174 protected byte[] engineGenerateSecret() throws IllegalStateException {
175 if (multiPartyAgreement != null) {
176 byte[] val = multiPartyAgreement.generateSecret();
177 multiPartyAgreement = null;
178 return val;
179 }
180 if ((privateKey == null) || (publicValue == null)) {
181 throw new IllegalStateException("Not initialized correctly");
182 }
183 Session session = null;
184 try {
185 session = token.getOpSession();
186 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
187 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
188 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
189 };
190 attributes = token.getAttributes
191 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
192 long keyID = token.p11.C_DeriveKey(session.id(),
193 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID,
194 attributes);
195 attributes = new CK_ATTRIBUTE[] {
196 new CK_ATTRIBUTE(CKA_VALUE)
197 };
198 token.p11.C_GetAttributeValue(session.id(), keyID, attributes);
199 byte[] secret = attributes[0].getByteArray();
200 token.p11.C_DestroyObject(session.id(), keyID);
201 // trim leading 0x00 bytes per JCE convention
202 return P11Util.trimZeroes(secret);
203 } catch (PKCS11Exception e) {
204 throw new ProviderException("Could not derive key", e);
205 } finally {
206 publicValue = null;
207 token.releaseSession(session);
208 }
209 }
210
211 // see JCE spec
212 protected int engineGenerateSecret(byte[] sharedSecret, int
213 offset) throws IllegalStateException, ShortBufferException {
214 if (multiPartyAgreement != null) {
215 int n = multiPartyAgreement.generateSecret(sharedSecret, offset);
216 multiPartyAgreement = null;
217 return n;
218 }
219 if (offset + secretLen > sharedSecret.length) {
220 throw new ShortBufferException("Need " + secretLen
221 + " bytes, only " + (sharedSecret.length - offset) + " available");
222 }
223 byte[] secret = engineGenerateSecret();
224 System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
225 return secret.length;
226 }
227
228 // see JCE spec
229 protected SecretKey engineGenerateSecret(String algorithm)
230 throws IllegalStateException, NoSuchAlgorithmException,
231 InvalidKeyException {
232 if (multiPartyAgreement != null) {
233 SecretKey key = multiPartyAgreement.generateSecret(algorithm);
234 multiPartyAgreement = null;
235 return key;
236 }
237 if (algorithm == null) {
238 throw new NoSuchAlgorithmException("Algorithm must not be null");
239 }
240 if (algorithm.equals("TlsPremasterSecret")) {
241 // For now, only perform native derivation for TlsPremasterSecret
242 // as that is required for FIPS compliance.
243 // For other algorithms, there are unresolved issues regarding
244 // how this should work in JCE plus a Solaris truncation bug.
245 // (bug not yet filed).
246 return nativeGenerateSecret(algorithm);
247 }
248 byte[] secret = engineGenerateSecret();
249 // Maintain compatibility for SunJCE:
250 // verify secret length is sensible for algorithm / truncate
251 // return generated key itself if possible
252 int keyLen;
253 if (algorithm.equalsIgnoreCase("DES")) {
254 keyLen = 8;
255 } else if (algorithm.equalsIgnoreCase("DESede")) {
256 keyLen = 24;
257 } else if (algorithm.equalsIgnoreCase("Blowfish")) {
258 keyLen = Math.min(56, secret.length);
259 } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) {
260 keyLen = secret.length;
261 } else {
262 throw new NoSuchAlgorithmException
263 ("Unknown algorithm " + algorithm);
264 }
265 if (secret.length < keyLen) {
266 throw new InvalidKeyException("Secret too short");
267 }
268 if (algorithm.equalsIgnoreCase("DES") ||
269 algorithm.equalsIgnoreCase("DESede")) {
270 for (int i = 0; i < keyLen; i+=8) {
271 P11SecretKeyFactory.fixDESParity(secret, i);
272 }
273 }
274 return new SecretKeySpec(secret, 0, keyLen, algorithm);
275 }
276
277 private SecretKey nativeGenerateSecret(String algorithm)
278 throws IllegalStateException, NoSuchAlgorithmException,
279 InvalidKeyException {
280 if ((privateKey == null) || (publicValue == null)) {
281 throw new IllegalStateException("Not initialized correctly");
282 }
283 long keyType = CKK_GENERIC_SECRET;
284 Session session = null;
285 try {
286 session = token.getObjSession();
287 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
288 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
289 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
290 };
291 attributes = token.getAttributes
292 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
293 long keyID = token.p11.C_DeriveKey(session.id(),
294 new CK_MECHANISM(mechanism, publicValue), privateKey.keyID,
295 attributes);
296 CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
297 new CK_ATTRIBUTE(CKA_VALUE_LEN),
298 };
299 token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes);
300 int keyLen = (int)lenAttributes[0].getLong();
301 SecretKey key = P11Key.secretKey
302 (session, keyID, algorithm, keyLen << 3, attributes);
303 if ("RAW".equals(key.getFormat())) {
304 // Workaround for Solaris bug 6318543.
305 // Strip leading zeroes ourselves if possible (key not sensitive).
306 // This should be removed once the Solaris fix is available
307 // as here we always retrieve the CKA_VALUE even for tokens
308 // that do not have that bug.
309 byte[] keyBytes = key.getEncoded();
310 byte[] newBytes = P11Util.trimZeroes(keyBytes);
311 if (keyBytes != newBytes) {
312 key = new SecretKeySpec(newBytes, algorithm);
313 }
314 }
315 return key;
316 } catch (PKCS11Exception e) {
317 throw new InvalidKeyException("Could not derive key", e);
318 } finally {
319 publicValue = null;
320 token.releaseSession(session);
321 }
322 }
323
324}