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.DSAParams;
27import java.security.interfaces.DSAPublicKey;
28import java.security.interfaces.RSAPublicKey;
29
30/**
31 * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
32 */
33public class OpenSSLSignature extends Signature {
34
35    /**
36     * Holds a pointer to the native message digest context.
37     */
38    private int ctx;
39
40    /**
41     * Holds a pointer to the native DSA key.
42     */
43    private int dsa;
44
45    /**
46     * Holds a pointer to the native RSA key.
47     */
48    private int rsa;
49
50    /**
51     * Holds the OpenSSL name of the algorithm (lower case, no dashes).
52     */
53    private String evpAlgorithm;
54
55    /**
56     * Holds a dummy buffer for writing single bytes to the digest.
57     */
58    private byte[] singleByte = new byte[1];
59
60    /**
61     * Creates a new OpenSSLSignature instance for the given algorithm name.
62     *
63     * @param algorithm The name of the algorithm, e.g. "SHA1".
64     *
65     * @return The new OpenSSLSignature instance.
66     *
67     * @throws RuntimeException In case of problems.
68     */
69    public static OpenSSLSignature getInstance(String algorithm) throws NoSuchAlgorithmException {
70        //log("OpenSSLSignature", "getInstance() invoked with " + algorithm);
71        return new OpenSSLSignature(algorithm);
72    }
73
74    /**
75     * Creates a new OpenSSLSignature instance for the given algorithm name.
76     *
77     * @param algorithm The name of the algorithm, e.g. "SHA1".
78     */
79    private OpenSSLSignature(String algorithm) throws NoSuchAlgorithmException {
80        super(algorithm);
81
82        int i = algorithm.indexOf("with");
83        if (i == -1) {
84            throw new NoSuchAlgorithmException(algorithm);
85        }
86
87        // We don't support MD2 anymore. This needs to also check for aliases
88        // and OIDs.
89        if ("MD2withRSA".equalsIgnoreCase(algorithm) ||
90                "MD2withRSAEncryption".equalsIgnoreCase(algorithm) ||
91                "1.2.840.113549.1.1.2".equalsIgnoreCase(algorithm) ||
92                "MD2/RSA".equalsIgnoreCase(algorithm)) {
93            throw new NoSuchAlgorithmException("MD2withRSA");
94        }
95
96        // For the special combination of DSA and SHA1, we need to pass the
97        // algorithm name as a pair consisting of crypto algorithm and hash
98        // algorithm. For all other (RSA) cases, passing the hash algorithm
99        // alone is not only sufficient, but actually necessary. OpenSSL
100        // doesn't accept something like RSA-SHA1.
101        if ("1.3.14.3.2.26with1.2.840.10040.4.1".equals(algorithm)
102                || "SHA1withDSA".equals(algorithm)
103                || "SHAwithDSA".equals(algorithm)) {
104            evpAlgorithm = "DSA-SHA";
105        } else {
106            evpAlgorithm = algorithm.substring(0, i).replace("-", "").toUpperCase();
107        }
108
109        ctx = NativeCrypto.EVP_new();
110    }
111
112    @Override
113    protected void engineUpdate(byte input) {
114        singleByte[0] = input;
115        engineUpdate(singleByte, 0, 1);
116    }
117
118    @Override
119    protected void engineUpdate(byte[] input, int offset, int len) {
120        if (state == SIGN) {
121            throw new UnsupportedOperationException();
122        } else {
123            NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len);
124        }
125    }
126
127    @Override
128    protected Object engineGetParameter(String param) throws InvalidParameterException {
129        return null;
130    }
131
132    @Override
133    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
134        throw new UnsupportedOperationException();
135    }
136
137    @Override
138    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
139        //log("OpenSSLSignature", "engineInitVerify() invoked with " + publicKey.getClass().getCanonicalName());
140
141        if (publicKey instanceof DSAPublicKey) {
142            try {
143                DSAPublicKey dsaPublicKey = (DSAPublicKey)publicKey;
144                DSAParams dsaParams = dsaPublicKey.getParams();
145                dsa = NativeCrypto.EVP_PKEY_new_DSA(dsaParams.getP().toByteArray(),
146                        dsaParams.getQ().toByteArray(), dsaParams.getG().toByteArray(),
147                        dsaPublicKey.getY().toByteArray(), null);
148
149            } catch (Exception ex) {
150                throw new InvalidKeyException(ex.toString());
151            }
152        } else if (publicKey instanceof RSAPublicKey) {
153            try {
154                RSAPublicKey rsaPublicKey = (RSAPublicKey)publicKey;
155                rsa = NativeCrypto.EVP_PKEY_new_RSA(rsaPublicKey.getModulus().toByteArray(),
156                        rsaPublicKey.getPublicExponent().toByteArray(), null, null, null);
157
158            } catch (Exception ex) {
159                throw new InvalidKeyException(ex.toString());
160            }
161        } else {
162            throw new InvalidKeyException("Need DSA or RSA public key");
163        }
164
165        try {
166            NativeCrypto.EVP_VerifyInit(ctx, evpAlgorithm);
167        } catch (Exception ex) {
168            throw new RuntimeException(ex);
169        }
170    }
171
172    @Override
173    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
174    }
175
176    @Override
177    protected byte[] engineSign() throws SignatureException {
178        throw new UnsupportedOperationException();
179    }
180
181    @Override
182    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
183        int handle = (rsa != 0) ? rsa : dsa;
184
185        if (handle == 0) {
186            // This can't actually happen, but you never know...
187            throw new SignatureException("Need DSA or RSA public key");
188        }
189
190        try {
191            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length, handle);
192            return result == 1;
193        } catch (Exception ex) {
194            throw new SignatureException(ex);
195        }
196
197    }
198
199    @Override
200    protected void finalize() throws Throwable {
201        super.finalize();
202
203        if (dsa != 0) {
204            NativeCrypto.EVP_PKEY_free(dsa);
205        }
206
207        if (rsa != 0) {
208            NativeCrypto.EVP_PKEY_free(rsa);
209        }
210
211        if (ctx != 0) {
212            NativeCrypto.EVP_free(ctx);
213        }
214    }
215}
216