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