1/*
2 * Copyright (c) 2003, 2016, 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.io.*;
29import java.lang.ref.*;
30import java.math.BigInteger;
31import java.util.*;
32
33import java.security.*;
34import java.security.interfaces.*;
35import java.security.spec.*;
36
37import javax.crypto.*;
38import javax.crypto.interfaces.*;
39import javax.crypto.spec.*;
40
41import sun.security.rsa.RSAPublicKeyImpl;
42
43import sun.security.internal.interfaces.TlsMasterSecret;
44
45import sun.security.pkcs11.wrapper.*;
46import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
47
48import sun.security.util.Debug;
49import sun.security.util.DerValue;
50import sun.security.util.Length;
51import sun.security.util.ECUtil;
52
53/**
54 * Key implementation classes.
55 *
56 * In PKCS#11, the components of private and secret keys may or may not
57 * be accessible. If they are, we use the algorithm specific key classes
58 * (e.g. DSAPrivateKey) for compatibility with existing applications.
59 * If the components are not accessible, we use a generic class that
60 * only implements PrivateKey (or SecretKey). Whether the components of a
61 * key are extractable is automatically determined when the key object is
62 * created.
63 *
64 * @author  Andreas Sterbenz
65 * @since   1.5
66 */
67abstract class P11Key implements Key, Length {
68
69    private static final long serialVersionUID = -2575874101938349339L;
70
71    private final static String PUBLIC = "public";
72    private final static String PRIVATE = "private";
73    private final static String SECRET = "secret";
74
75    // type of key, one of (PUBLIC, PRIVATE, SECRET)
76    final String type;
77
78    // token instance
79    final Token token;
80
81    // algorithm name, returned by getAlgorithm(), etc.
82    final String algorithm;
83
84    // key id
85    final long keyID;
86
87    // effective key length of the key, e.g. 56 for a DES key
88    final int keyLength;
89
90    // flags indicating whether the key is a token object, sensitive, extractable
91    final boolean tokenObject, sensitive, extractable;
92
93    // phantom reference notification clean up for session keys
94    private final SessionKeyRef sessionKeyRef;
95
96    P11Key(String type, Session session, long keyID, String algorithm,
97            int keyLength, CK_ATTRIBUTE[] attributes) {
98        this.type = type;
99        this.token = session.token;
100        this.keyID = keyID;
101        this.algorithm = algorithm;
102        this.keyLength = keyLength;
103        boolean tokenObject = false;
104        boolean sensitive = false;
105        boolean extractable = true;
106        int n = (attributes == null) ? 0 : attributes.length;
107        for (int i = 0; i < n; i++) {
108            CK_ATTRIBUTE attr = attributes[i];
109            if (attr.type == CKA_TOKEN) {
110                tokenObject = attr.getBoolean();
111            } else if (attr.type == CKA_SENSITIVE) {
112                sensitive = attr.getBoolean();
113            } else if (attr.type == CKA_EXTRACTABLE) {
114                extractable = attr.getBoolean();
115            }
116        }
117        this.tokenObject = tokenObject;
118        this.sensitive = sensitive;
119        this.extractable = extractable;
120        if (tokenObject == false) {
121            sessionKeyRef = new SessionKeyRef(this, keyID, session);
122        } else {
123            sessionKeyRef = null;
124        }
125    }
126
127    // see JCA spec
128    public final String getAlgorithm() {
129        token.ensureValid();
130        return algorithm;
131    }
132
133    // see JCA spec
134    public final byte[] getEncoded() {
135        byte[] b = getEncodedInternal();
136        return (b == null) ? null : b.clone();
137    }
138
139    abstract byte[] getEncodedInternal();
140
141    public boolean equals(Object obj) {
142        if (this == obj) {
143            return true;
144        }
145        // equals() should never throw exceptions
146        if (token.isValid() == false) {
147            return false;
148        }
149        if (obj instanceof Key == false) {
150            return false;
151        }
152        String thisFormat = getFormat();
153        if (thisFormat == null) {
154            // no encoding, key only equal to itself
155            // XXX getEncoded() for unextractable keys will change that
156            return false;
157        }
158        Key other = (Key)obj;
159        if (thisFormat.equals(other.getFormat()) == false) {
160            return false;
161        }
162        byte[] thisEnc = this.getEncodedInternal();
163        byte[] otherEnc;
164        if (obj instanceof P11Key) {
165            otherEnc = ((P11Key)other).getEncodedInternal();
166        } else {
167            otherEnc = other.getEncoded();
168        }
169        return MessageDigest.isEqual(thisEnc, otherEnc);
170    }
171
172    public int hashCode() {
173        // hashCode() should never throw exceptions
174        if (token.isValid() == false) {
175            return 0;
176        }
177        byte[] b1 = getEncodedInternal();
178        if (b1 == null) {
179            return 0;
180        }
181        int r = b1.length;
182        for (int i = 0; i < b1.length; i++) {
183            r += (b1[i] & 0xff) * 37;
184        }
185        return r;
186    }
187
188    protected Object writeReplace() throws ObjectStreamException {
189        KeyRep.Type type;
190        String format = getFormat();
191        if (isPrivate() && "PKCS#8".equals(format)) {
192            type = KeyRep.Type.PRIVATE;
193        } else if (isPublic() && "X.509".equals(format)) {
194            type = KeyRep.Type.PUBLIC;
195        } else if (isSecret() && "RAW".equals(format)) {
196            type = KeyRep.Type.SECRET;
197        } else {
198            // XXX short term serialization for unextractable keys
199            throw new NotSerializableException
200                ("Cannot serialize sensitive and unextractable keys");
201        }
202        return new KeyRep(type, getAlgorithm(), format, getEncoded());
203    }
204
205    public String toString() {
206        token.ensureValid();
207        String s1 = token.provider.getName() + " " + algorithm + " " + type
208                + " key, " + keyLength + " bits";
209        s1 += " (id " + keyID + ", "
210                + (tokenObject ? "token" : "session") + " object";
211        if (isPublic()) {
212            s1 += ")";
213        } else {
214            s1 += ", " + (sensitive ? "" : "not ") + "sensitive";
215            s1 += ", " + (extractable ? "" : "un") + "extractable)";
216        }
217        return s1;
218    }
219
220    /**
221     * Return bit length of the key.
222     */
223    @Override
224    public int length() {
225        return keyLength;
226    }
227
228    boolean isPublic() {
229        return type == PUBLIC;
230    }
231
232    boolean isPrivate() {
233        return type == PRIVATE;
234    }
235
236    boolean isSecret() {
237        return type == SECRET;
238    }
239
240    void fetchAttributes(CK_ATTRIBUTE[] attributes) {
241        Session tempSession = null;
242        try {
243            tempSession = token.getOpSession();
244            token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes);
245        } catch (PKCS11Exception e) {
246            throw new ProviderException(e);
247        } finally {
248            token.releaseSession(tempSession);
249        }
250    }
251
252    private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0];
253
254    private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID,
255            CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) {
256        if (knownAttributes == null) {
257            knownAttributes = A0;
258        }
259        for (int i = 0; i < desiredAttributes.length; i++) {
260            // For each desired attribute, check to see if we have the value
261            // available already. If everything is here, we save a native call.
262            CK_ATTRIBUTE attr = desiredAttributes[i];
263            for (CK_ATTRIBUTE known : knownAttributes) {
264                if ((attr.type == known.type) && (known.pValue != null)) {
265                    attr.pValue = known.pValue;
266                    break; // break inner for loop
267                }
268            }
269            if (attr.pValue == null) {
270                // nothing found, need to call C_GetAttributeValue()
271                for (int j = 0; j < i; j++) {
272                    // clear values copied from knownAttributes
273                    desiredAttributes[j].pValue = null;
274                }
275                try {
276                    session.token.p11.C_GetAttributeValue
277                            (session.id(), keyID, desiredAttributes);
278                } catch (PKCS11Exception e) {
279                    throw new ProviderException(e);
280                }
281                break; // break loop, goto return
282            }
283        }
284        return desiredAttributes;
285    }
286
287    static SecretKey secretKey(Session session, long keyID, String algorithm,
288            int keyLength, CK_ATTRIBUTE[] attributes) {
289        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
290            new CK_ATTRIBUTE(CKA_TOKEN),
291            new CK_ATTRIBUTE(CKA_SENSITIVE),
292            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
293        });
294        return new P11SecretKey(session, keyID, algorithm, keyLength, attributes);
295    }
296
297    static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
298            int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
299        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
300            new CK_ATTRIBUTE(CKA_TOKEN),
301            new CK_ATTRIBUTE(CKA_SENSITIVE),
302            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
303        });
304        return new P11TlsMasterSecretKey
305                (session, keyID, algorithm, keyLength, attributes, major, minor);
306    }
307
308    // we assume that all components of public keys are always accessible
309    static PublicKey publicKey(Session session, long keyID, String algorithm,
310            int keyLength, CK_ATTRIBUTE[] attributes) {
311        switch (algorithm) {
312            case "RSA":
313                return new P11RSAPublicKey
314                    (session, keyID, algorithm, keyLength, attributes);
315            case "DSA":
316                return new P11DSAPublicKey
317                    (session, keyID, algorithm, keyLength, attributes);
318            case "DH":
319                return new P11DHPublicKey
320                    (session, keyID, algorithm, keyLength, attributes);
321            case "EC":
322                return new P11ECPublicKey
323                    (session, keyID, algorithm, keyLength, attributes);
324            default:
325                throw new ProviderException
326                    ("Unknown public key algorithm " + algorithm);
327        }
328    }
329
330    static PrivateKey privateKey(Session session, long keyID, String algorithm,
331            int keyLength, CK_ATTRIBUTE[] attributes) {
332        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
333            new CK_ATTRIBUTE(CKA_TOKEN),
334            new CK_ATTRIBUTE(CKA_SENSITIVE),
335            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
336        });
337        if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
338            return new P11PrivateKey
339                (session, keyID, algorithm, keyLength, attributes);
340        } else {
341            switch (algorithm) {
342                case "RSA":
343                    // In order to decide if this is RSA CRT key, we first query
344                    // and see if all extra CRT attributes are available.
345                    CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {
346                        new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
347                        new CK_ATTRIBUTE(CKA_PRIME_1),
348                        new CK_ATTRIBUTE(CKA_PRIME_2),
349                        new CK_ATTRIBUTE(CKA_EXPONENT_1),
350                        new CK_ATTRIBUTE(CKA_EXPONENT_2),
351                        new CK_ATTRIBUTE(CKA_COEFFICIENT),
352                    };
353                    boolean crtKey;
354                    try {
355                        session.token.p11.C_GetAttributeValue
356                            (session.id(), keyID, attrs2);
357                        crtKey = ((attrs2[0].pValue instanceof byte[]) &&
358                                  (attrs2[1].pValue instanceof byte[]) &&
359                                  (attrs2[2].pValue instanceof byte[]) &&
360                                  (attrs2[3].pValue instanceof byte[]) &&
361                                  (attrs2[4].pValue instanceof byte[]) &&
362                                  (attrs2[5].pValue instanceof byte[])) ;
363                    } catch (PKCS11Exception e) {
364                        // ignore, assume not available
365                        crtKey = false;
366                    }
367                    if (crtKey) {
368                        return new P11RSAPrivateKey
369                                (session, keyID, algorithm, keyLength, attributes, attrs2);
370                    } else {
371                        return new P11RSAPrivateNonCRTKey
372                                (session, keyID, algorithm, keyLength, attributes);
373                    }
374                case "DSA":
375                    return new P11DSAPrivateKey
376                            (session, keyID, algorithm, keyLength, attributes);
377                case "DH":
378                    return new P11DHPrivateKey
379                            (session, keyID, algorithm, keyLength, attributes);
380                case "EC":
381                    return new P11ECPrivateKey
382                            (session, keyID, algorithm, keyLength, attributes);
383                default:
384                    throw new ProviderException
385                            ("Unknown private key algorithm " + algorithm);
386            }
387        }
388    }
389
390    // class for sensitive and unextractable private keys
391    private static final class P11PrivateKey extends P11Key
392                                                implements PrivateKey {
393        private static final long serialVersionUID = -2138581185214187615L;
394
395        P11PrivateKey(Session session, long keyID, String algorithm,
396                int keyLength, CK_ATTRIBUTE[] attributes) {
397            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
398        }
399        // XXX temporary encoding for serialization purposes
400        public String getFormat() {
401            token.ensureValid();
402            return null;
403        }
404        byte[] getEncodedInternal() {
405            token.ensureValid();
406            return null;
407        }
408    }
409
410    private static class P11SecretKey extends P11Key implements SecretKey {
411        private static final long serialVersionUID = -7828241727014329084L;
412        private volatile byte[] encoded;
413        P11SecretKey(Session session, long keyID, String algorithm,
414                int keyLength, CK_ATTRIBUTE[] attributes) {
415            super(SECRET, session, keyID, algorithm, keyLength, attributes);
416        }
417        public String getFormat() {
418            token.ensureValid();
419            if (sensitive || (extractable == false)) {
420                return null;
421            } else {
422                return "RAW";
423            }
424        }
425        byte[] getEncodedInternal() {
426            token.ensureValid();
427            if (getFormat() == null) {
428                return null;
429            }
430            byte[] b = encoded;
431            if (b == null) {
432                synchronized (this) {
433                    b = encoded;
434                    if (b == null) {
435                        Session tempSession = null;
436                        try {
437                            tempSession = token.getOpSession();
438                            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
439                                new CK_ATTRIBUTE(CKA_VALUE),
440                            };
441                            token.p11.C_GetAttributeValue
442                                (tempSession.id(), keyID, attributes);
443                            b = attributes[0].getByteArray();
444                        } catch (PKCS11Exception e) {
445                            throw new ProviderException(e);
446                        } finally {
447                            token.releaseSession(tempSession);
448                        }
449                        encoded = b;
450                    }
451                }
452            }
453            return b;
454        }
455    }
456
457    @SuppressWarnings("deprecation")
458    private static class P11TlsMasterSecretKey extends P11SecretKey
459            implements TlsMasterSecret {
460        private static final long serialVersionUID = -1318560923770573441L;
461
462        private final int majorVersion, minorVersion;
463        P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
464                int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
465            super(session, keyID, algorithm, keyLength, attributes);
466            this.majorVersion = major;
467            this.minorVersion = minor;
468        }
469        public int getMajorVersion() {
470            return majorVersion;
471        }
472
473        public int getMinorVersion() {
474            return minorVersion;
475        }
476    }
477
478    // RSA CRT private key
479    private static final class P11RSAPrivateKey extends P11Key
480                implements RSAPrivateCrtKey {
481        private static final long serialVersionUID = 9215872438913515220L;
482
483        private BigInteger n, e, d, p, q, pe, qe, coeff;
484        private byte[] encoded;
485        P11RSAPrivateKey(Session session, long keyID, String algorithm,
486                int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs) {
487            super(PRIVATE, session, keyID, algorithm, keyLength, attrs);
488
489            for (CK_ATTRIBUTE a : crtAttrs) {
490                if (a.type == CKA_PUBLIC_EXPONENT) {
491                    e = a.getBigInteger();
492                } else if (a.type == CKA_PRIME_1) {
493                    p = a.getBigInteger();
494                } else if (a.type == CKA_PRIME_2) {
495                    q = a.getBigInteger();
496                } else if (a.type == CKA_EXPONENT_1) {
497                    pe = a.getBigInteger();
498                } else if (a.type == CKA_EXPONENT_2) {
499                    qe = a.getBigInteger();
500                } else if (a.type == CKA_COEFFICIENT) {
501                    coeff = a.getBigInteger();
502                }
503            }
504        }
505        private synchronized void fetchValues() {
506            token.ensureValid();
507            if (n != null) {
508                return;
509            }
510            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
511                new CK_ATTRIBUTE(CKA_MODULUS),
512                new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
513            };
514            fetchAttributes(attributes);
515            n = attributes[0].getBigInteger();
516            d = attributes[1].getBigInteger();
517        }
518
519        public String getFormat() {
520            token.ensureValid();
521            return "PKCS#8";
522        }
523        synchronized byte[] getEncodedInternal() {
524            token.ensureValid();
525            if (encoded == null) {
526                fetchValues();
527                try {
528                    // XXX make constructor in SunRsaSign provider public
529                    // and call it directly
530                    KeyFactory factory = KeyFactory.getInstance
531                        ("RSA", P11Util.getSunRsaSignProvider());
532                    Key newKey = factory.translateKey(this);
533                    encoded = newKey.getEncoded();
534                } catch (GeneralSecurityException e) {
535                    throw new ProviderException(e);
536                }
537            }
538            return encoded;
539        }
540        public BigInteger getModulus() {
541            fetchValues();
542            return n;
543        }
544        public BigInteger getPublicExponent() {
545            return e;
546        }
547        public BigInteger getPrivateExponent() {
548            fetchValues();
549            return d;
550        }
551        public BigInteger getPrimeP() {
552            return p;
553        }
554        public BigInteger getPrimeQ() {
555            return q;
556        }
557        public BigInteger getPrimeExponentP() {
558            return pe;
559        }
560        public BigInteger getPrimeExponentQ() {
561            return qe;
562        }
563        public BigInteger getCrtCoefficient() {
564            return coeff;
565        }
566    }
567
568    // RSA non-CRT private key
569    private static final class P11RSAPrivateNonCRTKey extends P11Key
570                implements RSAPrivateKey {
571        private static final long serialVersionUID = 1137764983777411481L;
572
573        private BigInteger n, d;
574        private byte[] encoded;
575        P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
576                int keyLength, CK_ATTRIBUTE[] attributes) {
577            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
578        }
579        private synchronized void fetchValues() {
580            token.ensureValid();
581            if (n != null) {
582                return;
583            }
584            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
585                new CK_ATTRIBUTE(CKA_MODULUS),
586                new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
587            };
588            fetchAttributes(attributes);
589            n = attributes[0].getBigInteger();
590            d = attributes[1].getBigInteger();
591        }
592        public String getFormat() {
593            token.ensureValid();
594            return "PKCS#8";
595        }
596        synchronized byte[] getEncodedInternal() {
597            token.ensureValid();
598            if (encoded == null) {
599                fetchValues();
600                try {
601                    // XXX make constructor in SunRsaSign provider public
602                    // and call it directly
603                    KeyFactory factory = KeyFactory.getInstance
604                        ("RSA", P11Util.getSunRsaSignProvider());
605                    Key newKey = factory.translateKey(this);
606                    encoded = newKey.getEncoded();
607                } catch (GeneralSecurityException e) {
608                    throw new ProviderException(e);
609                }
610            }
611            return encoded;
612        }
613        public BigInteger getModulus() {
614            fetchValues();
615            return n;
616        }
617        public BigInteger getPrivateExponent() {
618            fetchValues();
619            return d;
620        }
621    }
622
623    private static final class P11RSAPublicKey extends P11Key
624                                                implements RSAPublicKey {
625        private static final long serialVersionUID = -826726289023854455L;
626
627        private BigInteger n, e;
628        private byte[] encoded;
629        P11RSAPublicKey(Session session, long keyID, String algorithm,
630                int keyLength, CK_ATTRIBUTE[] attributes) {
631            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
632        }
633        private synchronized void fetchValues() {
634            token.ensureValid();
635            if (n != null) {
636                return;
637            }
638            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
639                new CK_ATTRIBUTE(CKA_MODULUS),
640                new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
641            };
642            fetchAttributes(attributes);
643            n = attributes[0].getBigInteger();
644            e = attributes[1].getBigInteger();
645        }
646        public String getFormat() {
647            token.ensureValid();
648            return "X.509";
649        }
650        synchronized byte[] getEncodedInternal() {
651            token.ensureValid();
652            if (encoded == null) {
653                fetchValues();
654                try {
655                    encoded = new RSAPublicKeyImpl(n, e).getEncoded();
656                } catch (InvalidKeyException e) {
657                    throw new ProviderException(e);
658                }
659            }
660            return encoded;
661        }
662        public BigInteger getModulus() {
663            fetchValues();
664            return n;
665        }
666        public BigInteger getPublicExponent() {
667            fetchValues();
668            return e;
669        }
670        public String toString() {
671            fetchValues();
672            return super.toString() +  "\n  modulus: " + n
673                + "\n  public exponent: " + e;
674        }
675    }
676
677    private static final class P11DSAPublicKey extends P11Key
678                                                implements DSAPublicKey {
679        private static final long serialVersionUID = 5989753793316396637L;
680
681        private BigInteger y;
682        private DSAParams params;
683        private byte[] encoded;
684        P11DSAPublicKey(Session session, long keyID, String algorithm,
685                int keyLength, CK_ATTRIBUTE[] attributes) {
686            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
687        }
688        private synchronized void fetchValues() {
689            token.ensureValid();
690            if (y != null) {
691                return;
692            }
693            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
694                new CK_ATTRIBUTE(CKA_VALUE),
695                new CK_ATTRIBUTE(CKA_PRIME),
696                new CK_ATTRIBUTE(CKA_SUBPRIME),
697                new CK_ATTRIBUTE(CKA_BASE),
698            };
699            fetchAttributes(attributes);
700            y = attributes[0].getBigInteger();
701            params = new DSAParameterSpec(
702                attributes[1].getBigInteger(),
703                attributes[2].getBigInteger(),
704                attributes[3].getBigInteger()
705            );
706        }
707        public String getFormat() {
708            token.ensureValid();
709            return "X.509";
710        }
711        synchronized byte[] getEncodedInternal() {
712            token.ensureValid();
713            if (encoded == null) {
714                fetchValues();
715                try {
716                    Key key = new sun.security.provider.DSAPublicKey
717                            (y, params.getP(), params.getQ(), params.getG());
718                    encoded = key.getEncoded();
719                } catch (InvalidKeyException e) {
720                    throw new ProviderException(e);
721                }
722            }
723            return encoded;
724        }
725        public BigInteger getY() {
726            fetchValues();
727            return y;
728        }
729        public DSAParams getParams() {
730            fetchValues();
731            return params;
732        }
733        public String toString() {
734            fetchValues();
735            return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
736                + "\n  q: " + params.getQ() + "\n  g: " + params.getG();
737        }
738    }
739
740    private static final class P11DSAPrivateKey extends P11Key
741                                                implements DSAPrivateKey {
742        private static final long serialVersionUID = 3119629997181999389L;
743
744        private BigInteger x;
745        private DSAParams params;
746        private byte[] encoded;
747        P11DSAPrivateKey(Session session, long keyID, String algorithm,
748                int keyLength, CK_ATTRIBUTE[] attributes) {
749            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
750        }
751        private synchronized void fetchValues() {
752            token.ensureValid();
753            if (x != null) {
754                return;
755            }
756            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
757                new CK_ATTRIBUTE(CKA_VALUE),
758                new CK_ATTRIBUTE(CKA_PRIME),
759                new CK_ATTRIBUTE(CKA_SUBPRIME),
760                new CK_ATTRIBUTE(CKA_BASE),
761            };
762            fetchAttributes(attributes);
763            x = attributes[0].getBigInteger();
764            params = new DSAParameterSpec(
765                attributes[1].getBigInteger(),
766                attributes[2].getBigInteger(),
767                attributes[3].getBigInteger()
768            );
769        }
770        public String getFormat() {
771            token.ensureValid();
772            return "PKCS#8";
773        }
774        synchronized byte[] getEncodedInternal() {
775            token.ensureValid();
776            if (encoded == null) {
777                fetchValues();
778                try {
779                    Key key = new sun.security.provider.DSAPrivateKey
780                            (x, params.getP(), params.getQ(), params.getG());
781                    encoded = key.getEncoded();
782                } catch (InvalidKeyException e) {
783                    throw new ProviderException(e);
784                }
785            }
786            return encoded;
787        }
788        public BigInteger getX() {
789            fetchValues();
790            return x;
791        }
792        public DSAParams getParams() {
793            fetchValues();
794            return params;
795        }
796    }
797
798    private static final class P11DHPrivateKey extends P11Key
799                                                implements DHPrivateKey {
800        private static final long serialVersionUID = -1698576167364928838L;
801
802        private BigInteger x;
803        private DHParameterSpec params;
804        private byte[] encoded;
805        P11DHPrivateKey(Session session, long keyID, String algorithm,
806                int keyLength, CK_ATTRIBUTE[] attributes) {
807            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
808        }
809        private synchronized void fetchValues() {
810            token.ensureValid();
811            if (x != null) {
812                return;
813            }
814            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
815                new CK_ATTRIBUTE(CKA_VALUE),
816                new CK_ATTRIBUTE(CKA_PRIME),
817                new CK_ATTRIBUTE(CKA_BASE),
818            };
819            fetchAttributes(attributes);
820            x = attributes[0].getBigInteger();
821            params = new DHParameterSpec(
822                attributes[1].getBigInteger(),
823                attributes[2].getBigInteger()
824            );
825        }
826        public String getFormat() {
827            token.ensureValid();
828            return "PKCS#8";
829        }
830        synchronized byte[] getEncodedInternal() {
831            token.ensureValid();
832            if (encoded == null) {
833                fetchValues();
834                try {
835                    DHPrivateKeySpec spec = new DHPrivateKeySpec
836                        (x, params.getP(), params.getG());
837                    KeyFactory kf = KeyFactory.getInstance
838                        ("DH", P11Util.getSunJceProvider());
839                    Key key = kf.generatePrivate(spec);
840                    encoded = key.getEncoded();
841                } catch (GeneralSecurityException e) {
842                    throw new ProviderException(e);
843                }
844            }
845            return encoded;
846        }
847        public BigInteger getX() {
848            fetchValues();
849            return x;
850        }
851        public DHParameterSpec getParams() {
852            fetchValues();
853            return params;
854        }
855        public int hashCode() {
856            if (token.isValid() == false) {
857                return 0;
858            }
859            fetchValues();
860            return Objects.hash(x, params.getP(), params.getG());
861        }
862        public boolean equals(Object obj) {
863            if (this == obj) return true;
864            // equals() should never throw exceptions
865            if (token.isValid() == false) {
866                return false;
867            }
868            if (!(obj instanceof DHPrivateKey)) {
869                return false;
870            }
871            fetchValues();
872            DHPrivateKey other = (DHPrivateKey) obj;
873            DHParameterSpec otherParams = other.getParams();
874            return ((this.x.compareTo(other.getX()) == 0) &&
875                    (this.params.getP().compareTo(otherParams.getP()) == 0) &&
876                    (this.params.getG().compareTo(otherParams.getG()) == 0));
877        }
878    }
879
880    private static final class P11DHPublicKey extends P11Key
881                                                implements DHPublicKey {
882        static final long serialVersionUID = -598383872153843657L;
883
884        private BigInteger y;
885        private DHParameterSpec params;
886        private byte[] encoded;
887        P11DHPublicKey(Session session, long keyID, String algorithm,
888                int keyLength, CK_ATTRIBUTE[] attributes) {
889            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
890        }
891        private synchronized void fetchValues() {
892            token.ensureValid();
893            if (y != null) {
894                return;
895            }
896            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
897                new CK_ATTRIBUTE(CKA_VALUE),
898                new CK_ATTRIBUTE(CKA_PRIME),
899                new CK_ATTRIBUTE(CKA_BASE),
900            };
901            fetchAttributes(attributes);
902            y = attributes[0].getBigInteger();
903            params = new DHParameterSpec(
904                attributes[1].getBigInteger(),
905                attributes[2].getBigInteger()
906            );
907        }
908        public String getFormat() {
909            token.ensureValid();
910            return "X.509";
911        }
912        synchronized byte[] getEncodedInternal() {
913            token.ensureValid();
914            if (encoded == null) {
915                fetchValues();
916                try {
917                    DHPublicKeySpec spec = new DHPublicKeySpec
918                        (y, params.getP(), params.getG());
919                    KeyFactory kf = KeyFactory.getInstance
920                        ("DH", P11Util.getSunJceProvider());
921                    Key key = kf.generatePublic(spec);
922                    encoded = key.getEncoded();
923                } catch (GeneralSecurityException e) {
924                    throw new ProviderException(e);
925                }
926            }
927            return encoded;
928        }
929        public BigInteger getY() {
930            fetchValues();
931            return y;
932        }
933        public DHParameterSpec getParams() {
934            fetchValues();
935            return params;
936        }
937        public String toString() {
938            fetchValues();
939            return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
940                + "\n  g: " + params.getG();
941        }
942        public int hashCode() {
943            if (token.isValid() == false) {
944                return 0;
945            }
946            fetchValues();
947            return Objects.hash(y, params.getP(), params.getG());
948        }
949        public boolean equals(Object obj) {
950            if (this == obj) return true;
951            // equals() should never throw exceptions
952            if (token.isValid() == false) {
953                return false;
954            }
955            if (!(obj instanceof DHPublicKey)) {
956                return false;
957            }
958            fetchValues();
959            DHPublicKey other = (DHPublicKey) obj;
960            DHParameterSpec otherParams = other.getParams();
961            return ((this.y.compareTo(other.getY()) == 0) &&
962                    (this.params.getP().compareTo(otherParams.getP()) == 0) &&
963                    (this.params.getG().compareTo(otherParams.getG()) == 0));
964        }
965    }
966
967    private static final class P11ECPrivateKey extends P11Key
968                                                implements ECPrivateKey {
969        private static final long serialVersionUID = -7786054399510515515L;
970
971        private BigInteger s;
972        private ECParameterSpec params;
973        private byte[] encoded;
974        P11ECPrivateKey(Session session, long keyID, String algorithm,
975                int keyLength, CK_ATTRIBUTE[] attributes) {
976            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
977        }
978        private synchronized void fetchValues() {
979            token.ensureValid();
980            if (s != null) {
981                return;
982            }
983            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
984                new CK_ATTRIBUTE(CKA_VALUE),
985                new CK_ATTRIBUTE(CKA_EC_PARAMS, params),
986            };
987            fetchAttributes(attributes);
988            s = attributes[0].getBigInteger();
989            try {
990                params = P11ECKeyFactory.decodeParameters
991                            (attributes[1].getByteArray());
992            } catch (Exception e) {
993                throw new RuntimeException("Could not parse key values", e);
994            }
995        }
996        public String getFormat() {
997            token.ensureValid();
998            return "PKCS#8";
999        }
1000        synchronized byte[] getEncodedInternal() {
1001            token.ensureValid();
1002            if (encoded == null) {
1003                fetchValues();
1004                try {
1005                    Key key = ECUtil.generateECPrivateKey(s, params);
1006                    encoded = key.getEncoded();
1007                } catch (InvalidKeySpecException e) {
1008                    throw new ProviderException(e);
1009                }
1010            }
1011            return encoded;
1012        }
1013        public BigInteger getS() {
1014            fetchValues();
1015            return s;
1016        }
1017        public ECParameterSpec getParams() {
1018            fetchValues();
1019            return params;
1020        }
1021    }
1022
1023    private static final class P11ECPublicKey extends P11Key
1024                                                implements ECPublicKey {
1025        private static final long serialVersionUID = -6371481375154806089L;
1026
1027        private ECPoint w;
1028        private ECParameterSpec params;
1029        private byte[] encoded;
1030        P11ECPublicKey(Session session, long keyID, String algorithm,
1031                int keyLength, CK_ATTRIBUTE[] attributes) {
1032            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
1033        }
1034        private synchronized void fetchValues() {
1035            token.ensureValid();
1036            if (w != null) {
1037                return;
1038            }
1039            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
1040                new CK_ATTRIBUTE(CKA_EC_POINT),
1041                new CK_ATTRIBUTE(CKA_EC_PARAMS),
1042            };
1043            fetchAttributes(attributes);
1044
1045            try {
1046                params = P11ECKeyFactory.decodeParameters
1047                            (attributes[1].getByteArray());
1048                byte[] ecKey = attributes[0].getByteArray();
1049
1050                // Check whether the X9.63 encoding of an EC point is wrapped
1051                // in an ASN.1 OCTET STRING
1052                if (!token.config.getUseEcX963Encoding()) {
1053                    DerValue wECPoint = new DerValue(ecKey);
1054
1055                    if (wECPoint.getTag() != DerValue.tag_OctetString) {
1056                        throw new IOException("Could not DER decode EC point." +
1057                            " Unexpected tag: " + wECPoint.getTag());
1058                    }
1059                    w = P11ECKeyFactory.decodePoint
1060                        (wECPoint.getDataBytes(), params.getCurve());
1061
1062                } else {
1063                    w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());
1064                }
1065
1066            } catch (Exception e) {
1067                throw new RuntimeException("Could not parse key values", e);
1068            }
1069        }
1070        public String getFormat() {
1071            token.ensureValid();
1072            return "X.509";
1073        }
1074        synchronized byte[] getEncodedInternal() {
1075            token.ensureValid();
1076            if (encoded == null) {
1077                fetchValues();
1078                try {
1079                    return ECUtil.x509EncodeECPublicKey(w, params);
1080                } catch (InvalidKeySpecException e) {
1081                    throw new ProviderException(e);
1082                }
1083            }
1084            return encoded;
1085        }
1086        public ECPoint getW() {
1087            fetchValues();
1088            return w;
1089        }
1090        public ECParameterSpec getParams() {
1091            fetchValues();
1092            return params;
1093        }
1094        public String toString() {
1095            fetchValues();
1096            return super.toString()
1097                + "\n  public x coord: " + w.getAffineX()
1098                + "\n  public y coord: " + w.getAffineY()
1099                + "\n  parameters: " + params;
1100        }
1101    }
1102}
1103
1104/*
1105 * NOTE: Must use PhantomReference here and not WeakReference
1106 * otherwise the key maybe cleared before other objects which
1107 * still use these keys during finalization such as SSLSocket.
1108 */
1109final class SessionKeyRef extends PhantomReference<P11Key>
1110    implements Comparable<SessionKeyRef> {
1111    private static ReferenceQueue<P11Key> refQueue =
1112        new ReferenceQueue<P11Key>();
1113    private static Set<SessionKeyRef> refList =
1114        Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
1115
1116    static ReferenceQueue<P11Key> referenceQueue() {
1117        return refQueue;
1118    }
1119
1120    private static void drainRefQueueBounded() {
1121        Session sess = null;
1122        Token tkn = null;
1123        while (true) {
1124            SessionKeyRef next = (SessionKeyRef) refQueue.poll();
1125            if (next == null) {
1126                break;
1127            }
1128
1129            // If the token is still valid, try to remove the object
1130            if (next.session.token.isValid()) {
1131                // If this key's token is the same as the previous key, the
1132                // same session can be used for C_DestroyObject.
1133                try {
1134                    if (next.session.token != tkn || sess == null) {
1135                        // Release session if not using previous token
1136                        if (tkn != null && sess != null) {
1137                            tkn.releaseSession(sess);
1138                            sess = null;
1139                        }
1140
1141                        tkn = next.session.token;
1142                        sess = tkn.getOpSession();
1143                    }
1144                    next.disposeNative(sess);
1145                } catch (PKCS11Exception e) {
1146                    // ignore
1147                }
1148            }
1149            // Regardless of native results, dispose of java references
1150            next.dispose();
1151        }
1152
1153        if (tkn != null && sess != null) {
1154            tkn.releaseSession(sess);
1155        }
1156    }
1157
1158    // handle to the native key
1159    private long keyID;
1160    private Session session;
1161
1162    SessionKeyRef(P11Key key , long keyID, Session session) {
1163        super(key, refQueue);
1164        this.keyID = keyID;
1165        this.session = session;
1166        this.session.addObject();
1167        refList.add(this);
1168        drainRefQueueBounded();
1169    }
1170
1171    private void disposeNative(Session s) throws PKCS11Exception {
1172        session.token.p11.C_DestroyObject(s.id(), keyID);
1173    }
1174
1175    private void dispose() {
1176        refList.remove(this);
1177        this.clear();
1178        session.removeObject();
1179    }
1180
1181    public int compareTo(SessionKeyRef other) {
1182        if (this.keyID == other.keyID) {
1183            return 0;
1184        } else {
1185            return (this.keyID < other.keyID) ? -1 : 1;
1186        }
1187    }
1188}
1189