Home | History | Annotate | Download | only in jsse
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 package org.apache.harmony.xnet.provider.jsse;
     18 
     19 import java.security.DigestException;
     20 import java.security.InvalidKeyException;
     21 import java.security.MessageDigest;
     22 import java.security.NoSuchAlgorithmException;
     23 import java.security.PrivateKey;
     24 import java.security.Signature;
     25 import java.security.SignatureException;
     26 import java.security.cert.Certificate;
     27 import java.util.Arrays;
     28 import javax.crypto.BadPaddingException;
     29 import javax.crypto.Cipher;
     30 import javax.crypto.IllegalBlockSizeException;
     31 import javax.crypto.NoSuchPaddingException;
     32 import javax.net.ssl.SSLException;
     33 import libcore.util.EmptyArray;
     34 
     35 /**
     36  * This class represents Signature type, as described in TLS v 1.0 Protocol
     37  * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm
     38  * depends on SignatureAlgorithm.
     39  *
     40  * select (SignatureAlgorithm)
     41  *       {   case anonymous: struct { };
     42  *           case rsa:
     43  *               digitally-signed struct {
     44  *                   opaque md5_hash[16];
     45  *                   opaque sha_hash[20];
     46  *               };
     47  *           case dsa:
     48  *               digitally-signed struct {
     49  *                   opaque sha_hash[20];
     50  *               };
     51  *       } Signature;
     52  *
     53  * Digital signing description see in TLS spec., 4.7.
     54  * (http://www.ietf.org/rfc/rfc2246.txt)
     55  *
     56  */
     57 public class DigitalSignature {
     58 
     59     private final MessageDigest md5;
     60     private final MessageDigest sha;
     61     private final Signature signature;
     62     private final Cipher cipher;
     63 
     64     private byte[] md5_hash;
     65     private byte[] sha_hash;
     66 
     67     /**
     68      * Create Signature type
     69      * @param keyExchange
     70      */
     71     public DigitalSignature(String authType) {
     72         try {
     73             sha = MessageDigest.getInstance("SHA-1");
     74 
     75             if ("RSA".equals(authType)) {
     76                 md5 = MessageDigest.getInstance("MD5");
     77                 cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
     78                 signature = null;
     79             } else if ("DSA".equals(authType)) {
     80                 // SignatureAlgorithm is dsa
     81                 signature = Signature.getInstance("NONEwithDSA");
     82                 cipher = null;
     83                 md5 = null;
     84             } else {
     85                 cipher = null;
     86                 signature = null;
     87                 md5 = null;
     88             }
     89         } catch (NoSuchAlgorithmException e) {
     90             // this should never happen
     91             throw new AssertionError(e);
     92         } catch (NoSuchPaddingException e) {
     93             // this should never happen
     94             throw new AssertionError(e);
     95         }
     96     }
     97 
     98     /**
     99      * Initiate Signature type by private key
    100      * @param key
    101      */
    102     public void init(PrivateKey key) {
    103         try {
    104             if (signature != null) {
    105                 signature.initSign(key);
    106             } else if (cipher != null) {
    107                 cipher.init(Cipher.ENCRYPT_MODE, key);
    108             }
    109         } catch (InvalidKeyException e){
    110             throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
    111                     new SSLException("init - invalid private key", e));
    112         }
    113     }
    114 
    115     /**
    116      * Initiate Signature type by certificate
    117      * @param cert
    118      */
    119     public void init(Certificate cert) {
    120         try {
    121             if (signature != null) {
    122                 signature.initVerify(cert);
    123             } else if (cipher != null) {
    124                 cipher.init(Cipher.DECRYPT_MODE, cert);
    125             }
    126         } catch (InvalidKeyException e){
    127             throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
    128                     new SSLException("init - invalid certificate", e));
    129         }
    130     }
    131 
    132     /**
    133      * Update Signature hash
    134      * @param data
    135      */
    136     public void update(byte[] data) {
    137         if (sha != null) {
    138             sha.update(data);
    139         }
    140         if (md5 != null) {
    141             md5.update(data);
    142         }
    143     }
    144 
    145     /**
    146      * Sets MD5 hash
    147      * @param data
    148      */
    149     public void setMD5(byte[] data) {
    150         md5_hash = data;
    151     }
    152 
    153     /**
    154      * Sets SHA hash
    155      * @param data
    156      */
    157     public void setSHA(byte[] data) {
    158         sha_hash = data;
    159     }
    160 
    161     /**
    162      * Sign hash
    163      * @return Signature bytes
    164      */
    165     public byte[] sign() {
    166         try {
    167             if (md5 != null && md5_hash == null) {
    168                 md5_hash = new byte[16];
    169                 md5.digest(md5_hash, 0, md5_hash.length);
    170             }
    171             if (md5_hash != null) {
    172                 if (signature != null) {
    173                     signature.update(md5_hash);
    174                 } else if (cipher != null) {
    175                     cipher.update(md5_hash);
    176                 }
    177             }
    178             if (sha != null && sha_hash == null) {
    179                 sha_hash = new byte[20];
    180                 sha.digest(sha_hash, 0, sha_hash.length);
    181             }
    182             if (sha_hash != null) {
    183                 if (signature != null) {
    184                     signature.update(sha_hash);
    185                 } else if (cipher != null) {
    186                     cipher.update(sha_hash);
    187                 }
    188             }
    189             if (signature != null) {
    190                 return signature.sign();
    191             } else if (cipher != null) {
    192                 return cipher.doFinal();
    193             }
    194             return EmptyArray.BYTE;
    195         } catch (DigestException e){
    196             return EmptyArray.BYTE;
    197         } catch (SignatureException e){
    198             return EmptyArray.BYTE;
    199         } catch (BadPaddingException e){
    200             return EmptyArray.BYTE;
    201         } catch (IllegalBlockSizeException e){
    202             return EmptyArray.BYTE;
    203         }
    204     }
    205 
    206     /**
    207      * Verifies the signature data.
    208      * @param data - the signature bytes
    209      * @return true if verified
    210      */
    211     public boolean verifySignature(byte[] data) {
    212         if (signature != null) {
    213             try {
    214                 signature.update(sha_hash);
    215                 return signature.verify(data);
    216             } catch (SignatureException e) {
    217                 return false;
    218             }
    219         }
    220 
    221         if (cipher != null) {
    222             final byte[] decrypt;
    223             try {
    224                 decrypt = cipher.doFinal(data);
    225             } catch (IllegalBlockSizeException e) {
    226                 return false;
    227             } catch (BadPaddingException e) {
    228                 return false;
    229             }
    230 
    231             final byte[] md5_sha;
    232             if (md5_hash != null && sha_hash != null) {
    233                 md5_sha = new byte[md5_hash.length + sha_hash.length];
    234                 System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length);
    235                 System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length);
    236             } else if (md5_hash != null) {
    237                 md5_sha = md5_hash;
    238             } else {
    239                 md5_sha = sha_hash;
    240             }
    241 
    242             return Arrays.equals(decrypt, md5_sha);
    243         } else if (data == null || data.length == 0) {
    244             return true;
    245         } else {
    246             return false;
    247         }
    248     }
    249 
    250 }
    251