| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.harmony.xnet.provider.jsse; |
| |
| import java.security.DigestException; |
| import java.security.InvalidKeyException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PrivateKey; |
| import java.security.Signature; |
| import java.security.SignatureException; |
| import java.security.cert.Certificate; |
| import java.util.Arrays; |
| import javax.crypto.BadPaddingException; |
| import javax.crypto.Cipher; |
| import javax.crypto.IllegalBlockSizeException; |
| import javax.crypto.NoSuchPaddingException; |
| import javax.net.ssl.SSLException; |
| import libcore.util.EmptyArray; |
| |
| /** |
| * This class represents Signature type, as described in TLS v 1.0 Protocol |
| * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm |
| * depends on SignatureAlgorithm. |
| * |
| * select (SignatureAlgorithm) |
| * { case anonymous: struct { }; |
| * case rsa: |
| * digitally-signed struct { |
| * opaque md5_hash[16]; |
| * opaque sha_hash[20]; |
| * }; |
| * case dsa: |
| * digitally-signed struct { |
| * opaque sha_hash[20]; |
| * }; |
| * } Signature; |
| * |
| * Digital signing description see in TLS spec., 4.7. |
| * (http://www.ietf.org/rfc/rfc2246.txt) |
| * |
| */ |
| public class DigitalSignature { |
| |
| private final MessageDigest md5; |
| private final MessageDigest sha; |
| private final Signature signature; |
| private final Cipher cipher; |
| |
| private byte[] md5_hash; |
| private byte[] sha_hash; |
| |
| /** |
| * Create Signature type |
| * @param keyExchange |
| */ |
| public DigitalSignature(String authType) { |
| try { |
| sha = MessageDigest.getInstance("SHA-1"); |
| |
| if ("RSA".equals(authType)) { |
| md5 = MessageDigest.getInstance("MD5"); |
| cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); |
| signature = null; |
| } else if ("DSA".equals(authType)) { |
| // SignatureAlgorithm is dsa |
| signature = Signature.getInstance("NONEwithDSA"); |
| cipher = null; |
| md5 = null; |
| } else { |
| cipher = null; |
| signature = null; |
| md5 = null; |
| } |
| } catch (NoSuchAlgorithmException e) { |
| // this should never happen |
| throw new AssertionError(e); |
| } catch (NoSuchPaddingException e) { |
| // this should never happen |
| throw new AssertionError(e); |
| } |
| } |
| |
| /** |
| * Initiate Signature type by private key |
| * @param key |
| */ |
| public void init(PrivateKey key) { |
| try { |
| if (signature != null) { |
| signature.initSign(key); |
| } else if (cipher != null) { |
| cipher.init(Cipher.ENCRYPT_MODE, key); |
| } |
| } catch (InvalidKeyException e){ |
| throw new AlertException(AlertProtocol.BAD_CERTIFICATE, |
| new SSLException("init - invalid private key", e)); |
| } |
| } |
| |
| /** |
| * Initiate Signature type by certificate |
| * @param cert |
| */ |
| public void init(Certificate cert) { |
| try { |
| if (signature != null) { |
| signature.initVerify(cert); |
| } else if (cipher != null) { |
| cipher.init(Cipher.DECRYPT_MODE, cert); |
| } |
| } catch (InvalidKeyException e){ |
| throw new AlertException(AlertProtocol.BAD_CERTIFICATE, |
| new SSLException("init - invalid certificate", e)); |
| } |
| } |
| |
| /** |
| * Update Signature hash |
| * @param data |
| */ |
| public void update(byte[] data) { |
| if (sha != null) { |
| sha.update(data); |
| } |
| if (md5 != null) { |
| md5.update(data); |
| } |
| } |
| |
| /** |
| * Sets MD5 hash |
| * @param data |
| */ |
| public void setMD5(byte[] data) { |
| md5_hash = data; |
| } |
| |
| /** |
| * Sets SHA hash |
| * @param data |
| */ |
| public void setSHA(byte[] data) { |
| sha_hash = data; |
| } |
| |
| /** |
| * Sign hash |
| * @return Signature bytes |
| */ |
| public byte[] sign() { |
| try { |
| if (md5 != null && md5_hash == null) { |
| md5_hash = new byte[16]; |
| md5.digest(md5_hash, 0, md5_hash.length); |
| } |
| if (md5_hash != null) { |
| if (signature != null) { |
| signature.update(md5_hash); |
| } else if (cipher != null) { |
| cipher.update(md5_hash); |
| } |
| } |
| if (sha != null && sha_hash == null) { |
| sha_hash = new byte[20]; |
| sha.digest(sha_hash, 0, sha_hash.length); |
| } |
| if (sha_hash != null) { |
| if (signature != null) { |
| signature.update(sha_hash); |
| } else if (cipher != null) { |
| cipher.update(sha_hash); |
| } |
| } |
| if (signature != null) { |
| return signature.sign(); |
| } else if (cipher != null) { |
| return cipher.doFinal(); |
| } |
| return EmptyArray.BYTE; |
| } catch (DigestException e){ |
| return EmptyArray.BYTE; |
| } catch (SignatureException e){ |
| return EmptyArray.BYTE; |
| } catch (BadPaddingException e){ |
| return EmptyArray.BYTE; |
| } catch (IllegalBlockSizeException e){ |
| return EmptyArray.BYTE; |
| } |
| } |
| |
| /** |
| * Verifies the signature data. |
| * @param data - the signature bytes |
| * @return true if verified |
| */ |
| public boolean verifySignature(byte[] data) { |
| if (signature != null) { |
| try { |
| signature.update(sha_hash); |
| return signature.verify(data); |
| } catch (SignatureException e) { |
| return false; |
| } |
| } |
| |
| if (cipher != null) { |
| final byte[] decrypt; |
| try { |
| decrypt = cipher.doFinal(data); |
| } catch (IllegalBlockSizeException e) { |
| return false; |
| } catch (BadPaddingException e) { |
| return false; |
| } |
| |
| final byte[] md5_sha; |
| if (md5_hash != null && sha_hash != null) { |
| md5_sha = new byte[md5_hash.length + sha_hash.length]; |
| System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length); |
| System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length); |
| } else if (md5_hash != null) { |
| md5_sha = md5_hash; |
| } else { |
| md5_sha = sha_hash; |
| } |
| |
| return Arrays.equals(decrypt, md5_sha); |
| } else if (data == null || data.length == 0) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| } |