/***************************************************************************** * Copyright (c) 2008 g-Eclipse Consortium * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Mathias Stuempert - initial API and implementation *****************************************************************************/ package eu.geclipse.core.internal.security; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import javax.security.auth.x500.X500Principal; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import eu.geclipse.core.ICoreProblems; import eu.geclipse.core.internal.Activator; import eu.geclipse.core.reporting.ProblemException; import eu.geclipse.core.security.ICertificateHandle; import eu.geclipse.core.security.Security; import eu.geclipse.core.security.X509Util; import eu.geclipse.core.security.ICertificateManager.CertTrust; /** * Certificate handle dedicated to X.509 certificates. */ public class X509CertificateHandle implements ICertificateHandle { /** * The certificate itself. */ private X509Certificate certificate; /** * A file linked to the certificate. */ private File file; /** * The certificates's trust mode. */ private CertTrust trust; /** * Create a new handle for the specified certificate. If the trust mode * is {@link CertTrust#AlwaysTrusted} the certificate is saved to disk. * * @param c The certificate itself. * @param trust The trust mode of the certificate. * @throws ProblemException If saving the certificate failed. * @see {@link #save(X509Certificate)} */ public X509CertificateHandle( final X509Certificate c, final CertTrust trust ) throws ProblemException { if ( trust == CertTrust.AlwaysTrusted ) { this.file = save( c ); } this.certificate = c; this.trust = trust; } /** * Create a new handle from the specified file. * * @param f The file from which to load a certificate. * @throws ProblemException If no certificate could be loaded from the * specified file. */ X509CertificateHandle( final File f ) throws ProblemException { this.certificate = load( f ); this.file = f; this.trust = CertTrust.AlwaysTrusted; } /* (non-Javadoc) * @see eu.geclipse.core.security.ICertificateHandle#delete() */ public boolean delete() { return this.file != null ? this.file.delete() : true; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals( final Object o ) { boolean result = false; if ( o instanceof X509CertificateHandle ) { result = ( ( X509CertificateHandle ) o ).getCertificate().equals( getCertificate() ); } return result; } /* (non-Javadoc) * @see eu.geclipse.core.security.ICertificateHandle#getCertificate() */ public X509Certificate getCertificate() { return this.certificate; } /* (non-Javadoc) * @see eu.geclipse.core.security.ICertificateHandle#getTrust() */ public CertTrust getTrust() { return this.trust; } /** * Try to load a certificate from the specified file. * * @param f The file from which to load the certificate. * @return The loaded certificate. * @throws ProblemException if no certificate could be loaded from the * specified file. */ private X509Certificate load( final File f ) throws ProblemException { try { FileInputStream fis = new FileInputStream( f ); return X509Util.loadCertificate( fis ); } catch( FileNotFoundException fnfExc ) { throw new ProblemException( ICoreProblems.SECURITY_CERT_LOAD_FAILED, Messages.getString("X509CertificateHandle.load_failed") + f.getName(), //$NON-NLS-1$ fnfExc, Activator.PLUGIN_ID ); } } /** * Try to save the specified certificate at * {@link CertificateManager#getCertificateLocation()}. * * @param c The certificate to be saved. * @return The corresponding file. * @throws ProblemException If saving the certificate failed. */ private File save( final X509Certificate c ) throws ProblemException { File result = null; int hash = -1; try { hash = subjectDNHash( c ); } catch( NoSuchAlgorithmException nsaExc ) { Activator.logStatus( new Status( IStatus.WARNING, Activator.PLUGIN_ID, Messages.getString("X509CertificateHandle.no_md5_hash"), //$NON-NLS-1$ nsaExc ) ); hash = c.getSubjectX500Principal().hashCode(); } String name = String.format( "%08x", Integer.valueOf( hash ) ); //$NON-NLS-1$ IPath filepath = Security.getCertificateLocation().append( name ); for ( int i = 0 ; i <= 9 ; i++ ) { result = filepath.addFileExtension( String.valueOf( i ) ).toFile(); if ( ! result.exists() ) { try { X509Util.saveCertificate( c, new FileOutputStream( result ) ); } catch ( FileNotFoundException fnfExc ) { throw new ProblemException( ICoreProblems.SECURITY_CERT_SAVE_FAILED, fnfExc, Activator.PLUGIN_ID ); } break; } else if ( i == 9 ) { throw new ProblemException( ICoreProblems.SECURITY_CERT_SAVE_FAILED, Messages.getString("X509CertificateHandle.inappropriate_file_name"), //$NON-NLS-1$ Activator.PLUGIN_ID ); } } return result; } /** * Create the MD5 hash of the specified certificate's subject DN. * * @param c The certificate for which to create the hash. * @return The create hash. * @throws NoSuchAlgorithmException If no MD5 algorithm could be found. */ private int subjectDNHash( final X509Certificate c ) throws NoSuchAlgorithmException { X500Principal subjectDN = c.getSubjectX500Principal(); byte[] encoded = subjectDN.getEncoded(); MessageDigest md5 = MessageDigest.getInstance( "MD5" ); //$NON-NLS-1$ md5.reset(); byte[] digest = md5.digest( encoded ); int result = digest[ 0 ] & 0xFF | ( ( digest[ 1 ] & 0xFF ) << 8 ) | ( ( digest[ 2 ] & 0xFF ) << 16 ) | ( ( digest[ 3 ] & 0xFF ) << 24 ); return result; } }