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 
     34 /**
     35  * This class represents Signature type, as described in TLS v 1.0 Protocol
     36  * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm
     37  * depends on SignatureAlgorithm.
     38  *
     39  * select (SignatureAlgorithm)
     40  *       {   case anonymous: struct { };
     41  *           case rsa:
     42  *               digitally-signed struct {
     43  *                   opaque md5_hash[16];
     44  *                   opaque sha_hash[20];
     45  *               };
     46  *           case dsa:
     47  *               digitally-signed struct {
     48  *                   opaque sha_hash[20];
     49  *               };
     50  *       } Signature;
     51  *
     52  * Digital signing description see in TLS spec., 4.7.
     53  * (http://www.ietf.org/rfc/rfc2246.txt)
     54  *
     55  */
     56 public class DigitalSignature {
     57 
     58     private final MessageDigest md5;
     59     private final MessageDigest sha;
     60     private final Signature signature;
     61     private final Cipher cipher;
     62 
     63     private byte[] md5_hash;
     64     private byte[] sha_hash;
     65 
     66     /**
     67      * Create Signature type
     68      * @param keyExchange
     69      */
     70     public DigitalSignature(String authType) {
     71         try {
     72             sha = MessageDigest.getInstance("SHA-1");
     73 
     74             if ("RSA".equals(authType)) {
     75                 md5 = MessageDigest.getInstance("MD5");
     76                 cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
     77                 signature = null;
     78             } else if ("DSA".equals(authType)) {
     79                 // SignatureAlgorithm is dsa
     80                 signature = Signature.getInstance("NONEwithDSA");
     81                 cipher = null;
     82                 md5 = null;
     83             } else {
     84                 cipher = null;
     85                 signature = null;
     86                 md5 = null;
     87             }
     88         } catch (NoSuchAlgorithmException e) {
     89             // this should never happen
     90             throw new AssertionError(e);
     91         } catch (NoSuchPaddingException e) {
     92             // this should never happen
     93             throw new AssertionError(e);
     94         }
     95     }
     96 
     97     /**
     98      * Initiate Signature type by private key
     99      * @param key
    100      */
    101     public void init(PrivateKey key) {
    102         try {
    103             if (signature != null) {
    104                 signature.initSign(key);
    105             } else if (cipher != null) {
    106                 cipher.init(Cipher.ENCRYPT_MODE, key);
    107             }
    108         } catch (InvalidKeyException e){
    109             throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
    110                     new SSLException("init - invalid private key", e));
    111         }
    112     }
    113 
    114     /**
    115      * Initiate Signature type by certificate
    116      * @param cert
    117      */
    118     public void init(Certificate cert) {
    119         try {
    120             if (signature != null) {
    121                 signature.initVerify(cert);
    122             } else if (cipher != null) {
    123                 cipher.init(Cipher.DECRYPT_MODE, cert);
    124             }
    125         } catch (InvalidKeyException e){
    126             throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
    127                     new SSLException("init - invalid certificate", e));
    128         }
    129     }
    130 
    131     /**
    132      * Update Signature hash
    133      * @param data
    134      */
    135     public void update(byte[] data) {
    136         if (sha != null) {
    137             sha.update(data);
    138         }
    139         if (md5 != null) {
    140             md5.update(data);
    141         }
    142     }
    143 
    144     /**
    145      * Sets MD5 hash
    146      * @param data
    147      */
    148     public void setMD5(byte[] data) {
    149         md5_hash = data;
    150     }
    151 
    152     /**
    153      * Sets SHA hash
    154      * @param data
    155      */
    156     public void setSHA(byte[] data) {
    157         sha_hash = data;
    158     }
    159 
    160     /**
    161      * Sign hash
    162      * @return Signature bytes
    163      */
    164     public byte[] sign() {
    165         try {
    166             if (md5 != null && md5_hash == null) {
    167                 md5_hash = new byte[16];
    168                 md5.digest(md5_hash, 0, md5_hash.length);
    169             }
    170             if (md5_hash != null) {
    171                 if (signature != null) {
    172                     signature.update(md5_hash);
    173                 } else if (cipher != null) {
    174                     cipher.update(md5_hash);
    175                 }
    176             }
    177             if (sha != null && sha_hash == null) {
    178                 sha_hash = new byte[20];
    179                 sha.digest(sha_hash, 0, sha_hash.length);
    180             }
    181             if (sha_hash != null) {
    182                 if (signature != null) {
    183                     signature.update(sha_hash);
    184                 } else if (cipher != null) {
    185                     cipher.update(sha_hash);
    186                 }
    187             }
    188             if (signature != null) {
    189                 return signature.sign();
    190             } else if (cipher != null) {
    191                 return cipher.doFinal();
    192             }
    193             return new byte[0];
    194         } catch (DigestException e){
    195             return new byte[0];
    196         } catch (SignatureException e){
    197             return new byte[0];
    198         } catch (BadPaddingException e){
    199             return new byte[0];
    200         } catch (IllegalBlockSizeException e){
    201             return new byte[0];
    202         }
    203     }
    204 
    205     /**
    206      * Verifies the signature data.
    207      * @param data - the signature bytes
    208      * @return true if verified
    209      */
    210     public boolean verifySignature(byte[] data) {
    211         if (signature != null) {
    212             try {
    213                 signature.update(sha_hash);
    214                 return signature.verify(data);
    215             } catch (SignatureException e) {
    216                 return false;
    217             }
    218         }
    219 
    220         if (cipher != null) {
    221             final byte[] decrypt;
    222             try {
    223                 decrypt = cipher.doFinal(data);
    224             } catch (IllegalBlockSizeException e) {
    225                 return false;
    226             } catch (BadPaddingException e) {
    227                 return false;
    228             }
    229 
    230             final byte[] md5_sha;
    231             if (md5_hash != null && sha_hash != null) {
    232                 md5_sha = new byte[md5_hash.length + sha_hash.length];
    233                 System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length);
    234                 System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length);
    235             } else if (md5_hash != null) {
    236                 md5_sha = md5_hash;
    237             } else {
    238                 md5_sha = sha_hash;
    239             }
    240 
    241             return Arrays.equals(decrypt, md5_sha);
    242         } else if (data == null || data.length == 0) {
    243             return true;
    244         } else {
    245             return false;
    246         }
    247     }
    248 
    249 }
    250