1/*
2 * Copyright (c) 1997, 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
26package com.sun.crypto.provider;
27
28import java.security.MessageDigest;
29import java.security.KeyRep;
30import java.security.spec.InvalidKeySpecException;
31import java.util.Locale;
32import javax.crypto.SecretKey;
33import javax.crypto.spec.PBEKeySpec;
34
35import jdk.internal.ref.CleanerFactory;
36
37/**
38 * This class represents a PBE key.
39 *
40 * @author Jan Luehe
41 *
42 */
43final class PBEKey implements SecretKey {
44
45    static final long serialVersionUID = -2234768909660948176L;
46
47    private byte[] key;
48
49    private String type;
50
51    /**
52     * Creates a PBE key from a given PBE key specification.
53     *
54     * @param keytype the given PBE key specification
55     */
56    PBEKey(PBEKeySpec keySpec, String keytype) throws InvalidKeySpecException {
57        char[] passwd = keySpec.getPassword();
58        if (passwd == null) {
59            // Should allow an empty password.
60            passwd = new char[0];
61        }
62        // Accept "\0" to signify "zero-length password with no terminator".
63        if (!(passwd.length == 1 && passwd[0] == 0)) {
64            for (int i=0; i<passwd.length; i++) {
65                if ((passwd[i] < '\u0020') || (passwd[i] > '\u007E')) {
66                    throw new InvalidKeySpecException("Password is not ASCII");
67                }
68            }
69        }
70        this.key = new byte[passwd.length];
71        for (int i=0; i<passwd.length; i++)
72            this.key[i] = (byte) (passwd[i] & 0x7f);
73        java.util.Arrays.fill(passwd, ' ');
74        type = keytype;
75
76        // Use the cleaner to zero the key when no longer referenced
77        final byte[] k = this.key;
78        CleanerFactory.cleaner().register(this,
79                () -> java.util.Arrays.fill(k, (byte)0x00));
80    }
81
82    public byte[] getEncoded() {
83        return this.key.clone();
84    }
85
86    public String getAlgorithm() {
87        return type;
88    }
89
90    public String getFormat() {
91        return "RAW";
92    }
93
94    /**
95     * Calculates a hash code value for the object.
96     * Objects that are equal will also have the same hashcode.
97     */
98    public int hashCode() {
99        int retval = 0;
100        for (int i = 1; i < this.key.length; i++) {
101            retval += this.key[i] * i;
102        }
103        return(retval ^= getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode());
104    }
105
106    public boolean equals(Object obj) {
107        if (obj == this)
108            return true;
109
110        if (!(obj instanceof SecretKey))
111            return false;
112
113        SecretKey that = (SecretKey)obj;
114
115        if (!(that.getAlgorithm().equalsIgnoreCase(type)))
116            return false;
117
118        byte[] thatEncoded = that.getEncoded();
119        boolean ret = MessageDigest.isEqual(this.key, thatEncoded);
120        java.util.Arrays.fill(thatEncoded, (byte)0x00);
121        return ret;
122    }
123
124    /**
125     * readObject is called to restore the state of this key from
126     * a stream.
127     */
128    private void readObject(java.io.ObjectInputStream s)
129         throws java.io.IOException, ClassNotFoundException
130    {
131        s.defaultReadObject();
132        key = key.clone();
133    }
134
135
136    /**
137     * Replace the PBE key to be serialized.
138     *
139     * @return the standard KeyRep object to be serialized
140     *
141     * @throws java.io.ObjectStreamException if a new object representing
142     * this PBE key could not be created
143     */
144    private Object writeReplace() throws java.io.ObjectStreamException {
145        return new KeyRep(KeyRep.Type.SECRET,
146                        getAlgorithm(),
147                        getFormat(),
148                        getEncoded());
149    }
150}
151