SOLR-7126: Utility classes for Crypto

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1664116 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2015-03-04 19:12:53 +00:00
parent 4becc8868c
commit 82e72c8d19
3 changed files with 253 additions and 0 deletions

View File

@ -19,6 +19,10 @@ package org.apache.solr.cloud;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
@ -26,10 +30,12 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -99,4 +105,27 @@ public class CloudUtil {
((ZkSolrResourceLoader) loader).getConfigSetZkPath() + "/" :
loader.getConfigDir();
}
/**Read the list of public keys from ZK
*/
public static Map<String, byte[]> getTrustedKeys(SolrZkClient zk){
Map<String,byte[]> result = new HashMap<>();
try {
List<String> children = zk.getChildren("/keys", null, true);
for (String key : children) {
result.put(key, zk.getData("/keys/"+key,null,null,true));
}
} catch (KeeperException.NoNodeException e) {
log.warn("Error fetching key names");
return Collections.EMPTY_MAP;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SolrException(ErrorCode.SERVER_ERROR,"Unable to read crypto keys",e );
} catch (KeeperException e) {
throw new SolrException(ErrorCode.SERVER_ERROR,"Unable to read crypto keys",e );
}
return result;
}
}

View File

@ -0,0 +1,107 @@
package org.apache.solr.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import org.apache.solr.common.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**A utility class to verify signatures
*
*/
public final class CryptoKeys {
private static final Logger log = LoggerFactory.getLogger(CryptoKeys.class);
private final Map<String, PublicKey> keys;
public CryptoKeys(Map<String, byte[]> trustedKeys) throws Exception {
HashMap<String, PublicKey> m = new HashMap<>();
for (Map.Entry<String, byte[]> e : trustedKeys.entrySet()) {
m.put(e.getKey(), getX509PublicKey(e.getValue()));
}
this.keys = m;
}
/**
* Try with all signatures and return the name of the signature that matched
*/
public String verify(String sig, byte[] data) {
for (Map.Entry<String, PublicKey> entry : keys.entrySet()) {
boolean verified;
try {
verified = CryptoKeys.verify(entry.getValue(), Base64.base64ToByteArray(sig), ByteBuffer.wrap(data));
log.info("verified {} ", verified);
if (verified) return entry.getKey();
} catch (Exception e) {
log.info("NOT verified ");
}
}
return null;
}
/**
* Create PublicKey from a .DER file
*/
public static PublicKey getX509PublicKey(byte[] buf)
throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(buf);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
/**
* Verify the signature of a file
*
* @param publicKey the public key used to sign this
* @param sig the signature
* @param data The data tha is signed
*/
public static boolean verify(PublicKey publicKey, byte[] sig, ByteBuffer data) throws InvalidKeyException, SignatureException {
Signature signature = null;
try {
signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(data);
boolean verify = signature.verify(sig);
return verify;
} catch (NoSuchAlgorithmException e) {
//will not happen
}
return false;
}
}

View File

@ -0,0 +1,117 @@
package org.apache.solr.cloud;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.util.CryptoKeys;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestCryptoKeys extends AbstractFullDistribZkTestBase {
private static final Logger logger = LoggerFactory.getLogger(TestCryptoKeys.class);
@Override
public void distribSetUp() throws Exception {
super.distribSetUp();
System.setProperty("numShards", Integer.toString(sliceCount));
}
public TestCryptoKeys() {
super();
sliceCount = 1;
}
@Test
public void test() throws Exception {
String pk1sig = "G8LEW7uJ1is81Aqqfl3Sld3qDtOxPuVFeTLJHFJWecgDvUkmJNFXmf7nkHOVlXnDWahp1vqZf0W02VHXg37lBw==";
String pk2sig = "pCyBQycB/0YvLVZfKLDIIqG1tFwM/awqzkp2QNpO7R3ThTqmmrj11wEJFDRLkY79efuFuQPHt40EE7jrOKoj9jLNELsfEqvU3jw9sZKiDONY+rV9Bj9QPeW8Pgt+F9Y1";
String wrongKeySig = "xTk2hTipfpb+J5s4x3YZGOXkmHWtnJz05Vvd8RTm/Q1fbQVszR7vMk6dQ1URxX08fcg4HvxOo8g9bG2TSMOGjg==";
String result = null;
CryptoKeys cryptoKeys = null;
SolrZkClient zk = getCommonCloudSolrClient().getZkStateReader().getZkClient();
cryptoKeys = new CryptoKeys(CloudUtil.getTrustedKeys(zk));
byte[] samplefile = readFile("samplefile.bin");
//there are no keys
result = cryptoKeys.verify( pk1sig,samplefile);
assertNull(result);
zk.makePath("/keys", true);
createNode(zk, "pubk1.der");
createNode(zk, "pubk2.der");
Map<String, byte[]> trustedKeys = CloudUtil.getTrustedKeys(zk);
cryptoKeys = new CryptoKeys(trustedKeys);
result = cryptoKeys.verify(pk2sig, samplefile);
assertEquals("pubk2.der", result);
result = cryptoKeys.verify(pk1sig, samplefile);
assertEquals("pubk1.der", result);
try {
result = cryptoKeys.verify(wrongKeySig,samplefile);
assertNull(result);
} catch (Exception e) {
//pass
}
try {
result = cryptoKeys.verify( "SGVsbG8gV29ybGQhCg==", samplefile);
assertNull(result);
} catch (Exception e) {
//pass
}
}
private void createNode(SolrZkClient zk, String fname) throws IOException, KeeperException, InterruptedException {
byte[] buf = readFile(fname);
zk.create("/keys/" + fname, buf, CreateMode.PERSISTENT, true);
}
private byte[] readFile(String fname) throws IOException {
byte[] buf = null;
try (FileInputStream fis = new FileInputStream(getFile("cryptokeys/" + fname))) {
buf = new byte[fis.available()];
fis.read(buf);
}
return buf;
}
}