P11ECDHKeyAgreement.java revision 16554:ccf1ccb7adf9
1/*
2 * Copyright (c) 2006, 2007, 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
26package sun.security.pkcs11;
27
28import java.security.*;
29import java.security.interfaces.ECPublicKey;
30import java.security.spec.AlgorithmParameterSpec;
31
32import javax.crypto.*;
33
34import static sun.security.pkcs11.TemplateManager.*;
35import sun.security.pkcs11.wrapper.*;
36import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
37
38/**
39 * KeyAgreement implementation for ECDH.
40 *
41 * @author  Andreas Sterbenz
42 * @since   1.6
43 */
44final class P11ECDHKeyAgreement extends KeyAgreementSpi {
45
46    // token instance
47    private final Token token;
48
49    // algorithm name
50    private final String algorithm;
51
52    // mechanism id
53    private final long mechanism;
54
55    // private key, if initialized
56    private P11Key privateKey;
57
58    // encoded public point, non-null between doPhase() and generateSecret() only
59    private byte[] publicValue;
60
61    // length of the secret to be derived
62    private int secretLen;
63
64    P11ECDHKeyAgreement(Token token, String algorithm, long mechanism) {
65        super();
66        this.token = token;
67        this.algorithm = algorithm;
68        this.mechanism = mechanism;
69    }
70
71    // see JCE spec
72    protected void engineInit(Key key, SecureRandom random)
73            throws InvalidKeyException {
74        if (key instanceof PrivateKey == false) {
75            throw new InvalidKeyException
76                        ("Key must be instance of PrivateKey");
77        }
78        privateKey = P11KeyFactory.convertKey(token, key, "EC");
79        publicValue = null;
80    }
81
82    // see JCE spec
83    protected void engineInit(Key key, AlgorithmParameterSpec params,
84            SecureRandom random) throws InvalidKeyException,
85            InvalidAlgorithmParameterException {
86        if (params != null) {
87            throw new InvalidAlgorithmParameterException
88                        ("Parameters not supported");
89        }
90        engineInit(key, random);
91    }
92
93    // see JCE spec
94    protected Key engineDoPhase(Key key, boolean lastPhase)
95            throws InvalidKeyException, IllegalStateException {
96        if (privateKey == null) {
97            throw new IllegalStateException("Not initialized");
98        }
99        if (publicValue != null) {
100            throw new IllegalStateException("Phase already executed");
101        }
102        if (lastPhase == false) {
103            throw new IllegalStateException
104                ("Only two party agreement supported, lastPhase must be true");
105        }
106        if (key instanceof ECPublicKey == false) {
107            throw new InvalidKeyException
108                ("Key must be a PublicKey with algorithm EC");
109        }
110        ECPublicKey ecKey = (ECPublicKey)key;
111        int keyLenBits = ecKey.getParams().getCurve().getField().getFieldSize();
112        secretLen = (keyLenBits + 7) >> 3;
113        publicValue = P11ECKeyFactory.getEncodedPublicValue(ecKey);
114        return null;
115    }
116
117    // see JCE spec
118    protected byte[] engineGenerateSecret() throws IllegalStateException {
119        if ((privateKey == null) || (publicValue == null)) {
120            throw new IllegalStateException("Not initialized correctly");
121        }
122        Session session = null;
123        try {
124            session = token.getOpSession();
125            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
126                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
127                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
128            };
129            CK_ECDH1_DERIVE_PARAMS ckParams =
130                    new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
131            attributes = token.getAttributes
132                (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
133            long keyID = token.p11.C_DeriveKey(session.id(),
134                new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
135                attributes);
136            attributes = new CK_ATTRIBUTE[] {
137                new CK_ATTRIBUTE(CKA_VALUE)
138            };
139            token.p11.C_GetAttributeValue(session.id(), keyID, attributes);
140            byte[] secret = attributes[0].getByteArray();
141            token.p11.C_DestroyObject(session.id(), keyID);
142            return secret;
143        } catch (PKCS11Exception e) {
144            throw new ProviderException("Could not derive key", e);
145        } finally {
146            publicValue = null;
147            token.releaseSession(session);
148        }
149    }
150
151    // see JCE spec
152    protected int engineGenerateSecret(byte[] sharedSecret, int
153            offset) throws IllegalStateException, ShortBufferException {
154        if (offset + secretLen > sharedSecret.length) {
155            throw new ShortBufferException("Need " + secretLen
156                + " bytes, only " + (sharedSecret.length - offset) + " available");
157        }
158        byte[] secret = engineGenerateSecret();
159        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
160        return secret.length;
161    }
162
163    // see JCE spec
164    protected SecretKey engineGenerateSecret(String algorithm)
165            throws IllegalStateException, NoSuchAlgorithmException,
166            InvalidKeyException {
167        if (algorithm == null) {
168            throw new NoSuchAlgorithmException("Algorithm must not be null");
169        }
170        if (algorithm.equals("TlsPremasterSecret") == false) {
171            throw new NoSuchAlgorithmException
172                ("Only supported for algorithm TlsPremasterSecret");
173        }
174        return nativeGenerateSecret(algorithm);
175    }
176
177    private SecretKey nativeGenerateSecret(String algorithm)
178            throws IllegalStateException, NoSuchAlgorithmException,
179            InvalidKeyException {
180        if ((privateKey == null) || (publicValue == null)) {
181            throw new IllegalStateException("Not initialized correctly");
182        }
183        long keyType = CKK_GENERIC_SECRET;
184        Session session = null;
185        try {
186            session = token.getObjSession();
187            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
188                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
189                new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
190            };
191            CK_ECDH1_DERIVE_PARAMS ckParams =
192                    new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
193            attributes = token.getAttributes
194                (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
195            long keyID = token.p11.C_DeriveKey(session.id(),
196                new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
197                attributes);
198            CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
199                new CK_ATTRIBUTE(CKA_VALUE_LEN),
200            };
201            token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes);
202            int keyLen = (int)lenAttributes[0].getLong();
203            SecretKey key = P11Key.secretKey
204                        (session, keyID, algorithm, keyLen << 3, attributes);
205            return key;
206        } catch (PKCS11Exception e) {
207            throw new InvalidKeyException("Could not derive key", e);
208        } finally {
209            publicValue = null;
210            token.releaseSession(session);
211        }
212    }
213
214}
215