OpenSSLSignature.java revision 52ec5bcc7d5d042d7ba6d0244d98ee72007a95e4
1/*
2 * Copyright (C) 2008 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.apache.harmony.xnet.provider.jsse;
18
19import java.security.InvalidKeyException;
20import java.security.InvalidParameterException;
21import java.security.NoSuchAlgorithmException;
22import java.security.PrivateKey;
23import java.security.PublicKey;
24import java.security.Signature;
25import java.security.SignatureException;
26import java.security.interfaces.DSAPrivateKey;
27import java.security.interfaces.DSAPublicKey;
28import java.security.interfaces.RSAPrivateCrtKey;
29import java.security.interfaces.RSAPrivateKey;
30import java.security.interfaces.RSAPublicKey;
31
32/**
33 * Implements the subset of the JDK Signature interface needed for
34 * signature verification using OpenSSL.
35 */
36public class OpenSSLSignature extends Signature {
37    private static enum EngineType {
38        RSA, DSA,
39    };
40
41    /**
42     * Holds a pointer to the native message digest context.
43     */
44    private int ctx;
45
46    /**
47     * The current OpenSSL key we're operating on.
48     */
49    private OpenSSLKey key;
50
51    /**
52     * Holds the type of the Java algorithm.
53     */
54    private final EngineType engineType;
55
56    /**
57     * Holds the OpenSSL name of the algorithm (lower case, no dashes).
58     */
59    private final String evpAlgorithm;
60
61    /**
62     * Holds a dummy buffer for writing single bytes to the digest.
63     */
64    private final byte[] singleByte = new byte[1];
65
66    /**
67     * Creates a new OpenSSLSignature instance for the given algorithm name.
68     *
69     * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1".
70     */
71    private OpenSSLSignature(String algorithm, EngineType engineType)
72            throws NoSuchAlgorithmException {
73        super(algorithm);
74
75        // We don't support MD2
76        if ("RSA-MD2".equals(algorithm)) {
77            throw new NoSuchAlgorithmException(algorithm);
78        }
79
80        this.engineType = engineType;
81        this.evpAlgorithm = algorithm;
82    }
83
84    @Override
85    protected void engineUpdate(byte input) {
86        singleByte[0] = input;
87        engineUpdate(singleByte, 0, 1);
88    }
89
90    @Override
91    protected void engineUpdate(byte[] input, int offset, int len) {
92        if (state == SIGN) {
93            if (ctx == 0) {
94                try {
95                    ctx = NativeCrypto.EVP_SignInit(evpAlgorithm);
96                } catch (Exception ex) {
97                    throw new RuntimeException(ex);
98                }
99            }
100
101            NativeCrypto.EVP_SignUpdate(ctx, input, offset, len);
102        } else {
103            if (ctx == 0) {
104                try {
105                    ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm);
106                } catch (Exception ex) {
107                    throw new RuntimeException(ex);
108                }
109            }
110
111            NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len);
112        }
113    }
114
115    @Override
116    protected Object engineGetParameter(String param) throws InvalidParameterException {
117        return null;
118    }
119
120    @Override
121    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
122        destroyContextIfExists();
123
124        if (privateKey instanceof OpenSSLDSAPrivateKey) {
125            if (engineType != EngineType.DSA) {
126                throw new InvalidKeyException("Signature not initialized as DSA");
127            }
128
129            OpenSSLDSAPrivateKey dsaPrivateKey = (OpenSSLDSAPrivateKey) privateKey;
130            key = dsaPrivateKey.getOpenSSLKey();
131        } else if (privateKey instanceof DSAPrivateKey) {
132            if (engineType != EngineType.DSA) {
133                throw new InvalidKeyException("Signature not initialized as DSA");
134            }
135
136            DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privateKey;
137            key = OpenSSLDSAPrivateKey.getInstance(dsaPrivateKey);
138        } else if (privateKey instanceof OpenSSLRSAPrivateKey) {
139            if (engineType != EngineType.RSA) {
140                throw new InvalidKeyException("Signature not initialized as RSA");
141            }
142
143            OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
144            key = rsaPrivateKey.getOpenSSLKey();
145        } else if (privateKey instanceof RSAPrivateCrtKey) {
146            if (engineType != EngineType.RSA) {
147                throw new InvalidKeyException("Signature not initialized as RSA");
148            }
149
150            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
151            key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
152        } else if (privateKey instanceof RSAPrivateKey) {
153            if (engineType != EngineType.RSA) {
154                throw new InvalidKeyException("Signature not initialized as RSA");
155            }
156
157            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
158            key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
159        } else {
160            throw new InvalidKeyException("Need DSA or RSA private key");
161        }
162    }
163
164    @Override
165    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
166        // If we had an existing context, destroy it first.
167        destroyContextIfExists();
168
169        if (publicKey instanceof OpenSSLDSAPublicKey) {
170            if (engineType != EngineType.DSA) {
171                throw new InvalidKeyException("Signature not initialized as DSA");
172            }
173
174            OpenSSLDSAPublicKey dsaPublicKey = (OpenSSLDSAPublicKey) publicKey;
175            key = dsaPublicKey.getOpenSSLKey();
176        } else if (publicKey instanceof DSAPublicKey) {
177            if (engineType != EngineType.DSA) {
178                throw new InvalidKeyException("Signature not initialized as DSA");
179            }
180
181            DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
182            key = OpenSSLDSAPublicKey.getInstance(dsaPublicKey);
183        } else if (publicKey instanceof OpenSSLRSAPublicKey) {
184            if (engineType != EngineType.RSA) {
185                throw new InvalidKeyException("Signature not initialized as RSA");
186            }
187
188            OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
189            key = rsaPublicKey.getOpenSSLKey();
190        } else if (publicKey instanceof RSAPublicKey) {
191            if (engineType != EngineType.RSA) {
192                throw new InvalidKeyException("Signature not initialized as RSA");
193            }
194
195            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
196            key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
197        } else {
198            throw new InvalidKeyException("Need DSA or RSA public key");
199        }
200    }
201
202    @Override
203    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
204    }
205
206    @Override
207    protected byte[] engineSign() throws SignatureException {
208        if (key == null) {
209            // This can't actually happen, but you never know...
210            throw new SignatureException("Need DSA or RSA private key");
211        }
212
213        try {
214            byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())];
215            int bytesWritten = NativeCrypto.EVP_SignFinal(ctx, buffer, 0, key.getPkeyContext());
216
217            byte[] signature = new byte[bytesWritten];
218            System.arraycopy(buffer, 0, signature, 0, bytesWritten);
219
220            return signature;
221        } catch (Exception ex) {
222            throw new SignatureException(ex);
223        } finally {
224            /*
225             * Java expects the digest context to be reset completely after sign
226             * calls.
227             */
228            destroyContextIfExists();
229        }
230    }
231
232    @Override
233    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
234        if (key == null) {
235            // This can't actually happen, but you never know...
236            throw new SignatureException("Need DSA or RSA public key");
237        }
238
239        try {
240            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length,
241                    key.getPkeyContext());
242            return result == 1;
243        } catch (Exception ex) {
244            return false;
245        } finally {
246            /*
247             * Java expects the digest context to be reset completely after
248             * verify calls.
249             */
250            destroyContextIfExists();
251        }
252    }
253
254    private void destroyContextIfExists() {
255        if (ctx != 0) {
256            NativeCrypto.EVP_MD_CTX_destroy(ctx);
257            ctx = 0;
258        }
259    }
260
261    @Override
262    protected void finalize() throws Throwable {
263        try {
264            if (ctx != 0) {
265                NativeCrypto.EVP_MD_CTX_destroy(ctx);
266            }
267        } finally {
268            super.finalize();
269        }
270    }
271
272    public static final class MD5RSA extends OpenSSLSignature {
273        public MD5RSA() throws NoSuchAlgorithmException {
274            super("RSA-MD5", EngineType.RSA);
275        }
276    }
277    public static final class SHA1RSA extends OpenSSLSignature {
278        public SHA1RSA() throws NoSuchAlgorithmException {
279            super("RSA-SHA1", EngineType.RSA);
280        }
281    }
282    public static final class SHA256RSA extends OpenSSLSignature {
283        public SHA256RSA() throws NoSuchAlgorithmException {
284            super("RSA-SHA256", EngineType.RSA);
285        }
286    }
287    public static final class SHA384RSA extends OpenSSLSignature {
288        public SHA384RSA() throws NoSuchAlgorithmException {
289            super("RSA-SHA384", EngineType.RSA);
290        }
291    }
292    public static final class SHA512RSA extends OpenSSLSignature {
293        public SHA512RSA() throws NoSuchAlgorithmException {
294            super("RSA-SHA512", EngineType.RSA);
295        }
296    }
297    public static final class SHA1DSA extends OpenSSLSignature {
298        public SHA1DSA() throws NoSuchAlgorithmException {
299            super("DSA-SHA1", EngineType.DSA);
300        }
301    }
302}
303
304