
net.named_data.jndn.encrypt.AccessManagerV2 Maven / Gradle / Ivy
/**
* Copyright (C) 2018 Regents of the University of California.
* @author: Jeff Thompson
* @author: From the NAC library https://github.com/named-data/name-based-access-control/blob/new/src/access-manager.cpp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
* A copy of the GNU Lesser General Public License is in the file COPYING.
*/
package net.named_data.jndn.encrypt;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import net.named_data.jndn.Data;
import net.named_data.jndn.Face;
import net.named_data.jndn.Interest;
import net.named_data.jndn.InterestFilter;
import net.named_data.jndn.Name;
import net.named_data.jndn.OnInterestCallback;
import net.named_data.jndn.OnRegisterFailed;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.encrypt.algo.EncryptAlgorithmType;
import net.named_data.jndn.in_memory_storage.InMemoryStorageRetaining;
import net.named_data.jndn.security.KeyChain;
import net.named_data.jndn.security.KeyType;
import net.named_data.jndn.security.SigningInfo;
import net.named_data.jndn.security.RsaKeyParams;
import net.named_data.jndn.security.SafeBag;
import net.named_data.jndn.security.SecurityException;
import net.named_data.jndn.security.UnrecognizedKeyFormatException;
import net.named_data.jndn.security.certificate.PublicKey;
import net.named_data.jndn.security.pib.Pib;
import net.named_data.jndn.security.pib.PibIdentity;
import net.named_data.jndn.security.pib.PibImpl;
import net.named_data.jndn.security.pib.PibKey;
import net.named_data.jndn.security.tpm.Tpm;
import net.named_data.jndn.security.tpm.TpmBackEnd;
import net.named_data.jndn.security.v2.CertificateV2;
import net.named_data.jndn.util.Common;
/**
* AccessManagerV2 controls the decryption policy by publishing granular
* per-namespace access policies in the form of key encryption
* (KEK, plaintext public) and key decryption (KDK, encrypted private key)
* key pairs. This works with EncryptorV2 and DecryptorV2 using security v2.
* For the meaning of "KDK", etc. see:
* https://github.com/named-data/name-based-access-control/blob/new/docs/spec.rst
*/
public class AccessManagerV2 {
/**
* Create an AccessManagerV2 to serve the NAC public key for other data
* producers to fetch, and to serve encrypted versions of the private keys
* (as safe bags) for authorized consumers to fetch.
*
* KEK and KDK naming:
*
* [identity]/NAC/[dataset]/KEK /[key-id] (== KEK, public key)
*
* [identity]/NAC/[dataset]/KDK/[key-id] /ENCRYPTED-BY/[user]/KEY/[key-id] (== KDK, encrypted private key)
*
* \_____________ ______________/
* \/
* registered with NFD
*
* @param identity The data owner's namespace identity. (This will be used to
* sign the KEK and KDK.)
* @param dataset The name of dataset that this manager is controlling.
* @param keyChain The KeyChain used to sign Data packets.
* @param face The Face for calling registerPrefix that will be used to
* publish the KEK and KDK Data packets.
*/
public AccessManagerV2
(PibIdentity identity, Name dataset, KeyChain keyChain, Face face)
throws Tpm.Error, TpmBackEnd.Error, PibImpl.Error, Pib.Error, KeyChain.Error,
EncodingException, IOException, SecurityException
{
identity_ = identity;
keyChain_ = keyChain;
face_ = face;
// The NAC identity is: /NAC/
// Generate the NAC key.
PibIdentity nacIdentity = keyChain_.createIdentityV2
(new Name(identity.getName())
.append(EncryptorV2.NAME_COMPONENT_NAC).append(dataset),
new RsaKeyParams());
nacKey_ = nacIdentity.getDefaultKey();
if (nacKey_.getKeyType() != KeyType.RSA) {
logger_.log(Level.INFO,
"Cannot re-use existing KEK/KDK pair, as it is not an RSA key, regenerating");
nacKey_ = keyChain_.createKey(nacIdentity, new RsaKeyParams());
}
Name.Component nacKeyId = nacKey_.getName().get(-1);
Name kekPrefix = new Name(nacKey_.getIdentityName())
.append(EncryptorV2.NAME_COMPONENT_KEK);
Data kekData = new Data(nacKey_.getDefaultCertificate());
kekData.setName(new Name(kekPrefix).append(nacKeyId));
kekData.getMetaInfo().setFreshnessPeriod(DEFAULT_KEK_FRESHNESS_PERIOD_MS);
keyChain_.sign(kekData, new SigningInfo(identity_));
// kek looks like a cert, but doesn't have ValidityPeriod
storage_.insert(kekData);
OnInterestCallback serveFromStorage = new OnInterestCallback() {
public void onInterest
(Name prefix, Interest interest, Face face, long interestFilterId,
InterestFilter filter) {
Data data = storage_.find(interest);
if (data != null) {
logger_.log(Level.INFO, "Serving {0} from in-memory-storage",
data.getName());
try {
face.putData(data);
} catch (Throwable ex) {
logger_.log(Level.SEVERE, "AccessManagerV2: Error in Face.putData", ex);
}
}
else {
logger_.log(Level.INFO, "Didn't find data for {0}", interest.getName());
// TODO: Send NACK?
}
}
};
OnRegisterFailed registerFailed = new OnRegisterFailed() {
public void onRegisterFailed(Name prefix) {
logger_.log(Level.SEVERE,
"AccessManagerV2: Failed to register prefix {0}", prefix.toUri());
}
};
kekRegisteredPrefixId_ = face_.registerPrefix
(kekPrefix, serveFromStorage, registerFailed);
Name kdkPrefix = new Name(nacKey_.getIdentityName())
.append(EncryptorV2.NAME_COMPONENT_KDK).append(nacKeyId);
kdkRegisteredPrefixId_ = face_.registerPrefix
(kdkPrefix, serveFromStorage, registerFailed);
}
public final void
shutdown()
{
face_.unsetInterestFilter(kekRegisteredPrefixId_);
face_.unsetInterestFilter(kdkRegisteredPrefixId_);
}
/**
* Authorize a member identified by memberCertificate to decrypt data under
* the policy.
* @param memberCertificate The certificate that identifies the member to
* authorize.
* @return The published KDK Data packet.
*/
public final Data
addMember(CertificateV2 memberCertificate)
throws Pib.Error, PibImpl.Error, UnrecognizedKeyFormatException,
EncodingException, TpmBackEnd.Error, KeyChain.Error,
InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
CertificateV2.Error
{
Name kdkName = new Name(nacKey_.getIdentityName());
kdkName
.append(EncryptorV2.NAME_COMPONENT_KDK)
.append(nacKey_.getName().get(-1)) // key-id
.append(EncryptorV2.NAME_COMPONENT_ENCRYPTED_BY)
.append(memberCertificate.getKeyName());
final int secretLength = 32;
byte[] secret = new byte[secretLength];
Common.getRandom().nextBytes(secret);
// To be compatible with OpenSSL which uses a null-terminated string,
// replace each 0 with 1. And to be compatible with the Java security
// library interprets the secret as a char array converted to UTF8, limit
// each byte to the ASCII range 1 to 127.
for (int i = 0; i < secretLength; ++i) {
if (secret[i] == 0)
secret[i] = 1;
secret[i] &= 0x7f;
}
SafeBag kdkSafeBag = keyChain_.exportSafeBag
(nacKey_.getDefaultCertificate(), ByteBuffer.wrap(secret));
PublicKey memberKey = new PublicKey(memberCertificate.getPublicKey());
EncryptedContent encryptedContent = new EncryptedContent();
encryptedContent.setPayload(kdkSafeBag.wireEncode());
encryptedContent.setPayloadKey(memberKey.encrypt
(secret, EncryptAlgorithmType.RsaOaep));
Data kdkData = new Data(kdkName);
kdkData.setContent(encryptedContent.wireEncodeV2());
// FreshnessPeriod can serve as a soft access control for revoking access
kdkData.getMetaInfo().setFreshnessPeriod(DEFAULT_KDK_FRESHNESS_PERIOD_MS);
keyChain_.sign(kdkData, new SigningInfo(identity_));
storage_.insert(kdkData);
return kdkData;
}
/**
* The number of packets stored in in-memory storage.
* @return The number of packets.
*/
public final int
size() { return storage_.size(); }
/**
* Get the the storage cache, which should only be used for testing.
* @return The storage cache.
*/
public final HashMap
getCache_() { return storage_.getCache_(); }
private final PibIdentity identity_;
private PibKey nacKey_;
private final KeyChain keyChain_;
private final Face face_;
// storage_ is for the KEK and KDKs.
private final InMemoryStorageRetaining storage_ =
new InMemoryStorageRetaining();
private final long kekRegisteredPrefixId_;
private final long kdkRegisteredPrefixId_;
private static final Logger logger_ = Logger.getLogger(AccessManagerV2.class.getName());
private static final double DEFAULT_KEK_FRESHNESS_PERIOD_MS = 3600 * 1000.0;
private static final double DEFAULT_KDK_FRESHNESS_PERIOD_MS = 3600 * 1000.0;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy