/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.installer;
import java.io.InputStream;
import java.io.IOException;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.pki.CertificateException;
import com.sun.midp.pki.*;
import com.sun.midp.publickeystore.*;
import com.sun.midp.crypto.*;
import com.sun.midp.security.*;
import com.sun.midp.io.j2me.storage.RandomAccessStream;
import com.sun.midp.io.Base64;
/**
* Verifier that is able to verify midlet suite's signature.
* It is used when the crypto code is present in the build.
*/
public class VerifierImpl implements Verifier {
/**
* Current installation state.
*/
InstallState state;
/**
* Authorization Path: A list of authority names from the verification,
* begining with the most trusted.
*/
private String[] authPath;
/** Authenticated content provider certificate. */
private X509Certificate cpCert;
/**
* Constructor.
*
* @param state current state of the installation
*/
public VerifierImpl(InstallState installState) {
state = installState;
}
/**
* Checks to see if the JAD has a signature, but does not verify the
* signature.
*
* @return true if the JAD has a signature
*/
public boolean isJadSigned() {
return state.getAppProperty(SIG_PROP) != null;
}
/**
* Looks up the domain of a MIDlet suite.
*
* @param ca CA of an installed suite
*
* @return security domain of the MIDlet suite
*/
public String getSecurityDomainName(String ca) {
Vector keys;
String domain;
/*
* look up the domain owner, then get the domain from the
* trusted key store and set the security domain
*/
try {
keys = WebPublicKeyStore.getTrustedKeyStore().
findKeys(ca);
domain = ((PublicKeyInfo)keys.elementAt(0)).getDomain();
} catch (Exception e) {
domain = Permissions.UNIDENTIFIED_DOMAIN_BINDING;
}
return domain;
}
/**
* Verifies a Jar. On success set the name of the domain owner in the
* install state. Post any error back to the server.
*
* @param jarStorage System store for applications
* @param jarFilename name of the jar to read.
*
* @exception IOException if any error prevents the reading
* of the JAR
* @exception InvalidJadException if the JAR is not valid or the
* provider certificate is missing
*/
public String[] verifyJar(RandomAccessStream jarStorage,
String jarFilename) throws IOException, InvalidJadException {
InputStream jarStream;
String jarSig;
jarSig = state.getAppProperty(SIG_PROP);
if (jarSig == null) {
// no signature to verify
return null;
}
authPath = null;
// This will fill in the cpCert and authPath fields
findProviderCert();
jarStorage.connect(jarFilename, Connector.READ);
try {
jarStream = jarStorage.openInputStream();
try {
verifyStream(jarStream, jarSig);
// state.installInfo.authPath = authPath;
} finally {
jarStream.close();
}
} finally {
jarStorage.disconnect();
}
return authPath;
}
/**
* Find the first provider certificate that is signed by a known CA.
* Set the lastCA field to name of the CA. Set the cpCert field to the
* provider certificate.
*
* IMPL_NOTE: in the case of erroneous certificate chains the first
* chain error will be thrown.
*
* @exception InvalidJadException if the JAR is not valid or the
* provider certificate is missing or a general certificate error
*/
private void findProviderCert() throws InvalidJadException {
int chain;
int result;
InvalidJadException pendingException = null;
for (chain = 1; ; chain++) {
// sets the authPath and cpCert
try {
result = checkCertChain(chain);
} catch (InvalidJadException ije) {
// According to the spec, if some chain is invalid and
// the next chain exists, it should also be verified;
// the first valid chain should be used for the jar
// verification.
if (pendingException == null) {
pendingException = ije;
}
continue;
}
if (result == 1) {
// we found the good chain
return;
}
if (result == -1) {
// chain not found, done
break;
}
}
if (pendingException != null) {
throw pendingException;
}
if (chain == 1) {
throw new
InvalidJadException(InvalidJadException.MISSING_PROVIDER_CERT);
}
// None of the certificates were issued by a known CA
throw new
InvalidJadException(InvalidJadException.UNKNOWN_CA,
authPath[0]);
}
/**
* Check to see if a provider certificate chain is issued by a known
* CA. Set the authPath field to names of the auth chain in any case.
* Authenticate the chain and set the cpCert field to the provider's
* certificate if the CA is known.
*
* @param chainNum the number of the chain
*
* @return 1 if the CA of the chain is known, 0 if not, -1 if the
* chain is not found
*
* @exception InvalidJadException if something other wrong with a
* other than an unknown CA
*/
private int checkCertChain(int chainNum)
throws InvalidJadException {
int certNum;
Vector derCerts = new Vector();
String base64Cert;
byte[] derCert;
WebPublicKeyStore keyStore;
Vector keys;
PublicKeyInfo keyInfo;
for (certNum = 1; ; certNum++) {
base64Cert = state.getAppProperty(CERT_PROP +
chainNum + "-" + certNum);
if (base64Cert == null) {
break;
}
try {
derCert = Base64.decode(base64Cert);
derCerts.addElement(X509Certificate.generateCertificate(
derCert, 0, derCert.length));
} catch (Exception e) {
throw new InvalidJadException(
InvalidJadException.CORRUPT_PROVIDER_CERT);
}
}
if (certNum == 1) {
// Chain not found
return -1;
}
try {
keyStore = WebPublicKeyStore.getTrustedKeyStore();
authPath = X509Certificate.verifyChain(derCerts,
X509Certificate.DIGITAL_SIG_KEY_USAGE,
X509Certificate.CODE_SIGN_EXT_KEY_USAGE,
keyStore);
} catch (CertificateException ce) {
switch (ce.getReason()) {
case CertificateException.UNRECOGNIZED_ISSUER:
authPath = new String[1];
authPath[0] = ce.getCertificate().getIssuer();
// Issuer not found
return 0;
case CertificateException.EXPIRED:
case CertificateException.NOT_YET_VALID:
throw new InvalidJadException(
InvalidJadException.EXPIRED_PROVIDER_CERT,
ce.getCertificate().getSubject());
case CertificateException.ROOT_CA_EXPIRED:
throw new InvalidJadException(
InvalidJadException.EXPIRED_CA_KEY,
ce.getCertificate().getIssuer());
}
throw new InvalidJadException(
InvalidJadException.INVALID_PROVIDER_CERT,
ce.getCertificate().getSubject());
}
// The root CA may have been disabled for software authorization.
keys = keyStore.findKeys(authPath[0]);
keyInfo = (PublicKeyInfo)keys.elementAt(0);
if (!keyInfo.isEnabled()) {
throw new InvalidJadException(
InvalidJadException.CA_DISABLED,
authPath[0]);
}
cpCert = (X509Certificate)derCerts.elementAt(0);
// Authenticated
return 1;
}
/**
* Common routine that verifies a stream of bytes.
* The cpCert field must be set before calling.
*
* @param stream stream to verify
* @param base64Signature The base64 encoding of the PKCS v1.5 SHA with
* RSA signature of this stream.
*
* @exception NullPointerException if the public keystore has not been
* established.
* @exception InvalidJadException the JAR signature is not valid
* @exception IOException if any error prevents the reading
* of the JAR
*/
private void verifyStream(InputStream stream, String base64Signature)
throws InvalidJadException, IOException {
PublicKey cpKey;
byte[] sig;
Signature sigVerifier;
byte[] temp;
int bytesRead;
byte[] hash;
try {
cpKey = cpCert.getPublicKey();
} catch (CertificateException e) {
throw new
InvalidJadException(InvalidJadException.INVALID_PROVIDER_CERT);
}
try {
sig = Base64.decode(base64Signature);
} catch (IOException e) {
throw new
InvalidJadException(InvalidJadException.CORRUPT_SIGNATURE);
}
try {
// verify the jad signature
sigVerifier = Signature.getInstance("SHA1withRSA");
sigVerifier.initVerify(cpKey);
temp = new byte[1024];
for (; ; ) {
bytesRead = stream.read(temp);
if (bytesRead == -1) {
break;
}
sigVerifier.update(temp, 0, bytesRead);
}
if (!sigVerifier.verify(sig)) {
throw new
InvalidJadException(InvalidJadException.INVALID_SIGNATURE);
}
} catch (GeneralSecurityException e) {
throw new
InvalidJadException(InvalidJadException.INVALID_SIGNATURE);
}
}
}
|