/* * * Panbox - encryption for cloud storage * Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG * * This program 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 3 of the License, or * (at your option) any later version. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Additonally, third party code may be provided with notices and open source * licenses from communities and third parties that govern the use of those * portions, and any licenses granted hereunder do not alter any rights and * obligations you may have under such open source licenses, however, the * disclaimer of warranty and limitation of liability provisions of the GPLv3 * will apply to all the product. * */ package org.panbox.core.identitymgmt; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.security.PublicKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import org.apache.log4j.Logger; import org.panbox.core.crypto.CryptCore; import org.panbox.core.crypto.KeyConstants; import org.panbox.core.identitymgmt.exceptions.ContactExistsException; import ezvcard.Ezvcard; import ezvcard.VCard; import ezvcard.io.text.VCardReader; import ezvcard.property.Email; import ezvcard.property.Key; import ezvcard.property.RawProperty; import ezvcard.property.Role; import ezvcard.property.StructuredName; public abstract class AbstractAddressbookManager { private static final String PANBOX_CP_EXTENSION = "X-PanboxCP"; private static final Logger logger = Logger.getLogger("org.panbox.core"); public abstract void init(); public abstract void loadContacts(AbstractIdentity id); public abstract void persistContacts(Collection<PanboxContact> contacts, int identityKey); /** * Writes all contacts into a vCard file represented by vcardFile * * @param id * Identity to export contacts from * @param vcardFile * File to store vcard entries * @return true if export is successful, otherwise false */ public static boolean exportContacts(Collection<VCard> vcards, File vcardFile) { try { Ezvcard.write(vcards).go(vcardFile); } catch (IOException e) { logger.error( "Could not export contacts to file: " + vcardFile.getAbsolutePath() + File.separator + vcardFile.getName(), e); return false; } return true; } public List<PanboxContact> importContacts(AbstractIdentity id, VCard[] vcards, boolean authVerified) throws ContactExistsException { LinkedList<PanboxContact> importedContacts = new LinkedList<PanboxContact>(); if (vcards != null) { IAddressbook addressbook = id.getAddressbook(); LinkedList<PanboxContact> existingContacts = new LinkedList<PanboxContact>(); for (VCard vc : vcards) { PanboxContact curContact = vcard2Contact(vc, authVerified); PublicKey pk = curContact.getPublicKeySign(); PanboxContact refContact = addressbook .getContactBySignaturePubKey(pk); // if contact does not exist create it, otherwise update it if (refContact == null) { // don't import the contact if it has the same public // sign/enc key as my own identity if (Arrays.equals(id.getCertSign().getPublicKey() .getEncoded(), pk.getEncoded()) || (Arrays.equals(id.getCertEnc().getPublicKey() .getEncoded(), curContact.getPublicKeyEnc() .getEncoded()))) { logger.warn("VCard entry matched identity, skipping..."); continue; } addressbook.addContact(curContact); // to return imported contacts importedContacts.add(curContact); } else { // if we have this contact in our addressbook as untrusted // and now got a verified trusted contact -> update // trust level if (authVerified && (curContact.getTrustLevel() == PanboxContact.TRUSTED_CONTACT)) { refContact.setTrustLevel(PanboxContact.TRUSTED_CONTACT); } existingContacts.add(curContact); } } if (!existingContacts.isEmpty()) { throw new ContactExistsException( "One or more contacts already existed in the addressbook.", existingContacts); } } return importedContacts; } public List<PanboxContact> importContacts(AbstractIdentity id, File vcardFile, boolean authVerified) throws ContactExistsException { return importContacts(id, readVCardFile(vcardFile), authVerified); } public List<PanboxContact> importContacts(AbstractIdentity id, byte[] rawData, boolean authVerified) throws ContactExistsException { return importContacts(id, readVCardBytes(rawData), authVerified); } public static VCard[] readVCardBytes(byte[] rawData) { ArrayList<VCard> vcards = new ArrayList<VCard>(); VCardReader vcr = null; try { vcr = new VCardReader(new ByteArrayInputStream(rawData)); VCard vc = null; while ((vc = vcr.readNext()) != null) { vcards.add(vc); } return vcards.toArray(new VCard[vcards.size()]); } catch (IOException e) { logger.error("Error reading VCard byte[] data", e); } finally { if (vcr != null) { try { vcr.close(); } catch (IOException e) { // do nothing } } } return null; } public static VCard[] readVCardFile(File vcardFile) { ArrayList<VCard> vcards = new ArrayList<VCard>(); VCardReader vcr = null; try { vcr = new VCardReader(vcardFile); VCard vc = null; while ((vc = vcr.readNext()) != null) { vcards.add(vc); } return vcards.toArray(new VCard[vcards.size()]); } catch (IOException e) { logger.error( "Error reading VCard file " + vcardFile.getAbsolutePath(), e); } finally { if (vcr != null) { try { vcr.close(); } catch (IOException e) { // do nothing } } } return null; } /** * Imports contacts from a given vcard file vcardFile to the addressbook of * the identity id * * @param id * - Identity to add contacts to * @param vcardFile * - vcard file containing the contacts * @return true if successful * @throws ContactExistsException * - with list of contacts that have been skipped because they * already exist */ public static PanboxContact vcard2Contact(VCard vc, boolean authVerified) { // we only support ONE email address per contact // (first one in list)! String email = vc.getEmails().get(0).getValue(); PanboxContact ret = new PanboxContact(); if (authVerified) { // PersonRole role = getRoleFromVCard(vc); // if (role == PersonRole.IDENTITY) { // // always trust identity, if verified // ret.setTrustLevel(PanboxContact.TRUSTED_CONTACT); // } else if ((role == PersonRole.CONTACT) // && (getTrustLevelFromVCard(vc) == PanboxContact.TRUSTED_CONTACT)) // { // // transitive trust for contacts, if verified // ret.setTrustLevel(PanboxContact.TRUSTED_CONTACT); // } else { // ret.setTrustLevel(PanboxContact.UNTRUSTED_CONTACT); // } // for now, the trust-flag should *only* indicate if the user has // verified this import ret.setTrustLevel(PanboxContact.TRUSTED_CONTACT); } else { // never trust anything unverified ret.setTrustLevel(PanboxContact.UNTRUSTED_CONTACT); } ret.setEmail(email); StructuredName sn = vc.getStructuredName(); ret.setName(sn.getFamily()); ret.setFirstName(sn.getGiven()); ret.setCertEnc(getPublicEncCertFromVCard(vc)); ret.setCertSign(getPublicSigCertFromVCard(vc)); List<RawProperty> cps = vc.getExtendedProperties(PANBOX_CP_EXTENSION); for (RawProperty cp : cps) { CloudProviderInfo cpi = new CloudProviderInfo( cp.getParameter("TYPE"), cp.getParameter("USERNAME")); ret.addCloudProvider(cpi); } return ret; } /** * Converts a given contact into a Vcard instance * * @param c * - contact * @return the vcard object of the contact, or null if an error occurs */ public static VCard contact2VCard(IPerson c) { VCard vcard = new VCard(); StructuredName n = new StructuredName(); n.setFamily(c.getName()); n.setGiven(c.getFirstName()); vcard.setStructuredName(n); vcard.setFormattedName(c.getFirstName() + " " + c.getName()); Email email = new Email(c.getEmail()); vcard.setProperty(Email.class, email); // this is a contact, not an identity if (c instanceof PanboxContact) { vcard.addRole("contact"); PanboxContact pc = (PanboxContact) c; vcard.addExtendedProperty(KeyConstants.AB_CONTACT_TRUSTLEVEL, String.valueOf(pc.getTrustLevel())); } else if (c instanceof AbstractIdentity) { vcard.addRole("identity"); } Key key = new Key(); try { key.setData(c.getCertEnc().getEncoded(), KeyConstants.AB_CONTACT_PK_ENC); } catch (CertificateEncodingException e) { logger.error( "Cannot obtain encoded version of user's encryption certificate", e); return null; } vcard.addKey(key); try { key = new Key(c.getCertSign().getEncoded(), KeyConstants.AB_CONTACT_PK_SIG); } catch (CertificateEncodingException e) { logger.error( "Cannot obtain encoded version of user's signature certificate", e); return null; } vcard.addKey(key); for (Entry<String, CloudProviderInfo> cpiEntry : c.getCloudProviders() .entrySet()) { RawProperty rp = vcard.addExtendedProperty(PANBOX_CP_EXTENSION, ""); rp.addParameter("TYPE", cpiEntry.getKey()); rp.addParameter("USERNAME", cpiEntry.getValue().getUsername()); } return vcard; } public static X509Certificate getPublicSigCertFromVCard(VCard vc) { for (Key k : vc.getKeys()) { if (k.getContentType() == KeyConstants.AB_CONTACT_PK_SIG) { return CryptCore.createCertificateFromBytes(k.getData()); } } return null; } public static X509Certificate getPublicEncCertFromVCard(VCard vc) { for (Key k : vc.getKeys()) { if (k.getContentType() == KeyConstants.AB_CONTACT_PK_ENC) { return CryptCore.createCertificateFromBytes(k.getData()); } } return null; } public static int getTrustLevelFromVCard(VCard vc) { RawProperty rawProperty = vc .getExtendedProperty(KeyConstants.AB_CONTACT_TRUSTLEVEL); return Integer.valueOf(rawProperty.getValue()); } public static PersonRole getRoleFromVCard(VCard vc) { PersonRole role = PersonRole.NONE; List<Role> roles = vc.getRoles(); if ((null != roles) && (roles.size() == 1)) { // we assume there is be only one role defined per vcard Role ref = roles.get(0); if (ref.getValue().equals("contact")) { role = PersonRole.CONTACT; } else if (ref.getValue().equals("identity")) { role = PersonRole.IDENTITY; } else { role = PersonRole.NONE; } } else if ((null != roles) && roles.size() > 1) { logger.error("VCard contains more than one role!"); } return role; } public static List<PanboxContact> vcardBytes2Contacts(byte[] rawBytes, boolean verified) { LinkedList<PanboxContact> contacts = new LinkedList<PanboxContact>(); VCard[] vcards = readVCardBytes(rawBytes); for (VCard vc : vcards) { contacts.add(vcard2Contact(vc, verified)); } return contacts; } }