1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.io.IOException;
20import java.io.NotSerializableException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.security.InvalidKeyException;
24import java.util.Arrays;
25
26import javax.crypto.SecretKey;
27
28public class OpenSSLSecretKey implements SecretKey, OpenSSLKeyHolder {
29    private static final long serialVersionUID = 1831053062911514589L;
30
31    private final String algorithm;
32    private final int type;
33    private final byte[] encoded;
34
35    private transient OpenSSLKey key;
36
37    public OpenSSLSecretKey(String algorithm, byte[] encoded) {
38        this.algorithm = algorithm;
39        this.encoded = encoded;
40
41        type = NativeCrypto.EVP_PKEY_HMAC;
42        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_mac_key(type, encoded));
43    }
44
45    public OpenSSLSecretKey(String algorithm, OpenSSLKey key) {
46        this.algorithm = algorithm;
47        this.key = key;
48
49        type = NativeCrypto.EVP_PKEY_type(key.getPkeyContext());
50        encoded = null;
51    }
52
53    public static OpenSSLKey getInstance(SecretKey key) throws InvalidKeyException {
54        try {
55            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_mac_key(NativeCrypto.EVP_PKEY_HMAC,
56                    key.getEncoded()));
57        } catch (Exception e) {
58            throw new InvalidKeyException(e);
59        }
60    }
61
62    @Override
63    public String getAlgorithm() {
64        return algorithm;
65    }
66
67    @Override
68    public String getFormat() {
69        if (key.isEngineBased()) {
70            return null;
71        }
72
73        return "RAW";
74    }
75
76    @Override
77    public byte[] getEncoded() {
78        if (key.isEngineBased()) {
79            return null;
80        }
81
82        return encoded;
83    }
84
85    @Override
86    public OpenSSLKey getOpenSSLKey() {
87        return key;
88    }
89
90    @Override
91    public boolean equals(Object o) {
92        if (o == this) {
93            return true;
94        }
95
96        if (!(o instanceof SecretKey)) {
97            return false;
98        }
99
100        SecretKey other = (SecretKey) o;
101        if (!algorithm.equals(other.getAlgorithm())) {
102            return false;
103        }
104
105        if (o instanceof OpenSSLSecretKey) {
106            OpenSSLSecretKey otherOpenSSL = (OpenSSLSecretKey) o;
107            return key.equals(otherOpenSSL.getOpenSSLKey());
108        } else if (key.isEngineBased()) {
109            return false;
110        }
111
112        if (!getFormat().equals(other.getFormat())) {
113            return false;
114        }
115
116        return Arrays.equals(encoded, other.getEncoded());
117    }
118
119    @Override
120    public int hashCode() {
121        return key.hashCode();
122    }
123
124    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
125        stream.defaultReadObject();
126
127        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_mac_key(type, encoded));
128    }
129
130    private void writeObject(ObjectOutputStream stream) throws IOException {
131        if (getOpenSSLKey().isEngineBased()) {
132            throw new NotSerializableException("engine-based keys can not be serialized");
133        }
134
135        stream.defaultWriteObject();
136    }
137}
138