Home | History | Annotate | Download | only in jsse
      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 
     17 package org.apache.harmony.xnet.provider.jsse;
     18 
     19 import java.security.InvalidKeyException;
     20 import java.security.InvalidParameterException;
     21 import java.security.NoSuchAlgorithmException;
     22 import java.security.PrivateKey;
     23 import java.security.PublicKey;
     24 import java.security.Signature;
     25 import java.security.SignatureException;
     26 import java.security.interfaces.DSAPrivateKey;
     27 import java.security.interfaces.DSAPublicKey;
     28 import java.security.interfaces.RSAPrivateCrtKey;
     29 import java.security.interfaces.RSAPrivateKey;
     30 import java.security.interfaces.RSAPublicKey;
     31 
     32 /**
     33  * Implements the subset of the JDK Signature interface needed for
     34  * signature verification using OpenSSL.
     35  */
     36 public 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             throw new SignatureException(ex);
    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