blob: af12bb35451186eac8ec459b0e39139be7e9faee [file] [log] [blame]
/*
* 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;
}
}
}