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.conscrypt; 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 org.conscrypt.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 algorithm the key algorithm used for the signature 70 */ 71 public DigitalSignature(String algorithm) { 72 try { 73 sha = MessageDigest.getInstance("SHA-1"); 74 75 if ("RSA".equals(algorithm)) { 76 md5 = MessageDigest.getInstance("MD5"); 77 cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 78 signature = null; 79 } else if ("DSA".equals(algorithm)) { 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 if (sha_hash == null) { 215 sha_hash = sha.digest(); 216 } 217 signature.update(sha_hash); 218 return signature.verify(data); 219 } catch (SignatureException e) { 220 return false; 221 } 222 } 223 224 if (cipher != null) { 225 final byte[] decrypt; 226 try { 227 decrypt = cipher.doFinal(data); 228 } catch (IllegalBlockSizeException e) { 229 return false; 230 } catch (BadPaddingException e) { 231 return false; 232 } 233 234 final byte[] md5_sha; 235 if (sha != null && sha_hash == null) { 236 sha_hash = sha.digest(); 237 } 238 if (md5 != null && md5_hash == null) { 239 md5_hash = md5.digest(); 240 } 241 if (md5_hash != null && sha_hash != null) { 242 md5_sha = new byte[md5_hash.length + sha_hash.length]; 243 System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length); 244 System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length); 245 } else if (md5_hash != null) { 246 md5_sha = md5_hash; 247 } else { 248 md5_sha = sha_hash; 249 } 250 251 return Arrays.equals(decrypt, md5_sha); 252 } else if (data == null || data.length == 0) { 253 return true; 254 } else { 255 return false; 256 } 257 } 258 259 } 260