/*
* 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.
*/
/**
* @author Boris V. Kuznetsov
* @version $Revision$
*/
package java.security;
import java.nio.ByteBuffer;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Iterator;
import java.util.Set;
import org.apache.harmony.security.fortress.Engine;
import org.apache.harmony.security.internal.nls.Messages;
/**
* {@code Signature} is an engine class which is capable of creating and
* verifying digital signatures, using different algorithms that have been
* registered with the {@link Security} class.
*
* @see SignatureSpi
* @since Android 1.0
*/
public abstract class Signature extends SignatureSpi {
// The service name.
private static final String SERVICE = "Signature"; //$NON-NLS-1$
// Used to access common engine functionality
private static Engine engine = new Engine(SERVICE);
// The provider
private Provider provider;
// The algorithm.
private String algorithm;
/**
* Constant that indicates that this {@code Signature} instance has not yet
* been initialized.
*
* @since Android 1.0
*/
protected static final int UNINITIALIZED = 0;
/**
* Constant that indicates that this {@code Signature} instance has been
* initialized for signing.
*
* @since Android 1.0
*/
protected static final int SIGN = 2;
/**
* Constant that indicates that this {@code Signature} instance has been
* initialized for verification.
*
* @since Android 1.0
*/
protected static final int VERIFY = 3;
/**
* Represents the current state of this {@code Signature}. The three
* possible states are {@link #UNINITIALIZED}, {@link #SIGN} or
* {@link #VERIFY}.
*
* @since Android 1.0
*/
protected int state = UNINITIALIZED;
/**
* Constructs a new instance of {@code Signature} with the name of
* the algorithm to use.
*
* @param algorithm
* the name of algorithm to use.
* @since Android 1.0
*/
protected Signature(String algorithm) {
this.algorithm = algorithm;
}
/**
* Returns a new instance of {@code Signature} that utilizes the specified
* algorithm.
*
* @param algorithm
* the name of the algorithm to use.
* @return a new instance of {@code Signature} that utilizes the specified
* algorithm.
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available.
* @throws NullPointerException
* if {@code algorithm} is {@code null}.
* @since Android 1.0
*/
public static Signature getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
throw new NullPointerException(Messages.getString("security.01")); //$NON-NLS-1$
}
Signature result;
synchronized (engine) {
engine.getInstance(algorithm, null);
if (engine.spi instanceof Signature) {
result = (Signature) engine.spi;
result.algorithm = algorithm;
result.provider = engine.provider;
} else {
result = new SignatureImpl((SignatureSpi) engine.spi,
engine.provider, algorithm);
}
}
return result;
}
/**
* Returns a new instance of {@code Signature} that utilizes the specified
* algorithm from the specified provider.
*
* @param algorithm
* the name of the algorithm to use.
* @param provider
* the name of the provider.
* @return a new instance of {@code Signature} that utilizes the specified
* algorithm from the specified provider.
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available.
* @throws NoSuchProviderException
* if the specified provider is not available.
* @throws NullPointerException
* if {@code algorithm} is {@code null}.
* @since Android 1.0
*/
public static Signature getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (algorithm == null) {
throw new NullPointerException(Messages.getString("security.01")); //$NON-NLS-1$
}
if ((provider == null) || (provider.length() == 0)) {
throw new IllegalArgumentException(
Messages.getString("security.02")); //$NON-NLS-1$
}
Provider p = Security.getProvider(provider);
if (p == null) {
throw new NoSuchProviderException(Messages.getString("security.03", provider)); //$NON-NLS-1$
}
return getSignatureInstance(algorithm, p);
}
/**
* Returns a new instance of {@code Signature} that utilizes the specified
* algorithm from the specified provider.
*
* @param algorithm
* the name of the algorithm to use.
* @param provider
* the security provider.
* @return a new instance of {@code Signature} that utilizes the specified
* algorithm from the specified provider.
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available.
* @throws NullPointerException
* if {@code algorithm} is {@code null}.
* @since Android 1.0
*/
public static Signature getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
if (algorithm == null) {
throw new NullPointerException(Messages.getString("security.01")); //$NON-NLS-1$
}
if (provider == null) {
throw new IllegalArgumentException(Messages.getString("security.04")); //$NON-NLS-1$
}
return getSignatureInstance(algorithm, provider);
}
private static Signature getSignatureInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
Signature result;
synchronized (engine) {
engine.getInstance(algorithm, provider, null);
if (engine.spi instanceof Signature) {
result = (Signature) engine.spi;
result.algorithm = algorithm;
result.provider = provider;
} else {
result = new SignatureImpl((SignatureSpi) engine.spi, provider,
algorithm);
}
}
return result;
}
/**
* Returns the provider associated with this {@code Signature}.
*
* @return the provider associated with this {@code Signature}.
* @since Android 1.0
*/
public final Provider getProvider() {
return provider;
}
/**
* Returns the name of the algorithm of this {@code Signature}.
*
* @return the name of the algorithm of this {@code Signature}.
* @since Android 1.0
*/
public final String getAlgorithm() {
return algorithm;
}
/**
* Initializes this {@code Signature} instance for signature verification,
* using the public key of the identity whose signature is going to be
* verified.
*
* @param publicKey
* the public key.
* @throws InvalidKeyException
* if {@code publicKey} is not valid.
* @since Android 1.0
*/
public final void initVerify(PublicKey publicKey)
throws InvalidKeyException {
engineInitVerify(publicKey);
state = VERIFY;
}
/**
* Initializes this {@code Signature} instance for signature verification,
* using the certificate of the identity whose signature is going to be
* verified.
* <p>
* If the given certificate is an instance of {@link X509Certificate} and
* has a key usage parameter that indicates, that this certificate is not to
* be used for signing, an {@code InvalidKeyException} is thrown.
* </p>
*
* @param certificate
* the certificate used to verify a signature.
* @throws InvalidKeyException
* if the publicKey in the certificate is not valid or not to be
* used for signing.
* @since Android 1.0
*/
public final void initVerify(Certificate certificate)
throws InvalidKeyException {
if (certificate instanceof X509Certificate) {
Set ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
boolean critical = false;
if (ce != null && !ce.isEmpty()) {
for (Iterator i = ce.iterator(); i.hasNext();) {
if ("2.5.29.15".equals(i.next())) { //$NON-NLS-1$
//KeyUsage OID = 2.5.29.15
critical = true;
break;
}
}
if (critical) {
boolean[] keyUsage = ((X509Certificate) certificate)
.getKeyUsage();
// As specified in RFC 3280 -
// Internet X.509 Public Key Infrastructure
// Certificate and Certificate Revocation List (CRL) Profile.
// (http://www.ietf.org/rfc/rfc3280.txt)
//
// KeyUsage ::= BIT STRING { digitalSignature (0), <skipped> }
if ((keyUsage != null) && (!keyUsage[0])) { // digitalSignature
throw new InvalidKeyException(
Messages.getString("security.26")); //$NON-NLS-1$
}
}
}
}
engineInitVerify(certificate.getPublicKey());
state = VERIFY;
}
/**
* Initializes this {@code Signature} instance for signing, using the
* private key of the identity whose signature is going to be generated.
*
* @param privateKey
* the private key.
* @throws InvalidKeyException
* if {@code privateKey} is not valid.
* @since Android 1.0
*/
public final void initSign(PrivateKey privateKey)
throws InvalidKeyException {
engineInitSign(privateKey);
state = SIGN;
}
/**
* Initializes this {@code Signature} instance for signing, using the
* private key of the identity whose signature is going to be generated and
* the specified source of randomness.
*
* @param privateKey
* the private key.
* @param random
* the {@code SecureRandom} to use.
* @throws InvalidKeyException
* if {@code privateKey} is not valid.
* @since Android 1.0
*/
public final void initSign(PrivateKey privateKey, SecureRandom random)
throws InvalidKeyException {
engineInitSign(privateKey, random);
state = SIGN;
}
/**
* Generates and returns the signature of all updated data.
* <p>
* This {@code Signature} instance is reset to the state of its last
* initialization for signing and thus can be used for another signature
* from the same identity.
* </p>
*
* @return the signature of all updated data.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final byte[] sign() throws SignatureException {
if (state != SIGN) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
return engineSign();
}
/**
* Generates and stores the signature of all updated data in the provided
* {@code byte[]} at the specified position with the specified length.
* <p>
* This {@code Signature} instance is reset to the state of its last
* initialization for signing and thus can be used for another signature
* from the same identity.
* </p>
*
* @param outbuf
* the buffer to store the signature.
* @param offset
* the index of the first byte in {@code outbuf} to store.
* @param len
* the number of bytes allocated for the signature.
* @return the number of bytes stored in {@code outbuf}.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @throws IllegalArgumentException
* if {@code offset} or {@code len} are not valid in respect to
* {@code outbuf}.
* @since Android 1.0
*/
public final int sign(byte[] outbuf, int offset, int len)
throws SignatureException {
if (outbuf == null || offset < 0 || len < 0 ||
offset + len > outbuf.length) {
throw new IllegalArgumentException(
Messages.getString("security.05")); //$NON-NLS-1$
}
if (state != SIGN) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
return engineSign(outbuf, offset, len);
}
/**
* Indicates whether the given {@code signature} can be verified using the
* public key or a certificate of the signer.
* <p>
* This {@code Signature} instance is reset to the state of its last
* initialization for verifying and thus can be used to verify another
* signature of the same signer.
* </p>
*
* @param signature
* the signature to verify.
* @return {@code true} if the signature was verified, {@code false}
* otherwise.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final boolean verify(byte[] signature) throws SignatureException {
if (state != VERIFY) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
return engineVerify(signature);
}
/**
* Indicates whether the given {@code signature} starting at index {@code
* offset} with {@code length} bytes can be verified using the public key or
* a certificate of the signer.
* <p>
* This {@code Signature} instance is reset to the state of its last
* initialization for verifying and thus can be used to verify another
* signature of the same signer.
* </p>
*
* @param signature
* the {@code byte[]} containing the signature to verify.
* @param offset
* the start index in {@code signature} of the signature.
* @param length
* the number of bytes allocated for the signature.
* @return {@code true} if the signature was verified, {@code false}
* otherwise.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @throws IllegalArgumentException
* if {@code offset} or {@code length} are not valid in respect
* to {@code signature}.
* @since Android 1.0
*/
public final boolean verify(byte[] signature, int offset, int length)
throws SignatureException {
if (state != VERIFY) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
if (signature == null || offset < 0 || length < 0 ||
offset + length > signature.length) {
throw new IllegalArgumentException(
Messages.getString("security.05")); //$NON-NLS-1$
}
return engineVerify(signature, offset, length);
}
/**
* Updates the data to be verified or to be signed, using the specified
* {@code byte}.
*
* @param b
* the byte to update with.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final void update(byte b) throws SignatureException {
if (state == UNINITIALIZED) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
engineUpdate(b);
}
/**
* Updates the data to be verified or to be signed, using the specified
* {@code byte[]}.
*
* @param data
* the byte array to update with.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final void update(byte[] data) throws SignatureException {
if (state == UNINITIALIZED) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
engineUpdate(data, 0, data.length);
}
/**
* Updates the data to be verified or to be signed, using the given {@code
* byte[]}, starting form the specified index for the specified length.
*
* @param data
* the byte array to update with.
* @param off
* the start index in {@code data} of the data.
* @param len
* the number of bytes to use.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final void update(byte[] data, int off, int len)
throws SignatureException {
if (state == UNINITIALIZED) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
if (data == null || off < 0 || len < 0 ||
off + len > data.length) {
throw new IllegalArgumentException(
Messages.getString("security.05")); //$NON-NLS-1$
}
engineUpdate(data, off, len);
}
/**
* Updates the data to be verified or to be signed, using the specified
* {@code ByteBuffer}.
*
* @param data
* the {@code ByteBuffer} to update with.
* @throws SignatureException
* if this {@code Signature} instance is not initialized
* properly.
* @since Android 1.0
*/
public final void update(ByteBuffer data) throws SignatureException {
if (state == UNINITIALIZED) {
throw new SignatureException(
Messages.getString("security.27")); //$NON-NLS-1$
}
engineUpdate(data);
}
/**
* Returns a string containing a concise, human-readable description of this
* {@code Signature} including its algorithm and its state.
*
* @return a printable representation for this {@code Signature}.
* @since Android 1.0
*/
public String toString() {
return "SIGNATURE " + algorithm + " state: " + stateToString(state); //$NON-NLS-1$ //$NON-NLS-2$
}
// Convert state to string
private String stateToString(int state) {
switch (state) {
case UNINITIALIZED:
return "UNINITIALIZED"; //$NON-NLS-1$
case SIGN:
return "SIGN"; //$NON-NLS-1$
case VERIFY:
return "VERIFY"; //$NON-NLS-1$
default:
return ""; //$NON-NLS-1$
}
}
// BEGIN android-note
// added Deprecated annotation
// END android-note
/**
* Sets the specified parameter to the given value.
*
* @param param
* the name of the parameter.
* @param value
* the parameter value.
* @throws InvalidParameterException
* if the parameter is invalid, already set or is not allowed to
* be changed.
* @deprecated Use {@link #setParameter(AlgorithmParameterSpec)}
* @since Android 1.0
*/
@Deprecated
public final void setParameter(String param, Object value)
throws InvalidParameterException {
engineSetParameter(param, value);
}
/**
* Sets the specified {@code AlgorithmParameterSpec}.
*
* @param params
* the parameter to set.
* @throws InvalidAlgorithmParameterException
* if the parameter is invalid, already set or is not allowed to
* be changed.
* @since Android 1.0
*/
public final void setParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
engineSetParameter(params);
}
/**
* Returns the {@code AlgorithmParameters} of this {@link Signature}
* instance.
*
* @return the {@code AlgorithmParameters} of this {@link Signature}
* instance, maybe {@code null}.
* @since Android 1.0
*/
public final AlgorithmParameters getParameters() {
return engineGetParameters();
}
// BEGIN android-note
// added Deprecated annotation
// END android-note
/**
* Returns the value of the parameter with the specified name.
*
* @param param
* the name of the requested parameter value
* @return the value of the parameter with the specified name, maybe {@code
* null}.
* @throws InvalidParameterException
* if {@code param} is not a valid parameter for this {@code
* Signature} or an other error occures.
* @deprecated There is no generally accepted parameter naming convention.
* @since Android 1.0
*/
@Deprecated
public final Object getParameter(String param)
throws InvalidParameterException {
return engineGetParameter(param);
}
public Object clone() throws CloneNotSupportedException {
if (this instanceof Cloneable) {
return super.clone();
} else {
throw new CloneNotSupportedException();
}
}
/**
*
* Internal Signature implementation
*
*/
private static class SignatureImpl extends Signature {
private SignatureSpi spiImpl;
// Constructor
public SignatureImpl(SignatureSpi signatureSpi, Provider provider,
String algorithm) {
super(algorithm);
super.provider = provider;
spiImpl = signatureSpi;
}
// engineSign() implementation
protected byte[] engineSign() throws SignatureException {
return spiImpl.engineSign();
}
// engineUpdate() implementation
protected void engineUpdate(byte arg0) throws SignatureException {
spiImpl.engineUpdate(arg0);
}
// engineVerify() implementation
protected boolean engineVerify(byte[] arg0) throws SignatureException {
return spiImpl.engineVerify(arg0);
}
// engineUpdate() implementation
protected void engineUpdate(byte[] arg0, int arg1, int arg2)
throws SignatureException {
spiImpl.engineUpdate(arg0, arg1, arg2);
}
// engineInitSign() implementation
protected void engineInitSign(PrivateKey arg0)
throws InvalidKeyException {
spiImpl.engineInitSign(arg0);
}
// engineInitVerify() implementation
protected void engineInitVerify(PublicKey arg0)
throws InvalidKeyException {
spiImpl.engineInitVerify(arg0);
}
// engineGetParameter() implementation
protected Object engineGetParameter(String arg0)
throws InvalidParameterException {
return spiImpl.engineGetParameter(arg0);
}
// engineSetParameter() implementation
protected void engineSetParameter(String arg0, Object arg1)
throws InvalidParameterException {
spiImpl.engineSetParameter(arg0, arg1);
}
// Returns a clone if the spiImpl is cloneable
public Object clone() throws CloneNotSupportedException {
if (spiImpl instanceof Cloneable) {
SignatureSpi spi = (SignatureSpi) spiImpl.clone();
return new SignatureImpl(spi, getProvider(), getAlgorithm());
} else {
throw new CloneNotSupportedException();
}
}
}
}
|