[ http://issues.apache.org/jira/browse/WSS-59?page=comments#action_12452156 ] Ruchith Udayanga Fernando commented on WSS-59: ----------------------------------------------
hmm ... IMHO we don't have to change the default Merlin/AbstractCrypto stuff to support this. Especially since this does not improve security. I think one can easily implement their own Crypto impl which can be configured in any way required. > Crypto.properties doesn't allow encoded password for keystore - WSS4J1.5 > ------------------------------------------------------------------------ > > Key: WSS-59 > URL: http://issues.apache.org/jira/browse/WSS-59 > Project: WSS4J > Issue Type: New Feature > Environment: ALL > Reporter: Soumadeep > Assigned To: Davanum Srinivas > Priority: Blocker > > Crypto.properties file doesn't allow encoded passwords for keystore and > alias. The plain text password would be unacceptable for most of the > enterprises. > The load method of AbstractCrypto.java file actually picks up the passwords > from the crypto.properties file. I have made the required changes and am > putting it below. This only accounts for the keystore password as the alias > password would be usage specific. Please check the two files > crypto.properties and AbstractCrypto.java. The files are from the wss4j main > trunk - svn > Thanks > Soumadeep > Crypto.properties > ============= > org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin > org.apache.ws.security.crypto.merlin.keystore.type=jks > // This property has been added > org.apache.ws.security.crypto.merlin.keystore.base64.encoded=false > org.apache.ws.security.crypto.merlin.keystore.password=soumadeep > org.apache.ws.security.crypto.merlin.keystore.alias=soumadeep > org.apache.ws.security.crypto.merlin.alias.password=soumadeep > org.apache.ws.security.crypto.merlin.file=resources/soumadeep.jks > AbstractCrypto.java > =============== > /* > * Copyright 2003-2004 The Apache Software Foundation. > * > * Licensed 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.ws.security.components.crypto; > import org.apache.commons.logging.Log; > import org.apache.commons.logging.LogFactory; > import org.apache.ws.security.WSSecurityException; > import org.apache.ws.security.util.Base64; > import org.apache.ws.security.util.Loader; > import java.io.FileInputStream; > import java.io.IOException; > import java.io.InputStream; > import java.math.BigInteger; > import java.security.GeneralSecurityException; > import java.security.Key; > import java.security.KeyStore; > import java.security.KeyStoreException; > import java.security.MessageDigest; > import java.security.NoSuchAlgorithmException; > import java.security.NoSuchProviderException; > import java.security.PrivateKey; > import java.security.PublicKey; > import java.security.cert.Certificate; > import java.security.cert.CertificateEncodingException; > import java.security.cert.CertificateException; > import java.security.cert.CertificateFactory; > import java.security.cert.X509Certificate; > import java.security.interfaces.RSAPublicKey; > import java.util.Arrays; > import java.util.Enumeration; > import java.util.Properties; > import java.util.Vector; > /** > * Created by IntelliJ IDEA. > * User: dims > * Date: Sep 15, 2005 > * Time: 9:50:40 AM > * To change this template use File | Settings | File Templates. > */ > public abstract class AbstractCrypto implements Crypto { > private static Log log = LogFactory.getLog(AbstractCrypto.class); > protected static CertificateFactory certFact; > protected Properties properties = null; > protected KeyStore keystore = null; > static String SKI_OID = "2.5.29.14"; > /** > * Constructor > * > * @param properties > */ > public AbstractCrypto(Properties properties) throws CredentialException, > IOException { > this(properties,AbstractCrypto.class.getClassLoader()); > } > /** > * This allows providing a custom class loader to load the resources, etc > * @param properties > * @param loader > * @throws CredentialException > * @throws IOException > */ > public AbstractCrypto(Properties properties, ClassLoader loader) throws > CredentialException, IOException { > /* > * if no properties .. just return an instance, the rest will be > * done later or this instance is just used to handle certificate > * conversions in this implementatio > */ > if (properties == null) { > return; > } > this.properties = properties; > String location = > this.properties.getProperty("org.apache.ws.security.crypto.merlin.file"); > InputStream is = null; > java.net.URL url = Loader.getResource(loader, location); > if(url != null) { > is = url.openStream(); > } else { > is = new java.io.FileInputStream(location); > } > /** > * If we don't find it, then look on the file system. > */ > if (is == null) { > try { > is = new FileInputStream(location); > } catch (Exception e) { > throw new CredentialException(3, "proxyNotFound", new > Object[]{location}); > } > } > /** > * Load the keystore > */ > try { > load(is); > } finally { > is.close(); > } > } > > /** > * Singleton certificate factory for this Crypto instance. > * <p/> > * > * @return Returns a <code>CertificateFactory</code> to construct > * X509 certficates > * @throws org.apache.ws.security.WSSecurityException > * > */ > public synchronized CertificateFactory getCertificateFactory() throws > WSSecurityException { > if (certFact == null) { > try { > String provider = > properties.getProperty("org.apache.ws.security.crypto.merlin.cert.provider"); > if (provider == null || provider.length() == 0) { > certFact = CertificateFactory.getInstance("X.509"); > } else { > certFact = CertificateFactory.getInstance("X.509", > provider); > } > } catch (CertificateException e) { > throw new > WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, > "unsupportedCertType"); > } catch (NoSuchProviderException e) { > throw new > WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, > "noSecProvider"); > } > } > return certFact; > } > /** > * load a X509Certificate from the input stream. > * <p/> > * > * @param in The <code>InputStream</code> array containg the X509 data > * @return Returns a X509 certificate > * @throws org.apache.ws.security.WSSecurityException > * > */ > public X509Certificate loadCertificate(InputStream in) throws > WSSecurityException { > X509Certificate cert = null; > try { > cert = > (X509Certificate) > getCertificateFactory().generateCertificate(in); > } catch (CertificateException e) { > throw new > WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, > "parseError"); > } > return cert; > } > /** > * Gets the private key identified by <code>alias</> and > <code>password</code>. > * <p/> > * > * @param alias The alias (<code>KeyStore</code>) of the key owner > * @param password The password needed to access the private key > * @return The private key > * @throws Exception > */ > public PrivateKey getPrivateKey(String alias, String password) throws > Exception { > if (alias == null) { > throw new Exception("alias is null"); > } > boolean b = keystore.isKeyEntry(alias); > if (!b) { > log.error("Cannot find key for alias: " + alias); > throw new Exception("Cannot find key for alias: " + alias); > } > Key keyTmp = keystore.getKey(alias, password.toCharArray()); > if (!(keyTmp instanceof PrivateKey)) { > throw new Exception("Key is not a private key, alias: " + alias); > } > return (PrivateKey) keyTmp; > } > private Vector splitAndTrim(String inString) { > X509NameTokenizer nmTokens = new X509NameTokenizer(inString); > Vector vr = new Vector(); > while (nmTokens.hasMoreTokens()) { > vr.add(nmTokens.nextToken()); > } > java.util.Collections.sort(vr); > return vr; > } > /** > * Lookup a X509 Certificate in the keystore according to a given > * the issuer of a Certficate. > * <p/> > * The search gets all alias names of the keystore and gets the > certificate chain > * for each alias. Then the Issuer fo each certificate of the chain > * is compared with the parameters. > * > * @param issuer The issuer's name for the certificate > * @return alias name of the certificate that matches the issuer name > * or null if no such certificate was found. > */ > public String getAliasForX509Cert(String issuer) > throws WSSecurityException { > return getAliasForX509Cert(issuer, null, false); > } > /** > * Lookup a X509 Certificate in the keystore according to a given serial > number and > * the issuer of a Certficate. > * <p/> > * The search gets all alias names of the keystore and gets the > certificate chain > * for each alias. Then the SerialNumber and Issuer fo each certificate > of the chain > * is compared with the parameters. > * > * @param issuer The issuer's name for the certificate > * @param serialNumber The serial number of the certificate from the > named issuer > * @return alias name of the certificate that matches serialNumber and > issuer name > * or null if no such certificate was found. > */ > public String getAliasForX509Cert(String issuer, BigInteger serialNumber) > throws WSSecurityException { > return getAliasForX509Cert(issuer, serialNumber, true); > } > /* > * need to check if "getCertificateChain" also finds certificates that are > * used for enryption only, i.e. they may not be signed by a CA > * Otherwise we must define a restriction how to use certificate: > * each certificate must be signed by a CA or is a self signed Certificate > * (this should work as well). > * --- remains to be tested in several ways -- > */ > private String getAliasForX509Cert(String issuer, BigInteger serialNumber, > boolean useSerialNumber) > throws WSSecurityException { > Vector issuerRDN = splitAndTrim(issuer); > X509Certificate x509cert = null; > Vector certRDN = null; > Certificate cert = null; > try { > for (Enumeration e = keystore.aliases(); e.hasMoreElements();) { > String alias = (String) e.nextElement(); > Certificate[] certs = keystore.getCertificateChain(alias); > if (certs == null || certs.length == 0) { > // no cert chain, so lets check if getCertificate gives > us a result. > cert = keystore.getCertificate(alias); > if (cert == null) { > return null; > } > } else { > cert = certs[0]; > } > if (!(cert instanceof X509Certificate)) { > continue; > } > x509cert = (X509Certificate) cert; > if (!useSerialNumber || > useSerialNumber && > x509cert.getSerialNumber().compareTo(serialNumber) == 0) { > certRDN = splitAndTrim(x509cert.getIssuerDN().getName()); > if (certRDN.equals(issuerRDN)) { > return alias; > } > } > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > return null; > } > /** > * Lookup a X509 Certificate in the keystore according to a given > * SubjectKeyIdentifier. > * <p/> > * The search gets all alias names of the keystore and gets the > certificate chain > * or certificate for each alias. Then the SKI for each user certificate > * is compared with the SKI parameter. > * > * @param skiBytes The SKI info bytes > * @return alias name of the certificate that matches serialNumber and > issuer name > * or null if no such certificate was found. > * @throws org.apache.ws.security.WSSecurityException > * if problems during keystore handling or wrong certificate (no > SKI data) > */ > public String getAliasForX509Cert(byte[] skiBytes) throws > WSSecurityException { > Certificate cert = null; > boolean found = false; > try { > for (Enumeration e = keystore.aliases(); e.hasMoreElements();) { > String alias = (String) e.nextElement(); > Certificate[] certs = keystore.getCertificateChain(alias); > if (certs == null || certs.length == 0) { > // no cert chain, so lets check if getCertificate gives > us a result. > cert = keystore.getCertificate(alias); > if (cert == null) { > return null; > } > } else { > cert = certs[0]; > } > if (!(cert instanceof X509Certificate)) { > continue; > } > byte[] data = getSKIBytesFromCert((X509Certificate) cert); > if (data.length != skiBytes.length) { > continue; > } > if (Arrays.equals(data, skiBytes)) { > return alias; > } > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > return null; > } > /** > * Return a X509 Certificate alias in the keystore according to a given > Certificate > * <p/> > * > * @param cert The certificate to lookup > * @return alias name of the certificate that matches the given > certificate > * or null if no such certificate was found. > */ > /* > * See comment above > */ > public String getAliasForX509Cert(Certificate cert) throws > WSSecurityException { > try { > String alias = keystore.getCertificateAlias(cert); > if (alias != null) > return alias; > // Use brute force search > Enumeration e = keystore.aliases(); > while (e.hasMoreElements()) { > alias = (String) e.nextElement(); > X509Certificate cert2 = (X509Certificate) > keystore.getCertificate(alias); > if (cert2.equals(cert)) { > return alias; > } > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > return null; > } > /** > * Retrieves the alias name of the default certificate which has been > * specified as a property. This should be the certificate that is used > for > * signature and encryption. This alias corresponds to the certificate > that > * should be used whenever KeyInfo is not poresent in a signed or > * an encrypted message. May return null. > * > * @return alias name of the default X509 certificate > */ > public String getDefaultX509Alias() { > if (properties == null) { > return null; > } > return > properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias"); > } > /** > * Gets the list of certificates for a given alias. > * <p/> > * > * @param alias Lookup certificate chain for this alias > * @return Array of X509 certificates for this alias name, or > * null if this alias does not exist in the keystore > */ > public X509Certificate[] getCertificates(String alias) throws > WSSecurityException { > Certificate[] certs = null; > try { > certs = keystore.getCertificateChain(alias); > if (certs == null || certs.length == 0) { > // no cert chain, so lets check if getCertificate gives us a > result. > Certificate cert = keystore.getCertificate(alias); > if (cert == null) { > return null; > } > certs = new Certificate[]{cert}; > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > X509Certificate[] x509certs = new X509Certificate[certs.length]; > for (int i = 0; i < certs.length; i++) { > x509certs[i] = (X509Certificate) certs[i]; > } > return x509certs; > } > /** > * Lookup a X509 Certificate in the keystore according to a given > * Thumbprint. > * <p/> > * The search gets all alias names of the keystore, then reads the > certificate chain > * or certificate for each alias. Then the thumbprint for each user > certificate > * is compared with the thumbprint parameter. > * > * @param thumb The SHA1 thumbprint info bytes > * @return alias name of the certificate that matches the thumbprint > * or null if no such certificate was found. > * @throws org.apache.ws.security.WSSecurityException > * if problems during keystore handling or wrong certificate > */ > public String getAliasForX509CertThumb(byte[] thumb) throws > WSSecurityException { > Certificate cert = null; > MessageDigest sha = null; > try { > sha = MessageDigest.getInstance("SHA-1"); > } catch (NoSuchAlgorithmException e1) { > throw new WSSecurityException( > 0, > "noSHA1availabe"); > } > try { > for (Enumeration e = keystore.aliases(); e.hasMoreElements();) { > String alias = (String) e.nextElement(); > Certificate[] certs = keystore.getCertificateChain(alias); > if (certs == null || certs.length == 0) { > // no cert chain, so lets check if getCertificate gives > us a result. > cert = keystore.getCertificate(alias); > if (cert == null) { > return null; > } > } else { > cert = certs[0]; > } > if (!(cert instanceof X509Certificate)) { > continue; > } > sha.reset(); > try { > sha.update(cert.getEncoded()); > } catch (CertificateEncodingException e1) { > throw new WSSecurityException( > WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, > "encodeError"); > } > byte[] data = sha.digest(); > if (Arrays.equals(data, thumb)) { > return alias; > } > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > return null; > } > /** > * A Hook for subclasses to set the keystore without having to > * load it from an <code>InputStream</code>. > * > * @param ks existing keystore > */ > public void setKeyStore(KeyStore ks) { > keystore = ks; > } > /** > * Loads the the keystore from an <code>InputStream </code>. > * <p/> > * > * @param input <code>InputStream</code> to read from > * @throws CredentialException > */ > public void load(InputStream input) throws CredentialException { > if (input == null) { > throw new IllegalArgumentException("input stream cannot be null"); > } > try { > String provider = > properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.provider"); > if (provider == null || provider.length() == 0) { > keystore = KeyStore.getInstance > > (properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.type", > KeyStore.getDefaultType())); > } else { > keystore = KeyStore.getInstance > > (properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.type", > KeyStore.getDefaultType()), provider); > } > String password = null; > String base64Encoded = > properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.base64.encoded","false"); > password = > properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.password", > "security"); > if(base64Encoded.equalsIgnoreCase("true")){ > password = new String(Base64.decode(password)); > } > > keystore.load(input, (password == null || password.length() == 0) > ? new char[0] : password.toCharArray()); > } catch (IOException e) { > e.printStackTrace(); > throw new CredentialException(3, "ioError00", e); > } catch (GeneralSecurityException e) { > e.printStackTrace(); > throw new CredentialException(3, "secError00", e); > } catch (Exception e) { > e.printStackTrace(); > throw new CredentialException(-1, "error00", e); > } > } > /** > * Reads the SubjectKeyIdentifier information from the certificate. > * <p/> > * If the the certificate does not contain a SKI extension then > * try to compute the SKI according to RFC3280 using the > * SHA-1 hash value of the public key. The second method described > * in RFC3280 is not support. Also only RSA public keys are supported. > * If we cannot compute the SKI throw a WSSecurityException. > * > * @param cert The certificate to read SKI > * @return The byte array conating the binary SKI data > */ > public byte[] getSKIBytesFromCert(X509Certificate cert) > throws WSSecurityException { > /* > * Gets the DER-encoded OCTET string for the extension value > (extnValue) > * identified by the passed-in oid String. The oid string is > represented > * by a set of positive whole numbers separated by periods. > */ > byte[] derEncodedValue = cert.getExtensionValue(SKI_OID); > if (cert.getVersion() < 3 || derEncodedValue == null) { > PublicKey key = cert.getPublicKey(); > if (!(key instanceof RSAPublicKey)) { > throw new WSSecurityException( > 1, > "noSKIHandling", > new Object[]{"Support for RSA key only"}); > } > byte[] encoded = key.getEncoded(); > // remove 22-byte algorithm ID and header > byte[] value = new byte[encoded.length - 22]; > System.arraycopy(encoded, 22, value, 0, value.length); > MessageDigest sha; > try { > sha = MessageDigest.getInstance("SHA-1"); > } catch (NoSuchAlgorithmException ex) { > throw new WSSecurityException( > 1, > "noSKIHandling", > new Object[]{"Wrong certificate version (<3) and no > SHA1 message digest availabe"}); > } > sha.reset(); > sha.update(value); > return sha.digest(); > } > /** > * Strip away first four bytes from the DerValue (tag and length of > * ExtensionValue OCTET STRING and KeyIdentifier OCTET STRING) > */ > byte abyte0[] = new byte[derEncodedValue.length - 4]; > System.arraycopy(derEncodedValue, 4, abyte0, 0, abyte0.length); > return abyte0; > } > public KeyStore getKeyStore() { > return this.keystore; > } > /** > * Lookup X509 Certificates in the keystore according to a given DN of > the subject of the certificate > * <p/> > * The search gets all alias names of the keystore and gets the > certificate (chain) > * for each alias. Then the DN of the certificate is compared with the > parameters. > * > * @param subjectDN The DN of subject to look for in the keystore > * @return Vector with all alias of certificates with the same DN as > given in the parameters > * @throws org.apache.ws.security.WSSecurityException > * > */ > public String[] getAliasesForDN(String subjectDN) throws > WSSecurityException { > // Store the aliases found > Vector aliases = new Vector(); > Certificate cert = null; > // The DN to search the keystore for > Vector subjectRDN = splitAndTrim(subjectDN); > // Look at every certificate in the keystore > try { > for (Enumeration e = keystore.aliases(); e.hasMoreElements();) { > String alias = (String) e.nextElement(); > Certificate[] certs = keystore.getCertificateChain(alias); > if (certs == null || certs.length == 0) { > // no cert chain, so lets check if getCertificate gives > us a result. > cert = keystore.getCertificate(alias); > if (cert == null) { > return null; > } > certs = new Certificate[]{cert}; > } else { > cert = certs[0]; > } > if (cert instanceof X509Certificate) { > Vector foundRDN = splitAndTrim(((X509Certificate) > cert).getSubjectDN().getName()); > if (subjectRDN.equals(foundRDN)) { > aliases.add(alias); > } > } > } > } catch (KeyStoreException e) { > throw new WSSecurityException(WSSecurityException.FAILURE, > "keystore"); > } > // Convert the vector into an array > String[] result = new String[aliases.size()]; > for (int i = 0; i < aliases.size(); i++) > result[i] = (String) aliases.elementAt(i); > return result; > } > } -- This message is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
