1746a236e2be5dee62c482e27f4c682496d071d8bKenny Root/*
2746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * Copyright (C) 2012 The Android Open Source Project
3746a236e2be5dee62c482e27f4c682496d071d8bKenny Root *
4746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * you may not use this file except in compliance with the License.
6746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * You may obtain a copy of the License at
7746a236e2be5dee62c482e27f4c682496d071d8bKenny Root *
8746a236e2be5dee62c482e27f4c682496d071d8bKenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9746a236e2be5dee62c482e27f4c682496d071d8bKenny Root *
10746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * Unless required by applicable law or agreed to in writing, software
11746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * See the License for the specific language governing permissions and
14746a236e2be5dee62c482e27f4c682496d071d8bKenny Root * limitations under the License.
15746a236e2be5dee62c482e27f4c682496d071d8bKenny Root */
16746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
1738375a4d0b3d34e2babbd2f6a013976c7c439696Kenny Rootpackage org.conscrypt;
18746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
19d3df366d3fd59237f1fbf099e979e6843047032cKenny Rootimport java.io.IOException;
20d3df366d3fd59237f1fbf099e979e6843047032cKenny Rootimport java.io.NotSerializableException;
21d3df366d3fd59237f1fbf099e979e6843047032cKenny Rootimport java.io.ObjectInputStream;
22d3df366d3fd59237f1fbf099e979e6843047032cKenny Rootimport java.io.ObjectOutputStream;
23746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.math.BigInteger;
24746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.security.InvalidKeyException;
25746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.security.interfaces.DSAParams;
26746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.security.interfaces.DSAPublicKey;
27746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.security.spec.DSAPublicKeySpec;
28746a236e2be5dee62c482e27f4c682496d071d8bKenny Rootimport java.security.spec.InvalidKeySpecException;
29746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
300731d6d00c5e30c05e035d3ae96327029d07a606Kenny Rootpublic class OpenSSLDSAPublicKey implements DSAPublicKey, OpenSSLKeyHolder {
31d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    private static final long serialVersionUID = 5238609500353792232L;
32d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
33d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    private transient OpenSSLKey key;
34746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
35d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    private transient OpenSSLDSAParams params;
36746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
37746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    OpenSSLDSAPublicKey(OpenSSLKey key) {
38746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        this.key = key;
39746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
40746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
410731d6d00c5e30c05e035d3ae96327029d07a606Kenny Root    @Override
420731d6d00c5e30c05e035d3ae96327029d07a606Kenny Root    public OpenSSLKey getOpenSSLKey() {
4391bb5fbe55b854df891ff7720e30d42081dbcd58Kenny Root        return key;
4491bb5fbe55b854df891ff7720e30d42081dbcd58Kenny Root    }
4591bb5fbe55b854df891ff7720e30d42081dbcd58Kenny Root
46746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    OpenSSLDSAPublicKey(DSAPublicKeySpec dsaKeySpec) throws InvalidKeySpecException {
47746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        try {
48746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
49746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaKeySpec.getP().toByteArray(),
50746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaKeySpec.getQ().toByteArray(),
51746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaKeySpec.getG().toByteArray(),
52746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaKeySpec.getY().toByteArray(),
53746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    null));
54746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        } catch (Exception e) {
55746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            throw new InvalidKeySpecException(e);
56746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        }
57746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
58746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
59746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    private void ensureReadParams() {
60746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        if (params == null) {
61746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            params = new OpenSSLDSAParams(key);
62746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        }
63746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
64746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
65746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    static OpenSSLKey getInstance(DSAPublicKey dsaPublicKey) throws InvalidKeyException {
66746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        try {
67746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            final DSAParams dsaParams = dsaPublicKey.getParams();
68746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
69746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaParams.getP().toByteArray(),
70746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaParams.getQ().toByteArray(),
71746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaParams.getG().toByteArray(),
72746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    dsaPublicKey.getY().toByteArray(),
73746a236e2be5dee62c482e27f4c682496d071d8bKenny Root                    null));
74746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        } catch (Exception e) {
75746a236e2be5dee62c482e27f4c682496d071d8bKenny Root            throw new InvalidKeyException(e);
76746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        }
77746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
78746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
79746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    @Override
80746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    public DSAParams getParams() {
81746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        ensureReadParams();
82fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root
83fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root        /*
84fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root         * DSA keys can lack parameters if they're part of a certificate
85fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root         * chain. In this case, we just return null.
86fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root         */
87fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root        if (!params.hasParams()) {
88fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root            return null;
89fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root        }
90fc5480d13eb8b32c325ba79ba4221df2145727b7Kenny Root
91746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        return params;
92746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
93746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
94746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    @Override
95746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    public String getAlgorithm() {
96746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        return "DSA";
97746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
98746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
99746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    @Override
100746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    public String getFormat() {
101746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        return "X.509";
102746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
103746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
104746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    @Override
105746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    public byte[] getEncoded() {
106746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        return NativeCrypto.i2d_PUBKEY(key.getPkeyContext());
107746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
108746a236e2be5dee62c482e27f4c682496d071d8bKenny Root
109746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    @Override
110746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    public BigInteger getY() {
111746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        ensureReadParams();
112746a236e2be5dee62c482e27f4c682496d071d8bKenny Root        return params.getY();
113746a236e2be5dee62c482e27f4c682496d071d8bKenny Root    }
114d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
115d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    @Override
116d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    public boolean equals(Object o) {
117d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        if (o == this) {
118d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            return true;
119d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        }
120d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
121d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        if (o instanceof OpenSSLDSAPublicKey) {
122d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            OpenSSLDSAPublicKey other = (OpenSSLDSAPublicKey) o;
123d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
124d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            /*
125d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root             * We can shortcut the true case, but it still may be equivalent but
126d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root             * different copies.
127d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root             */
128d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            if (key.equals(other.getOpenSSLKey())) {
129d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root                return true;
130d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            }
131d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        }
132d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
133d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        if (!(o instanceof DSAPublicKey)) {
134d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root            return false;
135d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        }
136d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
137d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        ensureReadParams();
138d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
139d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        DSAPublicKey other = (DSAPublicKey) o;
140d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        return params.getY().equals(other.getY()) && params.equals(other.getParams());
141d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    }
142d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
143d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    @Override
144d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    public int hashCode() {
145d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        ensureReadParams();
146d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
147d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        return params.getY().hashCode() ^ params.hashCode();
148d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    }
149d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
150d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    @Override
151d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    public String toString() {
152d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        ensureReadParams();
153d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
154d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        final StringBuilder sb = new StringBuilder("OpenSSLDSAPublicKey{");
155d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append("Y=");
156d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append(params.getY().toString(16));
157d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append(',');
158d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append("params=");
159d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append(params.toString());
160d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        sb.append('}');
161d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root
162d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root        return sb.toString();
163d036721c2ecd146acef9f36408c7a397dd0a0785Kenny Root    }
164d3df366d3fd59237f1fbf099e979e6843047032cKenny Root
165d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
166d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.defaultReadObject();
167d3df366d3fd59237f1fbf099e979e6843047032cKenny Root
168d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        final BigInteger g = (BigInteger) stream.readObject();
169d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        final BigInteger p = (BigInteger) stream.readObject();
170d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        final BigInteger q = (BigInteger) stream.readObject();
171d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        final BigInteger y = (BigInteger) stream.readObject();
172d3df366d3fd59237f1fbf099e979e6843047032cKenny Root
173d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
174d3df366d3fd59237f1fbf099e979e6843047032cKenny Root                p.toByteArray(),
175d3df366d3fd59237f1fbf099e979e6843047032cKenny Root                q.toByteArray(),
176d3df366d3fd59237f1fbf099e979e6843047032cKenny Root                g.toByteArray(),
177d3df366d3fd59237f1fbf099e979e6843047032cKenny Root                y.toByteArray(),
178d3df366d3fd59237f1fbf099e979e6843047032cKenny Root                null));
179d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    }
180d3df366d3fd59237f1fbf099e979e6843047032cKenny Root
181d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    private void writeObject(ObjectOutputStream stream) throws IOException {
182d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        if (getOpenSSLKey().isEngineBased()) {
183d3df366d3fd59237f1fbf099e979e6843047032cKenny Root            throw new NotSerializableException("engine-based keys can not be serialized");
184d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        }
185d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.defaultWriteObject();
186d3df366d3fd59237f1fbf099e979e6843047032cKenny Root
187d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        ensureReadParams();
188d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.writeObject(params.getG());
189d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.writeObject(params.getP());
190d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.writeObject(params.getQ());
191d3df366d3fd59237f1fbf099e979e6843047032cKenny Root        stream.writeObject(params.getY());
192d3df366d3fd59237f1fbf099e979e6843047032cKenny Root    }
193746a236e2be5dee62c482e27f4c682496d071d8bKenny Root}
194