/* Copyright 2006 VPAC * * This file is part of Grix. * Grix is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * Grix 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 for more details. * You should have received a copy of the GNU General Public License * along with Grix; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.vpac.grix.control.certificate; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.ResourceBundle; import org.apache.log4j.Logger; import org.vpac.common.exceptions.MissingInformationException; import org.vpac.common.model.GlobusLocations; import org.vpac.grix.control.utils.GrixProperty; import org.vpac.grix.control.utils.UserProperty; import org.vpac.grix.control.utils.Utils; import org.vpac.grix.exceptions.certificate.UnableToDownloadCertificateException; import org.vpac.grix.exceptions.certificate.UnableToUploadCertificationRequestException; import org.vpac.grix.model.certificate.Certificate; import org.vpac.grix.model.certificate.CertificationRequest; import org.vpac.grix.plugins.openca.OpenCA; /** * Controls the retrieval and storage of the certificate. Should be easy to * implement some kind of plugin architecture so that other CA software than * OpenCA can be used. * * @author Markus Binsteiner * */ public class ManageCertificate { static final Logger myLogger = Logger.getLogger(ManageCertificate.class .getName()); static ResourceBundle messages = ResourceBundle.getBundle( "ControlMessagesBundle", java.util.Locale.getDefault()); /** * Get the certificate from server and store it afterwards. * * @return the certificate or null (if not found on the server) * @throws GeneralSecurityException * If the answer string can't be translated into a certificate * @throws UnableToDownloadCertificateException * If the certificate can't be downloaded for whatever reason */ public static Certificate downloadCertificate(String c, String o, String ou, String cn, String email) throws GeneralSecurityException, UnableToDownloadCertificateException { // start downloading myLogger.debug("Trying to get certificate with DN: C=" + c + ", O=" + o + ", OU=" + ou + ", CN=" + cn); if (!readyToDownload()) return null; // TODO implement plugin architecture String cert = null; try { cert = OpenCA.downloadCertificate(c, o, ou, cn, email); } catch (Exception e) { throw new UnableToDownloadCertificateException("openca", e); } myLogger.debug("Answer from server: \n" + cert); if (cert != null && cert.indexOf(Certificate.PEM_HEADER) != -1) { Certificate newCert = new Certificate(cert); Certificate localCert = null; try { localCert = new Certificate(GlobusLocations.defaultLocations() .getUserCert()); } catch (Exception e) { // can't create old cert, asuming this is the new one... return newCert; } if (localCert.getEnddate().equals(newCert.getEnddate())) { // myLogger.debug("Your certificate is not ready yet. Please try it again later."); return null; } // this means the certificate is ready return newCert; } else { return null; } } public static boolean exchangeRenewedCertificateFiles() { if (GlobusLocations.defaultLocations().getUserCert().exists()) { if (!GlobusLocations .defaultLocations() .getUserCert() .renameTo( Utils.backupFile(GlobusLocations.defaultLocations() .getUserCert()))) return false; } if (GlobusLocations.defaultLocations().getUserKey().exists()) { if (!GlobusLocations .defaultLocations() .getUserKey() .renameTo( Utils.backupFile(GlobusLocations.defaultLocations() .getUserKey()))) return false; } if (GlobusLocations.defaultLocations().getUserCertRequest().exists()) { if (!GlobusLocations .defaultLocations() .getUserCertRequest() .renameTo( Utils.backupFile(GlobusLocations.defaultLocations() .getUserCertRequest()))) return false; } if (GlobusLocations.defaultLocations().getUserCertPKCS12().exists()) { if (!GlobusLocations .defaultLocations() .getUserCertPKCS12() .renameTo( Utils.backupFile(GlobusLocations.defaultLocations() .getUserCertPKCS12()))) return false; } if (!GlobusLocations.defaultLocations().getRenewUserCert() .renameTo(GlobusLocations.defaultLocations().getUserCert())) return false; if (!GlobusLocations.defaultLocations().getRenewUserKey() .renameTo(GlobusLocations.defaultLocations().getUserKey())) return false; if (!GlobusLocations .defaultLocations() .getRenewUserCertRequest() .renameTo( GlobusLocations.defaultLocations().getUserCertRequest())) return false; UserProperty.setProperty("CERT_REQUEST", GlobusLocations .defaultLocations().getUserCertRequest().toString()); UserProperty.setProperty("PRIVATE_KEY", GlobusLocations .defaultLocations().getUserKey().toString()); UserProperty.setProperty("REQUESTED_RENEWED_CERTIFICATE", "no"); return true; } /** * Checks whether the certifcate is ready to download. * * @return true if ready, false if not */ public static boolean readyToDownload() { // TODO make this better if (UserProperty.getProperty("REQUEST_SERIAL") == null || "manual".equals(UserProperty.getProperty("REQUEST_SERIAL"))) return true; return OpenCA.checkStatusOfRequest(UserProperty .getProperty("REQUEST_SERIAL")); // String cert = null; // try { // cert = OpenCA.downloadCertificate(c, o, ou, cn, email); // } catch (Exception e) { // throw new UnableToDownloadCertificateException("openca", e); // } // myLogger.debug("Answer from server: \n" + cert); // if (cert != null && cert.indexOf(Certificate.PEM_HEADER) != -1) { // Certificate newCert = new Certificate(cert); // Certificate localCert = null; // try { // localCert = new // Certificate(GlobusLocations.defaultLocations().getUserCert()); // } catch (Exception e) { // // can't create old cert, asuming this is the new one... // return newCert; // } // // if ( localCert.getEnddate().equals(newCert.getEnddate()) ) { // //myLogger.debug("Your certificate is not ready yet. Please try it again later."); // return null; // } // // this means the certificate is ready // return newCert; } /** * This is just a wrapper of the downloadCertificate method which does all * the checking, parsing of usercer_request.pem ... * * @return the requested and approved Certificate or null if retrieving * fails for some reason. If you want more detailed information why * it failed you have to do the downloadCertificate() steps * manually. */ public static Certificate retrieveDefaultCertificate() { if (GlobusLocations.defaultLocations().userCertExists()) { myLogger.debug("There is already a certificate in the .globus directory. Remove this file if you want to retrieve a new one: " + GlobusLocations.defaultLocations().getUserCert() .toString()); return null; } CertificationRequest cert_req = null; try { cert_req = new CertificationRequest(GlobusLocations .defaultLocations().getUserCertRequest(), GrixProperty.getString("signature.algorithm"), false); } catch (IOException e) { myLogger.debug("Can't read file: " + GlobusLocations.defaultLocations().getUserCertRequest() + ". Please check permissions."); return null; } catch (MissingInformationException e) { myLogger.debug("Can not read the following fields out of the certification request:\n"); for (String info : e.getMissingInformation()) { myLogger.debug(info); } return null; } Certificate cert = null; try { cert = ManageCertificate.downloadCertificate(cert_req.getC(), cert_req.getO(), cert_req.getOu(), cert_req.getCn(), cert_req.getEmail()); } catch (GeneralSecurityException e) { myLogger.debug("Can't convert server answer into a certificate: " + e.getMessage()); return null; } catch (UnableToDownloadCertificateException e) { myLogger.debug("Can't download certificate: " + e.getMessage()); return null; } if (cert == null) { myLogger.debug("Your certificate is not ready yet. Please try it again later."); return null; } myLogger.debug("Successfully downloaded the new certificate"); return cert; } /** * Uploads the certification request using a plugin * * @param cert_req * The [@link CertificationRequest] * @return a String array with: string[0]=short message, string[1]=long * message (e.g. server answer) * @throws UnableToUploadCertificationRequestException * if, for whatever reason, the upload fails */ public static String[] uploadCertificationRequest( CertificationRequest cert_req, boolean hostCert, String name, String email, String department, String telephone) throws UnableToUploadCertificationRequestException { // start uploading myLogger.debug("Trying to upload certificate request..."); // TODO implement plugin architecture try { return OpenCA.uploadCertificationRequest(cert_req, hostCert, name, email, department, telephone); } catch (UnableToUploadCertificationRequestException ue) { throw ue; } catch (Exception e) { throw new UnableToUploadCertificationRequestException("openca", e); } } /** * This creates a certification request and stores it (along with the * private key) in the .globus folder on the user's home directory. It also * stores the DN of the user in the .globus/grix.properties file * * @param ui * The {@link CreateRequestInterface} is the view class that * returns values for the cert-request and informs the user about * the status of the creation * @param hostCert * whether the request is for a hostcert (true) or usercert * (false) * @param requestFile * the file to store the request into, if null, the CoGProperties * default is used * @param the * file to store the private key into, if null, the CoGProperties * default is used */ public static CertificationRequest createAndStoreCertificationRequest( CreateRequestInterface ui, boolean hostCert, int keysize, String signature_algorithm, File requestFile, File keyFile, boolean overwrite) { ui.lockInput(); boolean createdKeyDirectory = false; boolean createdRequestDirectory = false; boolean createdKey = false; String cn = ui.getCN(); if (cn == null || "".equals(cn)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.noCN")); ui.unlockInput(); return null; } String ou = ui.getOU(); if (ou == null || "".equals(ou)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.noOU")); ui.unlockInput(); return null; } String o = ui.getO(); if (o == null || "".equals(o)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.noO")); ui.unlockInput(); return null; } String c = ui.getC(); if (c == null || "".equals(c)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.noC")); ui.unlockInput(); return null; } if (c.length() != 2) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.wrongC")); ui.unlockInput(); return null; } String phone = ui.getPhone(); if (phone == null || "".equals(phone)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.noPhone")); ui.unlockInput(); return null; } String email = ui.getEmail(); if (!Utils.isValidEmail(email)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.invalidEmail")); ui.unlockInput(); return null; } char[] passphrase1 = null; char[] passphrase2 = null; if (!hostCert) { ui.setStatus(messages.getString("status.readingPassphrase")); passphrase1 = ui.getPassphrase(); if (!Utils.isValidPassphrase(passphrase1)) { Arrays.fill(passphrase1, 'x'); ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.invalidPassphrase")); ui.unlockInput(); return null; } passphrase2 = ui.getPassphrase2(); if (!Utils.samePasswords(passphrase1, passphrase2)) { Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.passphrasesNotTheSame")); ui.unlockInput(); return null; } Arrays.fill(passphrase2, 'x'); } ui.setStatus(messages.getString("status.checkingGlobusDirectory")); if (hostCert && requestFile == null) { requestFile = new File(GlobusLocations.defaultLocations() .getGlobusDirectory(), GrixProperty.getString("hostcert_request.pem")); } else if (!hostCert && requestFile == null) { requestFile = GlobusLocations.defaultLocations() .getUserCertRequest(); } if (keyFile == null) { if (hostCert) { keyFile = new File(GlobusLocations.defaultLocations() .getGlobusDirectory(), GrixProperty.getString("hostkey.pem")); } else { keyFile = GlobusLocations.defaultLocations().getUserKey(); } } if (keyFile.equals(requestFile)) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.RequestAndPrivKeyFileSame")); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); ui.unlockInput(); return null; } // check file permissions if (!keyFile.getParentFile().exists()) { if (!keyFile.getParentFile().mkdir()) { ui.clearPassphrases(); ui.setStatus(messages.getString("status.error")); ui.message(messages .getString("message.cannotCreateGlobusDirectory") + " " + keyFile.getParentFile().toString()); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); ui.unlockInput(); return null; } createdKeyDirectory = true; } if (!keyFile.getParentFile().canWrite()) { if (createdKeyDirectory) { keyFile.getParentFile().delete(); } ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); ui.setStatus(messages.getString("status.error")); ui.message(messages .getString("message.globusDirectoryNotWriteable") + " " + keyFile.getParentFile().toString()); ui.unlockInput(); return null; } if (!requestFile.getParentFile().exists()) { if (!keyFile.getParentFile().mkdir()) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); ui.setStatus(messages.getString("status.error")); ui.message(messages .getString("message.cannotCreateGlobusDirectory") + " " + requestFile.getParentFile().toString()); ui.unlockInput(); return null; } createdRequestDirectory = true; } if (!requestFile.getParentFile().canWrite()) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); ui.message(messages .getString("message.globusDirectoryNotWriteable") + " " + requestFile.toString()); ui.unlockInput(); return null; } // check whether key / request already exist if (!overwrite) { if (keyFile.exists()) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdRequestDirectory) requestFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.userKeyExistsAlready") + " " + keyFile.toString()); ui.unlockInput(); return null; } if (requestFile.exists()) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.userCertExistsAlready") + " " + requestFile.toString()); ui.unlockInput(); return null; } } ui.setStatus(messages.getString("status.generatingKeypair")); CertificationRequest cert_req = null; try { cert_req = new CertificationRequest(keysize, signature_algorithm); } catch (GeneralSecurityException e) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.cannotCreateRequest") + ": " + e.getMessage()); ui.unlockInput(); return null; } ui.setStatus(messages.getString("status.readingInformation")); cert_req.setC(c); cert_req.setO(o); cert_req.setOu(ou); cert_req.setCn(cn); cert_req.setEmail(email); try { cert_req.createDN(hostCert); } catch (MissingInformationException e) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); String message = messages .getString("message.cannotCreateRequest.missingInformation" + "\n\n"); for (String emptyField : e.getMissingInformation()) { message = message + emptyField + "\n"; } ui.message(message); ui.unlockInput(); return null; } ui.setStatus(messages.getString("status.generatingRequest")); if (!hostCert) { ui.setStatus(messages.getString("status.encryptingPrivateKey")); try { cert_req.getKeypair().encryptPrivateKey(passphrase1); Arrays.fill(passphrase1, 'x'); ui.setStatus(messages .getString("status.encryptingPrivateKeySuccess")); } catch (GeneralSecurityException e) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); Arrays.fill(passphrase1, 'x'); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.errorEncryptingKey")); ui.unlockInput(); return null; } } ui.setStatus(messages.getString("status.writingPrivateKey")); try { cert_req.getKeypair().getPrivateKey().writeTo(keyFile.toString()); ui.setStatus(messages.getString("status.writingPrivateKeySuccess") + " " + keyFile.toString()); createdKey = true; } catch (IOException e) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.errorWritingKey") + " " + keyFile.toString()); ui.unlockInput(); return null; } ui.setStatus(messages.getString("status.writingCertRequest")); try { cert_req.writeToPEMFile(requestFile); ui.setStatus(messages.getString("status.writingCertRequestSuccess") + " " + requestFile.toString()); } catch (IOException e) { ui.clearPassphrases(); Arrays.fill(passphrase1, 'x'); Arrays.fill(passphrase2, 'x'); if (createdKey) { keyFile.delete(); } if (createdKeyDirectory) keyFile.getParentFile().delete(); if (createdRequestDirectory && !keyFile.getParentFile().equals( requestFile.getParentFile())) requestFile.getParentFile().delete(); if (createdKey) { keyFile.delete(); } ui.setStatus(messages.getString("status.error")); ui.message(messages.getString("message.errorWritingRequest") + " " + requestFile.toString()); ui.unlockInput(); return null; } // writing the location of the request file in the grix.properties file if (!hostCert) { UserProperty.setProperty("CERT_REQUEST", requestFile.toString()); UserProperty.setProperty("PRIVATE_KEY", keyFile.toString()); UserProperty.setProperty("FIRST_NAME", ui.getFirstName()); UserProperty.setProperty("LAST_NAME", ui.getLastName()); UserProperty.setProperty("EMAIL", email); UserProperty.setProperty("ORGANISATION", o); UserProperty.setProperty("ORGANISATION_UNIT", ou); UserProperty.setProperty("COUNTRY", c); UserProperty.setProperty("PHONE", phone); } else { UserProperty .addToList("HOST_CERT_REQUESTS", requestFile.toString()); UserProperty.addToList("HOST_PRIVATE_KEYS", keyFile.toString()); } ui.setStatus(messages.getString("status.success")); ui.message(messages.getString("message.creationCertRequestSuccess") + " " + requestFile.toString() + " & " + keyFile.toString()); ui.unlockInput(); return cert_req; } }