/* * Part of the CCNx Java Library. * * Copyright (C) 2008-2012 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library 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 * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.protocol; import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; import java.net.URISyntaxException; import java.security.Key; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.logging.Level; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.encoding.XMLEncodable; import org.ccnx.ccn.impl.encoding.XMLEncoder; import org.ccnx.ccn.impl.security.crypto.CCNDigestHelper; import org.ccnx.ccn.impl.support.DataUtils; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.io.content.ContentDecodingException; import org.ccnx.ccn.io.content.ContentEncodingException; import org.ccnx.ccn.profiles.security.KeyProfile; import org.ccnx.ccn.protocol.ContentName.ComponentProvider; /** * Wrapper class around the digest of public keys used as a publisher identifier in ContentObjects. * The digest algorithm used to compute publisherKeyIDs is specified by the CCNx protocol, and changes * rarely with the CCNx protocol version (for example, now it is SHA-256, it will shift to SHA-3). * As this is a convenience filtering mechanism to help consumers retrieve content signed by the * specific publishers they want, failure in the digest algorithm used will only decrease the efficiency * of this filtering method, or potentially help an attacker prevent a user from finding legitimate * content. It will never cause the consumer to accept invalid content. We therefore opted to make * this one of the small number of fixed-algorithm components in CCNx, to vastly simplify what is * required of network routers that have to check and interpret this field. * * To generate a PublisherPublicKeyDigest, we use the digest of the encoded PublicKey (the encoded SubjectPublicKeyInfo). */ public class PublisherPublicKeyDigest extends GenericXMLEncodable implements XMLEncodable, Comparable<PublisherPublicKeyDigest>, Serializable, ComponentProvider { private static final long serialVersionUID = -1636681985247106846L; protected byte [] _publisherPublicKeyDigest; /** * Create a PublisherPublicKeyDigest from a PublicKey * @param key the key */ public PublisherPublicKeyDigest(Key key) { _publisherPublicKeyDigest = PublisherID.generatePublicKeyDigest(key); } /** * Create a PublisherPublicKeyDigest from an existing digest * @param publisherPublicKeyDigest the key */ public PublisherPublicKeyDigest(byte [] publisherPublicKeyDigest) { // Alas, Arrays.copyOf doesn't exist in 1.5, and we'd like // to be mostly 1.5 compatible for now... // _publisherPublicKeyDigest = Arrays.copyOf(publisherID, PUBLISHER_ID_LEN); if (null == publisherPublicKeyDigest) { throw new IllegalArgumentException("This is not a valid publisher public key digest!"); } _publisherPublicKeyDigest = new byte[PublisherID.PUBLISHER_ID_LEN]; int len = publisherPublicKeyDigest.length; if (len > PublisherID.PUBLISHER_ID_LEN) { len = PublisherID.PUBLISHER_ID_LEN; Log.warning("Truncating PublisherPublicKeyDigest to {0} bytes", len); } System.arraycopy(publisherPublicKeyDigest, 0, _publisherPublicKeyDigest, (PublisherID.PUBLISHER_ID_LEN-len), len); } /** * Expects the equivalent of publisherKeyID.toString * @param publisherPublicKeyDigest the string representation of the digest. * @throws IOException */ public PublisherPublicKeyDigest(String publisherPublicKeyDigest) throws IOException { _publisherPublicKeyDigest = DataUtils.base64Decode(publisherPublicKeyDigest.getBytes()); if (_publisherPublicKeyDigest.length != PublisherID.PUBLISHER_ID_LEN) { throw new IOException("Not a valid base64Binary encoding of a PublisherPublicKeyDigest" + publisherPublicKeyDigest); } } /** * Parses a URI-encoded key ID, with an optional keyid: command marker. * @throws URISyntaxException * @throws Component.DotDot */ public static PublisherPublicKeyDigest fromURIEncoded(String uriEncoded) throws Component.DotDot, URISyntaxException { byte [] encodedBytes = Component.parseURI(uriEncoded); if (KeyProfile.KEY_NAME_COMPONENT_MARKER.isMarker(encodedBytes)) { return new PublisherPublicKeyDigest(KeyProfile.getKeyIDFromNameComponent(encodedBytes)); } // No marker return new PublisherPublicKeyDigest(encodedBytes); } /** * Gets a PublisherPublicKeyDigest for the public key in an X509Certificate. */ public static PublisherPublicKeyDigest fromCertificate(X509Certificate certificate) { return new PublisherPublicKeyDigest(certificate.getPublicKey()); } /** * For use by decoders */ public PublisherPublicKeyDigest() {} /** * Return the digest * @return the digest itself */ public byte [] digest() { return _publisherPublicKeyDigest; } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + Arrays.hashCode(_publisherPublicKeyDigest); return result; } public boolean equals(PublisherID publisher) { if (PublisherID.PublisherType.KEY != publisher.type()) return false; if (!Arrays.equals(digest(), publisher.id())) return false; return true; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (PublisherID.class == obj.getClass()) return obj.equals(this); // put complex implementation in one place if (getClass() != obj.getClass()) return false; final PublisherPublicKeyDigest other = (PublisherPublicKeyDigest) obj; if (!Arrays.equals(_publisherPublicKeyDigest, other._publisherPublicKeyDigest)) return false; return true; } @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { // The format of a publisher ID is an octet string of the correct length. _publisherPublicKeyDigest = decoder.readBinaryElement(getElementLabel()); if (null == _publisherPublicKeyDigest) { throw new ContentDecodingException("Cannot parse publisher key digest."); } if (_publisherPublicKeyDigest.length != PublisherID.PUBLISHER_ID_LEN) { if (Log.isLoggable(Level.WARNING)) { Log.warning("Wrong length for PublisherPublicKeyDigest: {0}", CCNDigestHelper.printBytes(_publisherPublicKeyDigest, 32)); } _publisherPublicKeyDigest = new PublisherPublicKeyDigest(_publisherPublicKeyDigest).digest(); } } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeElement(getElementLabel(), digest()); } @Override public long getElementLabel() { return CCNProtocolDTags.PublisherPublicKeyDigest; } @Override public boolean validate() { return (null != digest()); } /** * Implement Comparable * @param o the other thing to compare to * @return -1, 0 or 1 depending on whether we are before, equal to or lexicographically after o */ public int compareTo(PublisherPublicKeyDigest o) { int result = DataUtils.compare(this.digest(), o.digest()); return result; } @Override /** * The string representation is base64Binary (RFC 2045, section 6.8). */ public String toString() { return DataUtils.base64Encode(digest(), PublisherID.PUBLISHER_ID_LEN*2); } /** * A short string representation of the key. Really want PGP fingerprints. * @return */ public String shortFingerprint() { long lf = new BigInteger(1, _publisherPublicKeyDigest).longValue(); return Long.toHexString(lf); } /** * Implements the {@link ComponentProvider} interface, allowing a {@link PublisherPublicKeyDigest} * to be included in a ContentName constructor, yielding a PublisherPublicKey ID as a name component. */ public byte[] getComponent() { return KeyProfile.keyIDToNameComponent(this); } }