区块链 加密解密公共方法提供
This commit is contained in:
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.exception;
|
||||||
|
|
||||||
|
public class BaseException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BaseException(String message, Throwable parent) {
|
||||||
|
super(message, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 DTCC, Fujitsu Australia Software Technology - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.exception;
|
||||||
|
|
||||||
|
public class CryptoException extends BaseException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public CryptoException(String message, Exception parent) {
|
||||||
|
super(message, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CryptoException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 DTCC, Fujitsu Australia Software Technology - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.exception;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Exception that is thrown from Chaincode
|
||||||
|
*/
|
||||||
|
public class InvalidArgumentException extends BaseException {
|
||||||
|
private static final long serialVersionUID = -6094512275378074427L;
|
||||||
|
|
||||||
|
public InvalidArgumentException(String message, Exception parent) {
|
||||||
|
super(message, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidArgumentException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidArgumentException(Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,607 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, 2017 IBM, DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.helper;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config allows for a global config of the toolkit. Central location for all
|
||||||
|
* toolkit configuration defaults. Has a local config file that can override any
|
||||||
|
* property defaults. Config file can be relocated via a system property
|
||||||
|
* "org.hyperledger.fabric.sdk.configuration". Any property can be overridden
|
||||||
|
* with environment variable and then overridden
|
||||||
|
* with a java system property. Property hierarchy goes System property
|
||||||
|
* overrides environment variable which overrides config file for default values specified here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
private static final Log logger = LogFactory.getLog(Config.class);
|
||||||
|
|
||||||
|
private static final String DEFAULT_CONFIG = "config.properties";
|
||||||
|
public static final String ORG_HYPERLEDGER_FABRIC_SDK_CONFIGURATION = "org.hyperledger.fabric.sdk.configuration";
|
||||||
|
|
||||||
|
private static final String DEFAULT_NULL = "\0DEFAULT_NULL\0".intern(); // Used to set value to NULL since null won't work.
|
||||||
|
/**
|
||||||
|
* Timeout settings
|
||||||
|
**/
|
||||||
|
public static final String PROPOSAL_WAIT_TIME = "org.hyperledger.fabric.sdk.proposal.wait.time";
|
||||||
|
public static final String CHANNEL_CONFIG_WAIT_TIME = "org.hyperledger.fabric.sdk.channelconfig.wait_time";
|
||||||
|
public static final String TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME = "org.hyperledger.fabric.sdk.client.transaction_cleanup_up_timeout_wait_time";
|
||||||
|
public static final String ORDERER_RETRY_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer_retry.wait_time";
|
||||||
|
public static final String ORDERER_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs";
|
||||||
|
public static final String PEER_EVENT_REGISTRATION_WAIT_TIME = "org.hyperledger.fabric.sdk.peer.eventRegistration.wait_time";
|
||||||
|
public static final String PEER_EVENT_RETRY_WAIT_TIME = "org.hyperledger.fabric.sdk.peer.retry_wait_time";
|
||||||
|
public static final String PEER_EVENT_RECONNECTION_WARNING_RATE = "org.hyperledger.fabric.sdk.peer.reconnection_warning_rate";
|
||||||
|
public static final String GENESISBLOCK_WAIT_TIME = "org.hyperledger.fabric.sdk.channel.genesisblock_wait_time";
|
||||||
|
/**
|
||||||
|
* Crypto configuration settings -- settings should not be changed.
|
||||||
|
**/
|
||||||
|
public static final String DEFAULT_CRYPTO_SUITE_FACTORY = "org.hyperledger.fabric.sdk.crypto.default_crypto_suite_factory";
|
||||||
|
public static final String SECURITY_LEVEL = "org.hyperledger.fabric.sdk.security_level";
|
||||||
|
public static final String SECURITY_PROVIDER_CLASS_NAME = "org.hyperledger.fabric.sdk.security_provider_class_name";
|
||||||
|
public static final String SECURITY_CURVE_MAPPING = "org.hyperledger.fabric.sdk.security_curve_mapping";
|
||||||
|
public static final String HASH_ALGORITHM = "org.hyperledger.fabric.sdk.hash_algorithm";
|
||||||
|
public static final String ASYMMETRIC_KEY_TYPE = "org.hyperledger.fabric.sdk.crypto.asymmetric_key_type";
|
||||||
|
public static final String CERTIFICATE_FORMAT = "org.hyperledger.fabric.sdk.crypto.certificate_format";
|
||||||
|
public static final String SIGNATURE_ALGORITHM = "org.hyperledger.fabric.sdk.crypto.default_signature_algorithm";
|
||||||
|
/**
|
||||||
|
* Logging settings
|
||||||
|
**/
|
||||||
|
public static final String MAX_LOG_STRING_LENGTH = "org.hyperledger.fabric.sdk.log.stringlengthmax";
|
||||||
|
public static final String EXTRALOGLEVEL = "org.hyperledger.fabric.sdk.log.extraloglevel"; // ORG_HYPERLEDGER_FABRIC_SDK_LOG_EXTRALOGLEVEL
|
||||||
|
public static final String LOGGERLEVEL = "org.hyperledger.fabric.sdk.loglevel"; // ORG_HYPERLEDGER_FABRIC_SDK_LOGLEVEL=TRACE,DEBUG
|
||||||
|
public static final String DIAGNOTISTIC_FILE_DIRECTORY = "org.hyperledger.fabric.sdk.diagnosticFileDir"; //ORG_HYPERLEDGER_FABRIC_SDK_DIAGNOSTICFILEDIR
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connections settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static final String CONN_SSL_PROVIDER = "org.hyperledger.fabric.sdk.connections.ssl.sslProvider";
|
||||||
|
public static final String CONN_SSL_NEGTYPE = "org.hyperledger.fabric.sdk.connections.ssl.negotiationType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default HFClient thread executor settings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static final String CLIENT_THREAD_EXECUTOR_COREPOOLSIZE = "org.hyperledger.fabric.sdk.client.thread_executor_corepoolsize";
|
||||||
|
public static final String CLIENT_THREAD_EXECUTOR_MAXIMUMPOOLSIZE = "org.hyperledger.fabric.sdk.client.thread_executor_maximumpoolsize";
|
||||||
|
public static final String CLIENT_THREAD_EXECUTOR_KEEPALIVETIME = "org.hyperledger.fabric.sdk.client.thread_executor_keepalivetime";
|
||||||
|
public static final String CLIENT_THREAD_EXECUTOR_KEEPALIVETIMEUNIT = "org.hyperledger.fabric.sdk.client.thread_executor_keepalivetimeunit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miscellaneous settings
|
||||||
|
**/
|
||||||
|
public static final String PROPOSAL_CONSISTENCY_VALIDATION = "org.hyperledger.fabric.sdk.proposal.consistency_validation";
|
||||||
|
|
||||||
|
public static final String SERVICE_DISCOVER_FREQ_SECONDS = "org.hyperledger.fabric.sdk.service_discovery.frequency_sec";
|
||||||
|
public static final String SERVICE_DISCOVER_WAIT_TIME = "org.hyperledger.fabric.sdk.service_discovery.discovery_wait_time";
|
||||||
|
public static final String SERVICE_DISCOVER_AS_LOCALHOST = "org.hyperledger.fabric.sdk.service_discovery.as_localhost";
|
||||||
|
|
||||||
|
public static final String LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN = "org.hyperledger.fabric.sdk.lifecycle.chaincode_endorsement_plugin"; //ORG_HYPERLEDGER_FABRIC_SDK_LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN
|
||||||
|
|
||||||
|
public static final String LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN = "org.hyperledger.fabric.sdk.lifecycle.chaincode_validation_plugin"; //ORG_HYPERLEDGER_FABRIC_SDK_LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN
|
||||||
|
public static final String LIFECYCLE_INITREQUIREDDEFAULT = "org.hyperledger.fabric.sdk.lifecycle.initRequiredDefault"; //ORG_HYPERLEDGER_FABRIC_SDK_LIFECYCLE_INITREQUIREDDEFAULT
|
||||||
|
|
||||||
|
private static Config config;
|
||||||
|
private static final Properties sdkProperties = new Properties();
|
||||||
|
private static final AtomicLong count = new AtomicLong(0);
|
||||||
|
|
||||||
|
//Provides a unique id for logging to identify a specific instance.
|
||||||
|
public String getNextID() {
|
||||||
|
return "" + count.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config() {
|
||||||
|
File loadFile;
|
||||||
|
FileInputStream configProps;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadFile = new File(System.getProperty(ORG_HYPERLEDGER_FABRIC_SDK_CONFIGURATION, DEFAULT_CONFIG))
|
||||||
|
.getAbsoluteFile();
|
||||||
|
logger.debug(format("Loading configuration from %s and it is present: %b", loadFile.toString(),
|
||||||
|
loadFile.exists()));
|
||||||
|
configProps = new FileInputStream(loadFile);
|
||||||
|
sdkProperties.load(configProps);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn(format("Failed to load any configuration from: %s. Using toolkit defaults",
|
||||||
|
DEFAULT_CONFIG));
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
/**
|
||||||
|
* Timeout settings
|
||||||
|
**/
|
||||||
|
defaultProperty(PROPOSAL_WAIT_TIME, "120000");
|
||||||
|
defaultProperty(CHANNEL_CONFIG_WAIT_TIME, "15000");
|
||||||
|
defaultProperty(ORDERER_RETRY_WAIT_TIME, "200");
|
||||||
|
defaultProperty(ORDERER_WAIT_TIME, "10000");
|
||||||
|
defaultProperty(PEER_EVENT_REGISTRATION_WAIT_TIME, "5000");
|
||||||
|
defaultProperty(PEER_EVENT_RETRY_WAIT_TIME, "500");
|
||||||
|
defaultProperty(GENESISBLOCK_WAIT_TIME, "5000");
|
||||||
|
/**
|
||||||
|
* This will NOT complete any transaction futures time out and must be kept WELL above any expected future timeout
|
||||||
|
* for transactions sent to the Orderer. For internal cleanup only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
defaultProperty(TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME, "600000"); //10 min.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypto configuration settings
|
||||||
|
**/
|
||||||
|
defaultProperty(DEFAULT_CRYPTO_SUITE_FACTORY, "org.hyperledger.fabric.sdk.security.HLSDKJCryptoSuiteFactory");
|
||||||
|
defaultProperty(SECURITY_LEVEL, "256");
|
||||||
|
defaultProperty(SECURITY_PROVIDER_CLASS_NAME, BouncyCastleProvider.class.getName());
|
||||||
|
defaultProperty(SECURITY_CURVE_MAPPING, "256=secp256r1:384=secp384r1");
|
||||||
|
defaultProperty(HASH_ALGORITHM, "SHA2");
|
||||||
|
defaultProperty(ASYMMETRIC_KEY_TYPE, "EC");
|
||||||
|
|
||||||
|
defaultProperty(CERTIFICATE_FORMAT, "X.509");
|
||||||
|
defaultProperty(SIGNATURE_ALGORITHM, "SHA256withECDSA");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection defaults
|
||||||
|
*/
|
||||||
|
|
||||||
|
defaultProperty(CONN_SSL_PROVIDER, "openSSL");
|
||||||
|
defaultProperty(CONN_SSL_NEGTYPE, "TLS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default HFClient thread executor settings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
defaultProperty(CLIENT_THREAD_EXECUTOR_COREPOOLSIZE, "0");
|
||||||
|
defaultProperty(CLIENT_THREAD_EXECUTOR_MAXIMUMPOOLSIZE, "" + Integer.MAX_VALUE);
|
||||||
|
defaultProperty(CLIENT_THREAD_EXECUTOR_KEEPALIVETIME, "" + "60");
|
||||||
|
defaultProperty(CLIENT_THREAD_EXECUTOR_KEEPALIVETIMEUNIT, "SECONDS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logging settings
|
||||||
|
**/
|
||||||
|
defaultProperty(MAX_LOG_STRING_LENGTH, "64");
|
||||||
|
defaultProperty(EXTRALOGLEVEL, "0");
|
||||||
|
defaultProperty(LOGGERLEVEL, null);
|
||||||
|
defaultProperty(DIAGNOTISTIC_FILE_DIRECTORY, null);
|
||||||
|
/**
|
||||||
|
* Miscellaneous settings
|
||||||
|
*/
|
||||||
|
defaultProperty(PROPOSAL_CONSISTENCY_VALIDATION, "true");
|
||||||
|
defaultProperty(PEER_EVENT_RECONNECTION_WARNING_RATE, "50");
|
||||||
|
|
||||||
|
defaultProperty(SERVICE_DISCOVER_FREQ_SECONDS, "120");
|
||||||
|
defaultProperty(SERVICE_DISCOVER_WAIT_TIME, "5000");
|
||||||
|
defaultProperty(SERVICE_DISCOVER_AS_LOCALHOST, "false");
|
||||||
|
defaultProperty(LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN, DEFAULT_NULL);
|
||||||
|
defaultProperty(LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN, DEFAULT_NULL);
|
||||||
|
defaultProperty(LIFECYCLE_INITREQUIREDDEFAULT, DEFAULT_NULL);
|
||||||
|
|
||||||
|
final String inLogLevel = sdkProperties.getProperty(LOGGERLEVEL);
|
||||||
|
|
||||||
|
if (null != inLogLevel) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getConfig return back singleton for SDK configuration.
|
||||||
|
*
|
||||||
|
* @return Global configuration
|
||||||
|
*/
|
||||||
|
public static Config getConfig() {
|
||||||
|
if (null == config) {
|
||||||
|
config = new Config();
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getProperty return back property for the given value.
|
||||||
|
*
|
||||||
|
* @param property
|
||||||
|
* @return String value for the property
|
||||||
|
*/
|
||||||
|
private String getProperty(String property) {
|
||||||
|
|
||||||
|
if (!sdkProperties.containsKey(property)) {
|
||||||
|
logger.warn(format("No configuration value found for '%s'", property));
|
||||||
|
}
|
||||||
|
String ret = sdkProperties.getProperty(property);
|
||||||
|
if (DEFAULT_NULL.equals(ret)) {
|
||||||
|
ret = null;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defaultProperty(String key, String value) {
|
||||||
|
|
||||||
|
String ret = System.getProperty(key);
|
||||||
|
if (ret != null) {
|
||||||
|
sdkProperties.put(key, ret);
|
||||||
|
} else {
|
||||||
|
String envKey = key.toUpperCase().replaceAll("\\.", "_");
|
||||||
|
ret = System.getenv(envKey);
|
||||||
|
if (null != ret) {
|
||||||
|
sdkProperties.put(key, ret);
|
||||||
|
} else {
|
||||||
|
if (null == sdkProperties.getProperty(key) && value != null) {
|
||||||
|
sdkProperties.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configured security level. The value determines the elliptic curve used to generate keys.
|
||||||
|
*
|
||||||
|
* @return the security level.
|
||||||
|
*/
|
||||||
|
public int getSecurityLevel() {
|
||||||
|
|
||||||
|
return Integer.parseInt(getProperty(SECURITY_LEVEL));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configured security provider.
|
||||||
|
* This is the security provider used for the default SDK crypto suite factory.
|
||||||
|
*
|
||||||
|
* @return the security provider.
|
||||||
|
*/
|
||||||
|
public String getSecurityProviderClassName() {
|
||||||
|
return getProperty(SECURITY_PROVIDER_CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the configured hash algorithm, used for digital signatures.
|
||||||
|
*
|
||||||
|
* @return the hash algorithm name.
|
||||||
|
*/
|
||||||
|
public String getHashAlgorithm() {
|
||||||
|
return getProperty(HASH_ALGORITHM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default ssl provider for grpc connection
|
||||||
|
*
|
||||||
|
* @return The default ssl provider for grpc connection
|
||||||
|
*/
|
||||||
|
public String getDefaultSSLProvider() {
|
||||||
|
return getProperty(CONN_SSL_PROVIDER);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default ssl negotiation type
|
||||||
|
*
|
||||||
|
* @return The default ssl negotiation type
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getDefaultSSLNegotiationType() {
|
||||||
|
return getProperty(CONN_SSL_NEGTYPE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, String> curveMapping = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a mapping from strength to curve desired.
|
||||||
|
*
|
||||||
|
* @return mapping from strength to curve name to use.
|
||||||
|
*/
|
||||||
|
public Map<Integer, String> getSecurityCurveMapping() {
|
||||||
|
|
||||||
|
if (curveMapping == null) {
|
||||||
|
|
||||||
|
curveMapping = parseSecurityCurveMappings(getProperty(SECURITY_CURVE_MAPPING));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableMap(curveMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, String> parseSecurityCurveMappings(final String property) {
|
||||||
|
Map<Integer, String> lcurveMapping = new HashMap<>(8);
|
||||||
|
|
||||||
|
if (property != null && !property.isEmpty()) { //empty will be caught later.
|
||||||
|
|
||||||
|
String[] cmaps = property.split("[ \t]*:[ \t]*");
|
||||||
|
for (String mape : cmaps) {
|
||||||
|
|
||||||
|
String[] ep = mape.split("[ \t]*=[ \t]*");
|
||||||
|
if (ep.length != 2) {
|
||||||
|
logger.warn(format("Bad curve mapping for %s in property %s", mape, SECURITY_CURVE_MAPPING));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int parseInt = Integer.parseInt(ep[0]);
|
||||||
|
lcurveMapping.put(parseInt, ep[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.warn(format("Bad curve mapping. Integer needed for strength %s for %s in property %s",
|
||||||
|
ep[0], mape, SECURITY_CURVE_MAPPING));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return lcurveMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timeout for a single proposal request to endorser.
|
||||||
|
*
|
||||||
|
* @return the timeout in milliseconds.
|
||||||
|
*/
|
||||||
|
public long getProposalWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(PROPOSAL_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configured time to wait for genesis block.
|
||||||
|
*
|
||||||
|
* @return time in milliseconds.
|
||||||
|
*/
|
||||||
|
public long getGenesisBlockWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(GENESISBLOCK_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to wait for channel to be configured.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getChannelConfigWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(CHANNEL_CONFIG_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to wait before retrying an operation.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getOrdererRetryWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(ORDERER_RETRY_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOrdererWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(ORDERER_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getPeerEventRegistrationWaitTime
|
||||||
|
*
|
||||||
|
* @return time in milliseconds to wait for peer eventing service to wait for event registration
|
||||||
|
*/
|
||||||
|
public long getPeerEventRegistrationWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(PEER_EVENT_REGISTRATION_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getPeerEventRegistrationWaitTime
|
||||||
|
*
|
||||||
|
* @return time in milliseconds to wait for peer eventing service to wait for event registration
|
||||||
|
*/
|
||||||
|
public long getPeerRetryWaitTime() {
|
||||||
|
return Long.parseLong(getProperty(PEER_EVENT_RETRY_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPeerEventReconnectionWarningRate() {
|
||||||
|
return Long.parseLong(getProperty(PEER_EVENT_RECONNECTION_WARNING_RATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often serviced discovery is preformed in seconds.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getServiceDiscoveryFreqSeconds() {
|
||||||
|
return Integer.parseInt(getProperty(SERVICE_DISCOVER_FREQ_SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to wait for service discovery to complete.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getServiceDiscoveryWaitTime() {
|
||||||
|
return Integer.parseInt(getProperty(SERVICE_DISCOVER_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean discoverAsLocalhost() {
|
||||||
|
return Boolean.parseBoolean(getProperty(SERVICE_DISCOVER_AS_LOCALHOST));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAsymmetricKeyType() {
|
||||||
|
return getProperty(ASYMMETRIC_KEY_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCertificateFormat() {
|
||||||
|
return getProperty(CERTIFICATE_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignatureAlgorithm() {
|
||||||
|
return getProperty(SIGNATURE_ALGORITHM);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultCryptoSuiteFactory() {
|
||||||
|
return getProperty(DEFAULT_CRYPTO_SUITE_FACTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int maxLogStringLength() {
|
||||||
|
return Integer.parseInt(getProperty(MAX_LOG_STRING_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getProposalConsistencyValidation determine if validation of the proposals should
|
||||||
|
* be done before sending to the orderer.
|
||||||
|
*
|
||||||
|
* @return if true proposals will be checked they are consistent with each other before sending to the Orderer
|
||||||
|
*/
|
||||||
|
|
||||||
|
public boolean getProposalConsistencyValidation() {
|
||||||
|
return Boolean.parseBoolean(getProperty(PROPOSAL_CONSISTENCY_VALIDATION));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int extraLogLevel = -1;
|
||||||
|
|
||||||
|
public boolean extraLogLevel(int val) {
|
||||||
|
if (extraLogLevel == -1) {
|
||||||
|
extraLogLevel = Integer.parseInt(getProperty(EXTRALOGLEVEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
return val <= extraLogLevel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagnosticFileDumper diagnosticFileDumper = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory where diagnostic dumps are to be place, null if none should be done.
|
||||||
|
*
|
||||||
|
* @return The directory where diagnostic dumps are to be place, null if none should be done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public DiagnosticFileDumper getDiagnosticFileDumper() {
|
||||||
|
|
||||||
|
if (diagnosticFileDumper != null) {
|
||||||
|
return diagnosticFileDumper;
|
||||||
|
}
|
||||||
|
|
||||||
|
String dd = sdkProperties.getProperty(DIAGNOTISTIC_FILE_DIRECTORY);
|
||||||
|
|
||||||
|
if (dd != null) {
|
||||||
|
|
||||||
|
diagnosticFileDumper = DiagnosticFileDumper.configInstance(new File(dd));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return diagnosticFileDumper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This does NOT trigger futures time out and must be kept WELL above any expected future timeout
|
||||||
|
* for transactions sent to the Orderer
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getTransactionListenerCleanUpTimeout() {
|
||||||
|
return Long.parseLong(getProperty(TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
|
||||||
|
*
|
||||||
|
* @return The number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int getClientThreadExecutorCorePoolSize() {
|
||||||
|
return Integer.parseInt(getProperty(CLIENT_THREAD_EXECUTOR_COREPOOLSIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* maximumPoolSize the maximum number of threads to allow in the pool
|
||||||
|
*
|
||||||
|
* @return maximumPoolSize the maximum number of threads to allow in the pool
|
||||||
|
*/
|
||||||
|
public int getClientThreadExecutorMaxiumPoolSize() {
|
||||||
|
return Integer.parseInt(getProperty(CLIENT_THREAD_EXECUTOR_MAXIMUMPOOLSIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keepAliveTime when the number of threads is greater than
|
||||||
|
* the core, this is the maximum time that excess idle threads
|
||||||
|
* will wait for new tasks before terminating.
|
||||||
|
*
|
||||||
|
* @return The keep alive time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public long getClientThreadExecutorKeepAliveTime() {
|
||||||
|
return Long.parseLong(getProperty(CLIENT_THREAD_EXECUTOR_KEEPALIVETIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the time unit for the argument
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public TimeUnit getClientThreadExecutorKeepAliveTimeUnit() {
|
||||||
|
|
||||||
|
return TimeUnit.valueOf(getProperty(CLIENT_THREAD_EXECUTOR_KEEPALIVETIMEUNIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default chaincode Endorsement policy plugin
|
||||||
|
* <p>
|
||||||
|
* This should never need setting
|
||||||
|
*
|
||||||
|
* @return The default chaincode Endorsement policy plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getDefaultChaincodeEndorsementPlugin() {
|
||||||
|
return getProperty(LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default chaincode validation plugin
|
||||||
|
* This should never need setting.
|
||||||
|
*
|
||||||
|
* @return The default chaincode validation plugin
|
||||||
|
*/
|
||||||
|
public String getDefaultChaincodeValidationPlugin() {
|
||||||
|
return getProperty(LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether require init method in chaincode to be run.
|
||||||
|
* The default will return null which will not set the Fabric protobuf value which then sets false. False is the Fabric
|
||||||
|
* default.
|
||||||
|
*
|
||||||
|
* @return The default setting for initRequired in chaincode approve for my org and commit chaincode definition.
|
||||||
|
*/
|
||||||
|
public Boolean getLifecycleInitRequiredDefault() {
|
||||||
|
|
||||||
|
String property = getProperty(LIFECYCLE_INITREQUIREDDEFAULT);
|
||||||
|
if (property != null) {
|
||||||
|
|
||||||
|
return Boolean.parseBoolean(property);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2016,2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.helper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.AsynchronousFileChannel;
|
||||||
|
import java.nio.channels.CompletionHandler;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps files for diagnostic purposes
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DiagnosticFileDumper implements Runnable {
|
||||||
|
|
||||||
|
private final File directory;
|
||||||
|
private final String dirAbsolutePath;
|
||||||
|
private final String pid;
|
||||||
|
// private static final Log logger = LogFactory.getLog(DiagnosticFileDumper.class);
|
||||||
|
private static Thread thread;
|
||||||
|
private final BlockingQueue<QueEntry> queEntries = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
private static DiagnosticFileDumper singleInstance = null;
|
||||||
|
private static final AtomicInteger counter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
private DiagnosticFileDumper(File directory) {
|
||||||
|
|
||||||
|
this.directory = directory;
|
||||||
|
this.dirAbsolutePath = directory == null ? null : directory.getAbsolutePath();
|
||||||
|
this.pid = getPID() + "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static DiagnosticFileDumper configInstance(File directory) {
|
||||||
|
|
||||||
|
if (singleInstance == null) {
|
||||||
|
singleInstance = new DiagnosticFileDumper(directory);
|
||||||
|
thread = new Thread(singleInstance);
|
||||||
|
thread.setName("DiagnosticFileDumper");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return singleInstance;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDiagnosticProtobufFile(byte[] byteString) {
|
||||||
|
|
||||||
|
return createDiagnosticFile(byteString, "protobuf_", "proto");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cantWrite() {
|
||||||
|
return null == directory || !directory.exists() || !directory.isDirectory() || !directory.canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDiagnosticFile(byte[] bytes) {
|
||||||
|
|
||||||
|
return createDiagnosticFile(bytes, null, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDiagnosticTarFile(byte[] bytes) {
|
||||||
|
|
||||||
|
return createDiagnosticFile(bytes, null, "tgz");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDiagnosticFile(String msg) {
|
||||||
|
|
||||||
|
return createDiagnosticFile(msg.getBytes(StandardCharsets.UTF_8), null, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDiagnosticFile(byte[] bytes, String prefix, String ext) {
|
||||||
|
String fileName = "";
|
||||||
|
if (cantWrite()) {
|
||||||
|
return "Missing dump directory or can not write: " + (dirAbsolutePath);
|
||||||
|
}
|
||||||
|
if (null != bytes) {
|
||||||
|
if (null == prefix) {
|
||||||
|
prefix = "diagnostic_";
|
||||||
|
}
|
||||||
|
if (null == ext) {
|
||||||
|
ext = "bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss_SSS");
|
||||||
|
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
fileName = prefix + dateFormatGmt.format(new Date()) + "P" + pid + "_" + Thread.currentThread().getId()
|
||||||
|
+ "_" + counter.addAndGet(1) + "." + ext;
|
||||||
|
fileName = fileName.replaceAll("\\:", "-"); // colon is bad for windows.
|
||||||
|
|
||||||
|
new QueEntry(fileName, bytes); //Add to Que let process by async thread.
|
||||||
|
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final LinkedList<QueEntry> entries = new LinkedList<>();
|
||||||
|
|
||||||
|
entries.add(this.queEntries.take()); // wait on one.
|
||||||
|
this.queEntries.drainTo(entries); //got one, see if there are more.
|
||||||
|
|
||||||
|
if (cantWrite()) {
|
||||||
|
return; //IF the directory is missing just assume user does not want diagnostic files created anymore.
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.forEach(queEntry -> {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get(dirAbsolutePath, queEntry.fileName),
|
||||||
|
StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
|
||||||
|
channel.write(ByteBuffer.wrap(queEntry.dataBytes), 0, null, new CompletionHandler<Integer, Object>() {
|
||||||
|
@Override
|
||||||
|
public void completed(Integer result, Object attachment) {
|
||||||
|
try {
|
||||||
|
channel.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//best effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable exc, Object attachment) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
channel.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//best effort.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
//best effort.
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// best effort
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueEntry {
|
||||||
|
final String fileName;
|
||||||
|
final byte[] dataBytes;
|
||||||
|
|
||||||
|
QueEntry(String fileName, byte[] dataBytes) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.dataBytes = dataBytes;
|
||||||
|
|
||||||
|
queEntries.add(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getPID() {
|
||||||
|
String processName =
|
||||||
|
java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
|
||||||
|
return Long.parseLong(processName.split("@")[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.helper;
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.helper.Config;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.Timestamp;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.bouncycastle.crypto.Digest;
|
||||||
|
import org.bouncycastle.crypto.digests.SHA3Digest;
|
||||||
|
import org.bouncycastle.util.Arrays;
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.FileVisitOption;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.apache.commons.codec.binary.Hex.encodeHexString;
|
||||||
|
|
||||||
|
public final class Utils {
|
||||||
|
private static final Log logger = LogFactory.getLog(Utils.class);
|
||||||
|
|
||||||
|
private static final boolean TRACE_ENABED = logger.isTraceEnabled();
|
||||||
|
private static final Config config = Config.getConfig();
|
||||||
|
private static final int MAX_LOG_STRING_LENGTH = config.maxLogStringLength();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate parameter hash for the given chaincode path,func and args
|
||||||
|
*
|
||||||
|
* @param path Chaincode path
|
||||||
|
* @param func Chaincode function name
|
||||||
|
* @param args List of arguments
|
||||||
|
* @return hash of path, func and args
|
||||||
|
*/
|
||||||
|
public static String generateParameterHash(String path, String func, List<String> args) {
|
||||||
|
logger.debug(format("GenerateParameterHash : path=%s, func=%s, args=%s", path, func, args));
|
||||||
|
|
||||||
|
// Append the arguments
|
||||||
|
StringBuilder param = new StringBuilder(path);
|
||||||
|
param.append(func);
|
||||||
|
args.forEach(param::append);
|
||||||
|
|
||||||
|
// Compute the hash
|
||||||
|
|
||||||
|
return Hex.toHexString(hash(param.toString().getBytes(UTF_8), new SHA3Digest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate hash of a chaincode directory
|
||||||
|
*
|
||||||
|
* @param rootDir Root directory
|
||||||
|
* @param chaincodeDir Channel code directory
|
||||||
|
* @param hash Previous hash (if any)
|
||||||
|
* @return hash of the directory
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static String generateDirectoryHash(String rootDir, String chaincodeDir, String hash) throws IOException {
|
||||||
|
// Generate the project directory
|
||||||
|
Path projectPath;
|
||||||
|
if (rootDir == null) {
|
||||||
|
projectPath = Paths.get(chaincodeDir);
|
||||||
|
} else {
|
||||||
|
projectPath = Paths.get(rootDir, chaincodeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
File dir = projectPath.toFile();
|
||||||
|
if (!dir.exists() || !dir.isDirectory()) {
|
||||||
|
throw new IOException(format("The chaincode path \"%s\" is invalid", projectPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder hashBuilder = new StringBuilder(hash);
|
||||||
|
Files.walk(projectPath)
|
||||||
|
.sorted(Comparator.naturalOrder())
|
||||||
|
.filter(Files::isRegularFile)
|
||||||
|
.map(Path::toFile)
|
||||||
|
.forEach(file -> {
|
||||||
|
try {
|
||||||
|
byte[] buf = readFile(file);
|
||||||
|
byte[] toHash = Arrays.concatenate(buf, hashBuilder.toString().getBytes(UTF_8));
|
||||||
|
hashBuilder.setLength(0);
|
||||||
|
hashBuilder.append(Hex.toHexString(hash(toHash, new SHA3Digest())));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(format("Error while reading file %s", file.getAbsolutePath()), ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If original hash and final hash are the same, it indicates that no new contents were found
|
||||||
|
if (hashBuilder.toString().equals(hash)) {
|
||||||
|
throw new IOException(format("The chaincode directory \"%s\" has no files", projectPath));
|
||||||
|
}
|
||||||
|
return hashBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress the contents of given directory using Tar and Gzip to an in-memory byte array.
|
||||||
|
*
|
||||||
|
* @param sourceDirectory the source directory.
|
||||||
|
* @param pathPrefix a path to be prepended to every file name in the .tar.gz output, or {@code null} if no prefix is required.
|
||||||
|
* @param chaincodeMetaInf
|
||||||
|
* @return the compressed directory contents.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] generateTarGz(File sourceDirectory, String pathPrefix, File chaincodeMetaInf) throws IOException {
|
||||||
|
logger.trace(format("generateTarGz: sourceDirectory: %s, pathPrefix: %s, chaincodeMetaInf: %s",
|
||||||
|
sourceDirectory == null ? "null" : sourceDirectory.getAbsolutePath(), pathPrefix,
|
||||||
|
chaincodeMetaInf == null ? "null" : chaincodeMetaInf.getAbsolutePath()));
|
||||||
|
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(500000);
|
||||||
|
|
||||||
|
String sourcePath = sourceDirectory.getAbsolutePath();
|
||||||
|
|
||||||
|
// try (TarArchiveOutputStream archiveOutputStream = new TarArchiveOutputStream(new GzipCompressorOutputStream(bos))) {
|
||||||
|
// archiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
|
||||||
|
//
|
||||||
|
// Collection<File> childrenFiles = org.apache.commons.io.FileUtils.listFiles(sourceDirectory, null, true);
|
||||||
|
//
|
||||||
|
// ArchiveEntry archiveEntry;
|
||||||
|
// for (File childFile : childrenFiles) {
|
||||||
|
// String childPath = childFile.getAbsolutePath();
|
||||||
|
// String relativePath = childPath.substring((sourcePath.length() + 1));
|
||||||
|
//
|
||||||
|
// if (pathPrefix != null) {
|
||||||
|
// relativePath = Utils.combinePaths(pathPrefix, relativePath);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// relativePath = FilenameUtils.separatorsToUnix(relativePath);
|
||||||
|
//
|
||||||
|
// if (TRACE_ENABED) {
|
||||||
|
// logger.trace(format("generateTarGz: Adding '%s' entry from source '%s' to archive.", relativePath, childFile.getAbsolutePath()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// archiveEntry = new TarArchiveEntry(childFile, relativePath);
|
||||||
|
// archiveOutputStream.putArchiveEntry(archiveEntry);
|
||||||
|
//
|
||||||
|
// try (FileInputStream fileInputStream = new FileInputStream(childFile)) {
|
||||||
|
// IOUtils.copy(fileInputStream, archiveOutputStream);
|
||||||
|
// } finally {
|
||||||
|
// archiveOutputStream.closeArchiveEntry();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (null != chaincodeMetaInf) {
|
||||||
|
// childrenFiles = org.apache.commons.io.FileUtils.listFiles(chaincodeMetaInf, null, true);
|
||||||
|
//
|
||||||
|
// final URI metabase = chaincodeMetaInf.toURI();
|
||||||
|
//
|
||||||
|
// for (File childFile : childrenFiles) {
|
||||||
|
//
|
||||||
|
// final String relativePath = Paths.get("META-INF", metabase.relativize(childFile.toURI()).getPath()).toString();
|
||||||
|
//
|
||||||
|
// if (TRACE_ENABED) {
|
||||||
|
// logger.trace(format("generateTarGz: Adding '%s' entry from source '%s' to archive.", relativePath, childFile.getAbsolutePath()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// archiveEntry = new TarArchiveEntry(childFile, relativePath);
|
||||||
|
// archiveOutputStream.putArchiveEntry(archiveEntry);
|
||||||
|
//
|
||||||
|
// try (FileInputStream fileInputStream = new FileInputStream(childFile)) {
|
||||||
|
// IOUtils.copy(fileInputStream, archiveOutputStream);
|
||||||
|
// } finally {
|
||||||
|
// archiveOutputStream.closeArchiveEntry();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the contents a file.
|
||||||
|
*
|
||||||
|
* @param input source file to read.
|
||||||
|
* @return contents of the file.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] readFile(File input) throws IOException {
|
||||||
|
return Files.readAllBytes(Paths.get(input.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a v4 UUID
|
||||||
|
*
|
||||||
|
* @return String representation of {@link UUID}
|
||||||
|
*/
|
||||||
|
public static String generateUUID() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Timestamp} instance based on the current time
|
||||||
|
*
|
||||||
|
* @return timestamp
|
||||||
|
*/
|
||||||
|
public static Timestamp generateTimestamp() {
|
||||||
|
Instant time = Instant.now();
|
||||||
|
return Timestamp.newBuilder().setSeconds(time.getEpochSecond())
|
||||||
|
.setNanos(time.getNano()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a file or directory
|
||||||
|
*
|
||||||
|
* @param file {@link File} representing file or directory
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void deleteFileOrDirectory(File file) throws IOException {
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Path rootPath = Paths.get(file.getAbsolutePath());
|
||||||
|
|
||||||
|
Files.walk(rootPath, FileVisitOption.FOLLOW_LINKS)
|
||||||
|
.sorted(Comparator.reverseOrder())
|
||||||
|
.map(Path::toFile)
|
||||||
|
.forEach(File::delete);
|
||||||
|
} else {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("File or directory does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate hash of the given input using the given Digest.
|
||||||
|
*
|
||||||
|
* @param input input data.
|
||||||
|
* @param digest the digest to use for hashing
|
||||||
|
* @return hashed data.
|
||||||
|
*/
|
||||||
|
public static byte[] hash(byte[] input, Digest digest) {
|
||||||
|
byte[] retValue = new byte[digest.getDigestSize()];
|
||||||
|
digest.update(input, 0, input.length);
|
||||||
|
digest.doFinal(retValue, 0);
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine two or more paths
|
||||||
|
*
|
||||||
|
* @param first parent directory path
|
||||||
|
* @param other children
|
||||||
|
* @return combined path
|
||||||
|
*/
|
||||||
|
public static String combinePaths(String first, String... other) {
|
||||||
|
return Paths.get(first, other).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a file from classpath
|
||||||
|
*
|
||||||
|
* @param fileName
|
||||||
|
* @return byte[] data
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static byte[] readFileFromClasspath(String fileName) throws IOException {
|
||||||
|
try (InputStream is = Utils.class.getClassLoader().getResourceAsStream(fileName)) {
|
||||||
|
return IOUtils.toByteArray(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties parseGrpcUrl(String url) {
|
||||||
|
if (isNullOrEmpty(url)) {
|
||||||
|
throw new RuntimeException("URL cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties props = new Properties();
|
||||||
|
Pattern p = Pattern.compile("([^:]+)[:]//([^:]+)[:]([0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher m = p.matcher(url);
|
||||||
|
if (m.matches()) {
|
||||||
|
props.setProperty("protocol", m.group(1));
|
||||||
|
props.setProperty("host", m.group(2));
|
||||||
|
props.setProperty("port", m.group(3));
|
||||||
|
|
||||||
|
String protocol = props.getProperty("protocol");
|
||||||
|
if (!"grpc".equals(protocol) && !"grpcs".equals(protocol)) {
|
||||||
|
throw new RuntimeException(format("Invalid protocol expected grpc or grpcs and found %s.", protocol));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("URL must be of the format protocol://host:port. Found: '" + url + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: allow all possible formats of the URL
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the strings Grpc url is valid
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return Return the exception that indicates the error or null if ok.
|
||||||
|
*/
|
||||||
|
public static Exception checkGrpcUrl(String url) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
parseGrpcUrl(url);
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is null or empty.
|
||||||
|
*
|
||||||
|
* @param url the string to test.
|
||||||
|
* @return {@code true} if the string is null or empty; otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
public static boolean isNullOrEmpty(String url) {
|
||||||
|
return url == null || url.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes logging strings which can be long or with unprintable characters be logged and trimmed.
|
||||||
|
*
|
||||||
|
* @param string Unsafe string too long
|
||||||
|
* @return returns a string which does not have unprintable characters and trimmed in length.
|
||||||
|
*/
|
||||||
|
public static String logString(final String string) {
|
||||||
|
if (string == null || string.length() == 0) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
String ret = string.replaceAll("[^\\p{Print}]", "?");
|
||||||
|
|
||||||
|
ret = ret.substring(0, Math.min(ret.length(), MAX_LOG_STRING_LENGTH)) + (ret.length() > MAX_LOG_STRING_LENGTH ? "..." : "");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int NONONCE_LENGTH = 24;
|
||||||
|
|
||||||
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
public static byte[] generateNonce() {
|
||||||
|
|
||||||
|
byte[] values = new byte[NONONCE_LENGTH];
|
||||||
|
RANDOM.nextBytes(values);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(ByteString byteString) {
|
||||||
|
if (byteString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeHexString(byteString.toByteArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(byte[] bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeHexString(bytes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(String bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeHexString(bytes.getBytes(UTF_8));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor to prevent instantiation.
|
||||||
|
*/
|
||||||
|
private Utils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,977 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, 2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.security;
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.CryptoException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.InvalidArgumentException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.helper.Config;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.helper.DiagnosticFileDumper;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.helper.Utils;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.bouncycastle.asn1.*;
|
||||||
|
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
|
||||||
|
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||||
|
import org.bouncycastle.crypto.Digest;
|
||||||
|
import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||||
|
import org.bouncycastle.crypto.digests.SHA3Digest;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.openssl.PEMKeyPair;
|
||||||
|
import org.bouncycastle.openssl.PEMParser;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||||
|
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
|
||||||
|
import org.bouncycastle.operator.ContentSigner;
|
||||||
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
|
||||||
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
|
||||||
|
import org.bouncycastle.util.io.pem.PemObject;
|
||||||
|
import org.bouncycastle.util.io.pem.PemReader;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.io.*;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.*;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.spec.ECGenParameterSpec;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static com.chinaunicom.mall.ebtp.common.crypto.helper.Utils.isNullOrEmpty;
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
|
public class CryptoPrimitives implements CryptoSuite {
|
||||||
|
private static final Log logger = LogFactory.getLog(CryptoPrimitives.class);
|
||||||
|
private static final Config config = Config.getConfig();
|
||||||
|
private static final boolean IS_TRACE_LEVEL = logger.isTraceEnabled();
|
||||||
|
|
||||||
|
private static final DiagnosticFileDumper diagnosticFileDumper = IS_TRACE_LEVEL
|
||||||
|
? config.getDiagnosticFileDumper() : null;
|
||||||
|
|
||||||
|
private String curveName;
|
||||||
|
private CertificateFactory cf;
|
||||||
|
private Provider SECURITY_PROVIDER;
|
||||||
|
private String hashAlgorithm = config.getHashAlgorithm();
|
||||||
|
private int securityLevel = config.getSecurityLevel();
|
||||||
|
private String CERTIFICATE_FORMAT = config.getCertificateFormat();
|
||||||
|
private String DEFAULT_SIGNATURE_ALGORITHM = config.getSignatureAlgorithm();
|
||||||
|
|
||||||
|
private Map<Integer, String> securityCurveMapping = config.getSecurityCurveMapping();
|
||||||
|
|
||||||
|
// Following configuration settings are hardcoded as they don't deal with any interactions with Fabric MSP and BCCSP components
|
||||||
|
// If you wish to make these customizable, follow the logic from setProperties();
|
||||||
|
//TODO May need this for TCERTS ?
|
||||||
|
// private String ASYMMETRIC_KEY_TYPE = "EC";
|
||||||
|
// private String KEY_AGREEMENT_ALGORITHM = "ECDH";
|
||||||
|
// private String SYMMETRIC_KEY_TYPE = "AES";
|
||||||
|
// private int SYMMETRIC_KEY_BYTE_COUNT = 32;
|
||||||
|
// private String SYMMETRIC_ALGORITHM = "AES/CFB/NoPadding";
|
||||||
|
// private int MAC_KEY_BYTE_COUNT = 32;
|
||||||
|
|
||||||
|
public CryptoPrimitives() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
String securityProviderClassName = config.getSecurityProviderClassName();
|
||||||
|
|
||||||
|
SECURITY_PROVIDER = setUpExplicitProvider(securityProviderClassName);
|
||||||
|
|
||||||
|
//Decided TO NOT do this as it can have affects over the whole JVM and could have
|
||||||
|
// unexpected results. The embedding application can easily do this!
|
||||||
|
// Leaving this here as a warning.
|
||||||
|
// Security.insertProviderAt(SECURITY_PROVIDER, 1); // 1 is top not 0 :)
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider setUpExplicitProvider(String securityProviderClassName) throws InstantiationException, ClassNotFoundException, IllegalAccessException {
|
||||||
|
if (null == securityProviderClassName) {
|
||||||
|
throw new InstantiationException(format("Security provider class name property (%s) set to null ", Config.SECURITY_PROVIDER_CLASS_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CryptoSuiteFactory.DEFAULT_JDK_PROVIDER.equals(securityProviderClassName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> aClass = Class.forName(securityProviderClassName);
|
||||||
|
if (null == aClass) {
|
||||||
|
throw new InstantiationException(format("Getting class for security provider %s returned null ", securityProviderClassName));
|
||||||
|
}
|
||||||
|
if (!Provider.class.isAssignableFrom(aClass)) {
|
||||||
|
throw new InstantiationException(format("Class for security provider %s is not a Java security provider", aClass.getName()));
|
||||||
|
}
|
||||||
|
Provider securityProvider = (Provider) aClass.newInstance();
|
||||||
|
if (securityProvider == null) {
|
||||||
|
throw new InstantiationException(format("Creating instance of security %s returned null ", aClass.getName()));
|
||||||
|
}
|
||||||
|
return securityProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * sets the signature algorithm used for signing/verifying.
|
||||||
|
// *
|
||||||
|
// * @param sigAlg the name of the signature algorithm. See the list of valid names in the JCA Standard Algorithm Name documentation
|
||||||
|
// */
|
||||||
|
// public void setSignatureAlgorithm(String sigAlg) {
|
||||||
|
// this.DEFAULT_SIGNATURE_ALGORITHM = sigAlg;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * returns the signature algorithm used by this instance of CryptoPrimitives.
|
||||||
|
// * Note that fabric and fabric-ca have not yet standardized on which algorithms are supported.
|
||||||
|
// * While that plays out, CryptoPrimitives will try the algorithm specified in the certificate and
|
||||||
|
// * the default SHA256withECDSA that's currently hardcoded for fabric and fabric-ca
|
||||||
|
// *
|
||||||
|
// * @return the name of the signature algorithm
|
||||||
|
// */
|
||||||
|
// public String getSignatureAlgorithm() {
|
||||||
|
// return this.DEFAULT_SIGNATURE_ALGORITHM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate bytesToCertificate(byte[] certBytes) throws CryptoException {
|
||||||
|
if (certBytes == null || certBytes.length == 0) {
|
||||||
|
throw new CryptoException("bytesToCertificate: input null or zero length");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getX509Certificate(certBytes);
|
||||||
|
// X509Certificate certificate;
|
||||||
|
// try {
|
||||||
|
// BufferedInputStream pem = new BufferedInputStream(new ByteArrayInputStream(certBytes));
|
||||||
|
// CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT);
|
||||||
|
// certificate = (X509Certificate) certFactory.generateCertificate(pem);
|
||||||
|
// } catch (CertificateException e) {
|
||||||
|
// String emsg = "Unable to converts byte array to certificate. error : " + e.getMessage();
|
||||||
|
// logger.error(emsg);
|
||||||
|
// logger.debug("input bytes array :" + new String(certBytes));
|
||||||
|
// throw new CryptoException(emsg, e);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return X509Certificate from pem bytes.
|
||||||
|
* So you may ask why this ? Well some providers (BC) seems to have problems with creating the
|
||||||
|
* X509 cert from bytes so here we go through all available providers till one can convert. :)
|
||||||
|
*
|
||||||
|
* @param pemCertificate
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
private X509Certificate getX509Certificate(byte[] pemCertificate) throws CryptoException {
|
||||||
|
X509Certificate ret = null;
|
||||||
|
CryptoException rete = null;
|
||||||
|
|
||||||
|
List<Provider> providerList = new LinkedList<>(Arrays.asList(Security.getProviders()));
|
||||||
|
if (SECURITY_PROVIDER != null) { //Add if overridden
|
||||||
|
providerList.add(SECURITY_PROVIDER);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
providerList.add(BouncyCastleProvider.class.newInstance()); // bouncy castle is there always.
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
for (Provider provider : providerList) {
|
||||||
|
try {
|
||||||
|
if (null == provider) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT, provider);
|
||||||
|
if (null != certFactory) {
|
||||||
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(pemCertificate)) {
|
||||||
|
Certificate certificate = certFactory.generateCertificate(bis);
|
||||||
|
|
||||||
|
if (certificate instanceof X509Certificate) {
|
||||||
|
ret = (X509Certificate) certificate;
|
||||||
|
rete = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
rete = new CryptoException(e.getMessage(), e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != rete) {
|
||||||
|
|
||||||
|
throw rete;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == null) {
|
||||||
|
|
||||||
|
logger.error("Could not convert pem bytes");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return PrivateKey from pem bytes.
|
||||||
|
*
|
||||||
|
* @param pemKey pem-encoded private key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public PrivateKey bytesToPrivateKey(byte[] pemKey) throws CryptoException {
|
||||||
|
PrivateKey pk;
|
||||||
|
|
||||||
|
try {
|
||||||
|
PemReader pr = new PemReader(new StringReader(new String(pemKey)));
|
||||||
|
PemObject po = pr.readPemObject();
|
||||||
|
PEMParser pem = new PEMParser(new StringReader(new String(pemKey)));
|
||||||
|
|
||||||
|
if (po.getType().equals("PRIVATE KEY")) {
|
||||||
|
pk = new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo) pem.readObject());
|
||||||
|
} else {
|
||||||
|
logger.trace("Found private key with type " + po.getType());
|
||||||
|
PEMKeyPair kp = (PEMKeyPair) pem.readObject();
|
||||||
|
pk = new JcaPEMKeyConverter().getPrivateKey(kp.getPrivateKeyInfo());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CryptoException("Failed to convert private key bytes", e);
|
||||||
|
}
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(byte[] pemCertificate, String signatureAlgorithm, byte[] signature, byte[] plainText) throws CryptoException {
|
||||||
|
boolean isVerified = false;
|
||||||
|
|
||||||
|
if (plainText == null || signature == null || pemCertificate == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.extraLogLevel(10)) {
|
||||||
|
if (null != diagnosticFileDumper) {
|
||||||
|
String message = "plaintext in hex: " + DatatypeConverter.printHexBinary(plainText) + '\n' +
|
||||||
|
"signature in hex: " + DatatypeConverter.printHexBinary(signature) + '\n' +
|
||||||
|
"PEM cert in hex: " + DatatypeConverter.printHexBinary(pemCertificate);
|
||||||
|
logger.trace("verify : " +
|
||||||
|
diagnosticFileDumper.createDiagnosticFile(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
X509Certificate certificate = getX509Certificate(pemCertificate);
|
||||||
|
|
||||||
|
if (certificate != null) {
|
||||||
|
|
||||||
|
//isVerified = validateCertificate(certificate);
|
||||||
|
isVerified = true;
|
||||||
|
if (isVerified) { // only proceed if cert is trusted
|
||||||
|
|
||||||
|
Signature sig = Signature.getInstance(signatureAlgorithm);
|
||||||
|
sig.initVerify(certificate);
|
||||||
|
sig.update(plainText);
|
||||||
|
isVerified = sig.verify(signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
CryptoException ex = new CryptoException("Cannot verify signature. Error is: "
|
||||||
|
+ e.getMessage() + "\r\nCertificate: "
|
||||||
|
+ DatatypeConverter.printHexBinary(pemCertificate), e);
|
||||||
|
logger.error(ex.getMessage(), ex);
|
||||||
|
throw ex;
|
||||||
|
} catch (NoSuchAlgorithmException | SignatureException e) {
|
||||||
|
CryptoException ex = new CryptoException("Cannot verify. Signature algorithm is invalid. Error is: " + e.getMessage(), e);
|
||||||
|
logger.error(ex.getMessage(), ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isVerified;
|
||||||
|
} // verify
|
||||||
|
|
||||||
|
private KeyStore trustStore = null;
|
||||||
|
|
||||||
|
private void createTrustStore() throws CryptoException {
|
||||||
|
try {
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
keyStore.load(null, null);
|
||||||
|
setTrustStore(keyStore);
|
||||||
|
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | InvalidArgumentException e) {
|
||||||
|
throw new CryptoException("Cannot create trust store. Error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setTrustStore uses the given KeyStore object as the container for trusted
|
||||||
|
* certificates
|
||||||
|
*
|
||||||
|
* @param keyStore the KeyStore which will be used to hold trusted certificates
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
private void setTrustStore(KeyStore keyStore) throws InvalidArgumentException {
|
||||||
|
|
||||||
|
if (keyStore == null) {
|
||||||
|
throw new InvalidArgumentException("Need to specify a java.security.KeyStore input parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
trustStore = keyStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getTrustStore returns the KeyStore object where we keep trusted certificates.
|
||||||
|
* If no trust store has been set, this method will create one.
|
||||||
|
*
|
||||||
|
* @return the trust store as a java.security.KeyStore object
|
||||||
|
* @throws CryptoException
|
||||||
|
* @see KeyStore
|
||||||
|
*/
|
||||||
|
public KeyStore getTrustStore() throws CryptoException {
|
||||||
|
if (trustStore == null) {
|
||||||
|
createTrustStore();
|
||||||
|
}
|
||||||
|
return trustStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addCACertificateToTrustStore adds a CA cert to the set of certificates used for signature validation
|
||||||
|
*
|
||||||
|
* @param caCertPem an X.509 certificate in PEM format
|
||||||
|
* @param alias an alias associated with the certificate. Used as shorthand for the certificate during crypto operations
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public void addCACertificateToTrustStore(File caCertPem, String alias) throws CryptoException, InvalidArgumentException {
|
||||||
|
|
||||||
|
if (caCertPem == null) {
|
||||||
|
throw new InvalidArgumentException("The certificate cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alias == null || alias.isEmpty()) {
|
||||||
|
throw new InvalidArgumentException("You must assign an alias to a certificate when adding to the trust store");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
try (BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(FileUtils.readFileToByteArray(caCertPem)))) {
|
||||||
|
|
||||||
|
Certificate caCert = cf.generateCertificate(bis);
|
||||||
|
addCACertificateToTrustStore(caCert, alias);
|
||||||
|
}
|
||||||
|
} catch (CertificateException | IOException e) {
|
||||||
|
throw new CryptoException("Unable to add CA certificate to trust store. Error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addCACertificatesToTrustStore adds a CA certs in a stream to the trust store used for signature validation
|
||||||
|
*
|
||||||
|
* @param bis an X.509 certificate stream in PEM format in bytes
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public void addCACertificatesToTrustStore(BufferedInputStream bis) throws CryptoException, InvalidArgumentException {
|
||||||
|
|
||||||
|
if (bis == null) {
|
||||||
|
throw new InvalidArgumentException("The certificate stream bis cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Collection<? extends Certificate> certificates = cf.generateCertificates(bis);
|
||||||
|
for (Certificate certificate : certificates) {
|
||||||
|
addCACertificateToTrustStore(certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
throw new CryptoException("Unable to add CA certificate to trust store. Error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<String> certificateSet = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
private void addCACertificateToTrustStore(Certificate certificate) throws InvalidArgumentException, CryptoException {
|
||||||
|
|
||||||
|
String alias;
|
||||||
|
if (certificate instanceof X509Certificate) {
|
||||||
|
alias = ((X509Certificate) certificate).getSerialNumber().toString();
|
||||||
|
} else { // not likely ...
|
||||||
|
alias = Integer.toString(certificate.hashCode());
|
||||||
|
}
|
||||||
|
addCACertificateToTrustStore(certificate, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addCACertificateToTrustStore adds a CA cert to the set of certificates used for signature validation
|
||||||
|
*
|
||||||
|
* @param caCert an X.509 certificate
|
||||||
|
* @param alias an alias associated with the certificate. Used as shorthand for the certificate during crypto operations
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
void addCACertificateToTrustStore(Certificate caCert, String alias) throws InvalidArgumentException, CryptoException {
|
||||||
|
|
||||||
|
if (alias == null || alias.isEmpty()) {
|
||||||
|
throw new InvalidArgumentException("You must assign an alias to a certificate when adding to the trust store.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caCert == null) {
|
||||||
|
throw new InvalidArgumentException("Certificate cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (config.extraLogLevel(10)) {
|
||||||
|
if (null != diagnosticFileDumper) {
|
||||||
|
logger.trace(format("Adding cert to trust store. alias: %s. certificate:", alias) + diagnosticFileDumper.createDiagnosticFile(alias + "cert: " + caCert.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (certificateSet) {
|
||||||
|
if (certificateSet.contains(alias)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTrustStore().setCertificateEntry(alias, caCert);
|
||||||
|
certificateSet.add(alias);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
String emsg = "Unable to add CA certificate to trust store. Error: " + e.getMessage();
|
||||||
|
logger.error(emsg, e);
|
||||||
|
throw new CryptoException(emsg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadCACertificates(Collection<Certificate> certificates) throws CryptoException {
|
||||||
|
if (certificates == null || certificates.size() == 0) {
|
||||||
|
throw new CryptoException("Unable to load CA certificates. List is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (Certificate cert : certificates) {
|
||||||
|
|
||||||
|
addCACertificateToTrustStore(cert);
|
||||||
|
}
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
// Note: This can currently never happen (as cert<>null and alias<>null)
|
||||||
|
throw new CryptoException("Unable to add certificate to trust store. Error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.hyperledger.fabric.sdk.security.CryptoSuite#loadCACertificatesAsBytes(java.util.Collection)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void loadCACertificatesAsBytes(Collection<byte[]> certificatesBytes) throws CryptoException {
|
||||||
|
if (certificatesBytes == null || certificatesBytes.size() == 0) {
|
||||||
|
throw new CryptoException("List of CA certificates is empty. Nothing to load.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Certificate> certList = new ArrayList<>();
|
||||||
|
for (byte[] certBytes : certificatesBytes) {
|
||||||
|
certList.add(bytesToCertificate(certBytes));
|
||||||
|
}
|
||||||
|
loadCACertificates(certList);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a CA certificate with a private key and password.
|
||||||
|
*
|
||||||
|
* @param clientKey the private key bytes input stream. Cannot be null.
|
||||||
|
* @param clientCert the client certificate bytes input stream. Cannot be null.
|
||||||
|
* @param clientKeyPassword the password as a String. Can be null.
|
||||||
|
*/
|
||||||
|
public void addClientCACertificateToTrustStore(byte[] clientKey, byte[] clientCert, String clientKeyPassword) throws CryptoException, IllegalArgumentException {
|
||||||
|
if (clientKey == null) {
|
||||||
|
throw new IllegalArgumentException("Client key byte input stream is required.");
|
||||||
|
}
|
||||||
|
if (clientCert == null) {
|
||||||
|
throw new IllegalArgumentException("Client certificate byte input stream is required.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Certificate tlsClientCertificate = bytesToCertificate(clientCert);
|
||||||
|
|
||||||
|
String alias;
|
||||||
|
if (tlsClientCertificate instanceof X509Certificate) {
|
||||||
|
alias = ((X509Certificate) tlsClientCertificate).getSerialNumber().toString();
|
||||||
|
} else { // not likely ...
|
||||||
|
alias = Integer.toString(tlsClientCertificate.hashCode());
|
||||||
|
}
|
||||||
|
char[] password = clientKeyPassword == null ? new char[0] : clientKeyPassword.toCharArray();
|
||||||
|
|
||||||
|
getTrustStore().setKeyEntry(alias, bytesToPrivateKey(clientKey), password, new Certificate[] {tlsClientCertificate});
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
throw new CryptoException("Unable to add client CA certificate to trust store.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validateCertificate checks whether the given certificate is trusted. It
|
||||||
|
* checks if the certificate is signed by one of the trusted certs in the
|
||||||
|
* trust store.
|
||||||
|
*
|
||||||
|
* @param certPEM the certificate in PEM format
|
||||||
|
* @return true if the certificate is trusted
|
||||||
|
*/
|
||||||
|
boolean validateCertificate(byte[] certPEM) {
|
||||||
|
|
||||||
|
if (certPEM == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
X509Certificate certificate = getX509Certificate(certPEM);
|
||||||
|
if (null == certificate) {
|
||||||
|
throw new Exception("Certificate transformation returned null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateCertificate(certificate);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Cannot validate certificate. Error is: " + e.getMessage() + "\r\nCertificate (PEM, hex): "
|
||||||
|
+ DatatypeConverter.printHexBinary(certPEM));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean validateCertificate(Certificate cert) {
|
||||||
|
boolean isValidated;
|
||||||
|
|
||||||
|
if (cert == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
KeyStore keyStore = getTrustStore();
|
||||||
|
|
||||||
|
PKIXParameters parms = new PKIXParameters(keyStore);
|
||||||
|
parms.setRevocationEnabled(false);
|
||||||
|
|
||||||
|
CertPathValidator certValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType()); // PKIX
|
||||||
|
|
||||||
|
ArrayList<Certificate> start = new ArrayList<>();
|
||||||
|
start.add(cert);
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT);
|
||||||
|
CertPath certPath = certFactory.generateCertPath(start);
|
||||||
|
|
||||||
|
certValidator.validate(certPath, parms);
|
||||||
|
isValidated = true;
|
||||||
|
} catch (KeyStoreException | InvalidAlgorithmParameterException | NoSuchAlgorithmException
|
||||||
|
| CertificateException | CertPathValidatorException | CryptoException e) {
|
||||||
|
logger.error("Cannot validate certificate. Error is: " + e.getMessage() + "\r\nCertificate"
|
||||||
|
+ cert.toString());
|
||||||
|
isValidated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValidated;
|
||||||
|
} // validateCertificate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security Level determines the elliptic curve used in key generation
|
||||||
|
*
|
||||||
|
* @param securityLevel currently 256 or 384
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
void setSecurityLevel(final int securityLevel) throws InvalidArgumentException {
|
||||||
|
logger.trace(format("setSecurityLevel to %d", securityLevel));
|
||||||
|
|
||||||
|
if (securityCurveMapping.isEmpty()) {
|
||||||
|
throw new InvalidArgumentException("Security curve mapping has no entries.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!securityCurveMapping.containsKey(securityLevel)) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String sp = "";
|
||||||
|
for (int x : securityCurveMapping.keySet()) {
|
||||||
|
sb.append(sp).append(x);
|
||||||
|
|
||||||
|
sp = ", ";
|
||||||
|
|
||||||
|
}
|
||||||
|
throw new InvalidArgumentException(format("Illegal security level: %d. Valid values are: %s", securityLevel, sb.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
String lcurveName = securityCurveMapping.get(securityLevel);
|
||||||
|
|
||||||
|
logger.debug(format("Mapped curve strength %d to %s", securityLevel, lcurveName));
|
||||||
|
|
||||||
|
X9ECParameters params = ECNamedCurveTable.getByName(lcurveName);
|
||||||
|
//Check if can match curve name to requested strength.
|
||||||
|
if (params == null) {
|
||||||
|
|
||||||
|
InvalidArgumentException invalidArgumentException = new InvalidArgumentException(
|
||||||
|
format("Curve %s defined for security strength %d was not found.", curveName, securityLevel));
|
||||||
|
|
||||||
|
logger.error(invalidArgumentException);
|
||||||
|
throw invalidArgumentException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
curveName = lcurveName;
|
||||||
|
this.securityLevel = securityLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHashAlgorithm(String algorithm) throws InvalidArgumentException {
|
||||||
|
if (isNullOrEmpty(algorithm)
|
||||||
|
|| !("SHA2".equals(algorithm) || "SHA3".equals(algorithm))) {
|
||||||
|
throw new InvalidArgumentException("Illegal Hash function family: "
|
||||||
|
+ algorithm + " - must be either SHA2 or SHA3");
|
||||||
|
}
|
||||||
|
|
||||||
|
hashAlgorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair keyGen() throws CryptoException {
|
||||||
|
return ecdsaKeyGen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyPair ecdsaKeyGen() throws CryptoException {
|
||||||
|
return generateKey("EC", curveName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyPair generateKey(String encryptionName, String curveName) throws CryptoException {
|
||||||
|
try {
|
||||||
|
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(curveName);
|
||||||
|
KeyPairGenerator g = SECURITY_PROVIDER == null ? KeyPairGenerator.getInstance(encryptionName) :
|
||||||
|
KeyPairGenerator.getInstance(encryptionName, SECURITY_PROVIDER);
|
||||||
|
g.initialize(ecGenSpec, new SecureRandom());
|
||||||
|
return g.generateKeyPair();
|
||||||
|
} catch (Exception exp) {
|
||||||
|
throw new CryptoException("Unable to generate key pair", exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes an ECDSA signature and returns a two element BigInteger array.
|
||||||
|
*
|
||||||
|
* @param signature ECDSA signature bytes.
|
||||||
|
* @return BigInteger array for the signature's r and s values
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static BigInteger[] decodeECDSASignature(byte[] signature) throws Exception {
|
||||||
|
|
||||||
|
try (ByteArrayInputStream inStream = new ByteArrayInputStream(signature)) {
|
||||||
|
ASN1InputStream asnInputStream = new ASN1InputStream(inStream);
|
||||||
|
ASN1Primitive asn1 = asnInputStream.readObject();
|
||||||
|
|
||||||
|
BigInteger[] sigs = new BigInteger[2];
|
||||||
|
int count = 0;
|
||||||
|
if (asn1 instanceof ASN1Sequence) {
|
||||||
|
ASN1Sequence asn1Sequence = (ASN1Sequence) asn1;
|
||||||
|
ASN1Encodable[] asn1Encodables = asn1Sequence.toArray();
|
||||||
|
for (ASN1Encodable asn1Encodable : asn1Encodables) {
|
||||||
|
ASN1Primitive asn1Primitive = asn1Encodable.toASN1Primitive();
|
||||||
|
if (asn1Primitive instanceof ASN1Integer) {
|
||||||
|
ASN1Integer asn1Integer = (ASN1Integer) asn1Primitive;
|
||||||
|
BigInteger integer = asn1Integer.getValue();
|
||||||
|
if (count < 2) {
|
||||||
|
sigs[count] = integer;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count != 2) {
|
||||||
|
throw new CryptoException(format("Invalid ECDSA signature. Expected count of 2 but got: %d. Signature is: %s", count,
|
||||||
|
DatatypeConverter.printHexBinary(signature)));
|
||||||
|
}
|
||||||
|
return sigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign data with the specified elliptic curve private key.
|
||||||
|
*
|
||||||
|
* @param privateKey elliptic curve private key.
|
||||||
|
* @param data data to sign
|
||||||
|
* @return the signed data.
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
private byte[] ecdsaSignToBytes(ECPrivateKey privateKey, byte[] data) throws CryptoException {
|
||||||
|
if (data == null) {
|
||||||
|
throw new CryptoException("Data that to be signed is null.");
|
||||||
|
}
|
||||||
|
if (data.length == 0) {
|
||||||
|
throw new CryptoException("Data to be signed was empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
X9ECParameters params = ECNamedCurveTable.getByName(curveName);
|
||||||
|
BigInteger curveN = params.getN();
|
||||||
|
|
||||||
|
Signature sig = SECURITY_PROVIDER == null ? Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM) :
|
||||||
|
Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM, SECURITY_PROVIDER);
|
||||||
|
sig.initSign(privateKey);
|
||||||
|
sig.update(data);
|
||||||
|
byte[] signature = sig.sign();
|
||||||
|
|
||||||
|
BigInteger[] sigs = preventMalleability(decodeECDSASignature(signature), curveN);
|
||||||
|
|
||||||
|
try (ByteArrayOutputStream s = new ByteArrayOutputStream()) {
|
||||||
|
|
||||||
|
DERSequenceGenerator seq = new DERSequenceGenerator(s);
|
||||||
|
seq.addObject(new ASN1Integer(sigs[0]));
|
||||||
|
seq.addObject(new ASN1Integer(sigs[1]));
|
||||||
|
seq.close();
|
||||||
|
return s.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CryptoException("Could not sign the message using private key", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ClassCastException if the supplied private key is not of type {@link ECPrivateKey}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public byte[] sign(PrivateKey key, byte[] data) throws CryptoException {
|
||||||
|
return ecdsaSignToBytes((ECPrivateKey) key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger[] preventMalleability(BigInteger[] sigs, BigInteger curveN) {
|
||||||
|
BigInteger cmpVal = curveN.divide(BigInteger.valueOf(2L));
|
||||||
|
|
||||||
|
BigInteger sval = sigs[1];
|
||||||
|
|
||||||
|
if (sval.compareTo(cmpVal) > 0) {
|
||||||
|
|
||||||
|
sigs[1] = curveN.subtract(sval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generateCertificationRequest
|
||||||
|
*
|
||||||
|
* @param subject The subject to be added to the certificate
|
||||||
|
* @param pair Public private key pair
|
||||||
|
* @return PKCS10CertificationRequest Certificate Signing Request.
|
||||||
|
* @throws OperatorCreationException
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateCertificationRequest(String subject, KeyPair pair)
|
||||||
|
throws InvalidArgumentException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
|
||||||
|
new X500Principal("CN=" + subject), pair.getPublic());
|
||||||
|
|
||||||
|
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withECDSA");
|
||||||
|
|
||||||
|
if (null != SECURITY_PROVIDER) {
|
||||||
|
csBuilder.setProvider(SECURITY_PROVIDER);
|
||||||
|
}
|
||||||
|
ContentSigner signer = csBuilder.build(pair.getPrivate());
|
||||||
|
|
||||||
|
return certificationRequestToPEM(p10Builder.build(signer));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
logger.error(e);
|
||||||
|
throw new InvalidArgumentException(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* certificationRequestToPEM - Convert a PKCS10CertificationRequest to PEM
|
||||||
|
* format.
|
||||||
|
*
|
||||||
|
* @param csr The Certificate to convert
|
||||||
|
* @return An equivalent PEM format certificate.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String certificationRequestToPEM(PKCS10CertificationRequest csr) throws IOException {
|
||||||
|
PemObject pemCSR = new PemObject("CERTIFICATE REQUEST", csr.getEncoded());
|
||||||
|
|
||||||
|
StringWriter str = new StringWriter();
|
||||||
|
JcaPEMWriter pemWriter = new JcaPEMWriter(str);
|
||||||
|
pemWriter.writeObject(pemCSR);
|
||||||
|
pemWriter.close();
|
||||||
|
str.close();
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// public PrivateKey ecdsaKeyFromPrivate(byte[] key) throws CryptoException {
|
||||||
|
// try {
|
||||||
|
// EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(key);
|
||||||
|
// KeyFactory generator = KeyFactory.getInstance("ECDSA", SECURITY_PROVIDER_NAME);
|
||||||
|
// PrivateKey privateKey = generator.generatePrivate(privateKeySpec);
|
||||||
|
//
|
||||||
|
// return privateKey;
|
||||||
|
// } catch (Exception exp) {
|
||||||
|
// throw new CryptoException("Unable to convert byte[] into PrivateKey", exp);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] hash(byte[] input) {
|
||||||
|
Digest digest = getHashDigest();
|
||||||
|
byte[] retValue = new byte[digest.getDigestSize()];
|
||||||
|
digest.update(input, 0, input.length);
|
||||||
|
digest.doFinal(retValue, 0);
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CryptoSuiteFactory getCryptoSuiteFactory() {
|
||||||
|
return HLSDKJCryptoSuiteFactory.instance(); //Factory for this crypto suite.
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AtomicBoolean inited = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
public void init() throws CryptoException, InvalidArgumentException {
|
||||||
|
if (inited.getAndSet(true)) {
|
||||||
|
throw new InvalidArgumentException("Crypto suite already initialized");
|
||||||
|
} else {
|
||||||
|
resetConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Digest getHashDigest() {
|
||||||
|
if ("SHA3".equals(hashAlgorithm)) {
|
||||||
|
return new SHA3Digest();
|
||||||
|
} else {
|
||||||
|
// Default to SHA2
|
||||||
|
return new SHA256Digest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Shake256 hash the supplied byte data.
|
||||||
|
// *
|
||||||
|
// * @param in byte array to be hashed.
|
||||||
|
// * @param bitLength of the result.
|
||||||
|
// * @return the hashed byte data.
|
||||||
|
// */
|
||||||
|
// public byte[] shake256(byte[] in, int bitLength) {
|
||||||
|
//
|
||||||
|
// if (bitLength % 8 != 0) {
|
||||||
|
// throw new IllegalArgumentException("bit length not modulo 8");
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final int byteLen = bitLength / 8;
|
||||||
|
//
|
||||||
|
// SHAKEDigest sd = new SHAKEDigest(256);
|
||||||
|
//
|
||||||
|
// sd.update(in, 0, in.length);
|
||||||
|
//
|
||||||
|
// byte[] out = new byte[byteLen];
|
||||||
|
//
|
||||||
|
// sd.doFinal(out, 0, byteLen);
|
||||||
|
//
|
||||||
|
// return out;
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets curve name, hash algorithm and cert factory. Call this method when a config value changes
|
||||||
|
*
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
private void resetConfiguration() throws CryptoException, InvalidArgumentException {
|
||||||
|
|
||||||
|
setSecurityLevel(securityLevel);
|
||||||
|
|
||||||
|
setHashAlgorithm(hashAlgorithm);
|
||||||
|
|
||||||
|
try {
|
||||||
|
cf = CertificateFactory.getInstance(CERTIFICATE_FORMAT);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
CryptoException ex = new CryptoException("Cannot initialize " + CERTIFICATE_FORMAT + " certificate factory. Error = " + e.getMessage(), e);
|
||||||
|
logger.error(ex.getMessage(), ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /* (non-Javadoc)
|
||||||
|
// * @see org.hyperledger.fabric.sdk.security.CryptoSuite#setProperties(java.util.Properties)
|
||||||
|
// */
|
||||||
|
// @Override
|
||||||
|
void setProperties(Properties properties) throws CryptoException, InvalidArgumentException {
|
||||||
|
if (properties == null) {
|
||||||
|
throw new InvalidArgumentException("properties must not be null");
|
||||||
|
}
|
||||||
|
// if (properties != null) {
|
||||||
|
hashAlgorithm = Optional.ofNullable(properties.getProperty(Config.HASH_ALGORITHM)).orElse(hashAlgorithm);
|
||||||
|
String secLevel = Optional.ofNullable(properties.getProperty(Config.SECURITY_LEVEL)).orElse(Integer.toString(securityLevel));
|
||||||
|
securityLevel = Integer.parseInt(secLevel);
|
||||||
|
if (properties.containsKey(Config.SECURITY_CURVE_MAPPING)) {
|
||||||
|
securityCurveMapping = Config.parseSecurityCurveMappings(properties.getProperty(Config.SECURITY_CURVE_MAPPING));
|
||||||
|
} else {
|
||||||
|
securityCurveMapping = config.getSecurityCurveMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String providerName = properties.containsKey(Config.SECURITY_PROVIDER_CLASS_NAME) ?
|
||||||
|
properties.getProperty(Config.SECURITY_PROVIDER_CLASS_NAME) :
|
||||||
|
config.getSecurityProviderClassName();
|
||||||
|
|
||||||
|
try {
|
||||||
|
SECURITY_PROVIDER = setUpExplicitProvider(providerName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new InvalidArgumentException(format("Getting provider for class name: %s", providerName), e);
|
||||||
|
|
||||||
|
}
|
||||||
|
CERTIFICATE_FORMAT = Optional.ofNullable(properties.getProperty(Config.CERTIFICATE_FORMAT)).orElse(CERTIFICATE_FORMAT);
|
||||||
|
DEFAULT_SIGNATURE_ALGORITHM = Optional.ofNullable(properties.getProperty(Config.SIGNATURE_ALGORITHM)).orElse(DEFAULT_SIGNATURE_ALGORITHM);
|
||||||
|
|
||||||
|
resetConfiguration();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.hyperledger.fabric.sdk.security.CryptoSuite#getProperties()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Properties getProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty(Config.HASH_ALGORITHM, hashAlgorithm);
|
||||||
|
properties.setProperty(Config.SECURITY_LEVEL, Integer.toString(securityLevel));
|
||||||
|
properties.setProperty(Config.CERTIFICATE_FORMAT, CERTIFICATE_FORMAT);
|
||||||
|
properties.setProperty(Config.SIGNATURE_ALGORITHM, DEFAULT_SIGNATURE_ALGORITHM);
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] certificateToDER(String certificatePEM) {
|
||||||
|
|
||||||
|
byte[] content = null;
|
||||||
|
|
||||||
|
try (PemReader pemReader = new PemReader(new StringReader(certificatePEM))) {
|
||||||
|
final PemObject pemObject = pemReader.readPemObject();
|
||||||
|
content = pemObject.getContent();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
// best attempt
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016,2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.security;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.CryptoException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.InvalidArgumentException;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All packages for PKI key creation/signing/verification implement this interface
|
||||||
|
*/
|
||||||
|
public interface CryptoSuite {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Crypto Suite Factory for this implementation.
|
||||||
|
*
|
||||||
|
* @return MUST return the one and only one instance of a factory that produced this crypto suite.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CryptoSuiteFactory getCryptoSuiteFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link Properties} object containing implementation specific key generation properties
|
||||||
|
*/
|
||||||
|
Properties getProperties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Certificate Authority certificates to be used when validating a certificate chain of trust
|
||||||
|
*
|
||||||
|
* @param certificates A collection of {@link Certificate}s
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
void loadCACertificates(Collection<Certificate> certificates) throws CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Certificate Authority certificates to be used when validating a certificate chain of trust.
|
||||||
|
*
|
||||||
|
* @param certificates a collection of certificates in PEM format
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
void loadCACertificatesAsBytes(Collection<byte[]> certificates) throws CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a key.
|
||||||
|
*
|
||||||
|
* @return the generated key.
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
KeyPair keyGen() throws CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign the specified byte string.
|
||||||
|
*
|
||||||
|
* @param key the {@link PrivateKey} to be used for signing
|
||||||
|
* @param plainText the byte string to sign
|
||||||
|
* @return the signed data.
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
byte[] sign(PrivateKey key, byte[] plainText) throws CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the specified signature
|
||||||
|
*
|
||||||
|
* @param certificate the certificate of the signer as the contents of the PEM file
|
||||||
|
* @param signatureAlgorithm the algorithm used to create the signature.
|
||||||
|
* @param signature the signature to verify
|
||||||
|
* @param plainText the original text that is to be verified
|
||||||
|
* @return {@code true} if the signature is successfully verified; otherwise {@code false}.
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
boolean verify(byte[] certificate, String signatureAlgorithm, byte[] signature, byte[] plainText) throws CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash the specified text byte data.
|
||||||
|
*
|
||||||
|
* @param plainText the text to hash
|
||||||
|
* @return the hashed data.
|
||||||
|
*/
|
||||||
|
byte[] hash(byte[] plainText);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a CertificationRequest
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* @param keypair
|
||||||
|
* @return String in PEM format for certificate request.
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
String generateCertificationRequest(String user, KeyPair keypair) throws InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert bytes in PEM format to Certificate.
|
||||||
|
*
|
||||||
|
* @param certBytes
|
||||||
|
* @return Certificate
|
||||||
|
* @throws CryptoException
|
||||||
|
*/
|
||||||
|
Certificate bytesToCertificate(byte[] certBytes) throws CryptoException, CryptoException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CryptoSuite factory. Currently {@link #getCryptoSuite} will always
|
||||||
|
* give you a {@link CryptoPrimitives} object
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Factory {
|
||||||
|
private Factory() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a crypto suite with the default factory with default settings.
|
||||||
|
* Settings which can define such parameters such as curve strength, are specific to the crypto factory.
|
||||||
|
*
|
||||||
|
* @return Default crypto suite.
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
* @throws InstantiationException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
* @throws NoSuchMethodException
|
||||||
|
* @throws InvocationTargetException
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static CryptoSuite getCryptoSuite() throws IllegalAccessException, InstantiationException,
|
||||||
|
ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException,
|
||||||
|
InvocationTargetException {
|
||||||
|
return CryptoSuiteFactory.getDefault().getCryptoSuite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a crypto suite with the default factory with settings defined by properties
|
||||||
|
* Properties are uniquely defined by the specific crypto factory.
|
||||||
|
*
|
||||||
|
* @param properties properties that define suite characteristics such as strength, curve, hashing .
|
||||||
|
* @return
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
* @throws InstantiationException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
* @throws NoSuchMethodException
|
||||||
|
* @throws InvocationTargetException
|
||||||
|
*/
|
||||||
|
public static CryptoSuite getCryptoSuite(Properties properties) throws IllegalAccessException, InstantiationException,
|
||||||
|
ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException,
|
||||||
|
InvocationTargetException {
|
||||||
|
return CryptoSuiteFactory.getDefault().getCryptoSuite(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2016,2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.security;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.CryptoException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.InvalidArgumentException;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to produce a set of crypto suite implementations offering differing cryptographic algorithms and strengths.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface CryptoSuiteFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set as the default security provider then default crypto suite will not use explicit
|
||||||
|
* provider
|
||||||
|
*/
|
||||||
|
|
||||||
|
String DEFAULT_JDK_PROVIDER = "org.hyperledger.fabric.sdk.security.default_jdk_provider";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a crypto suite by specified by these properties.
|
||||||
|
* Properties are unique to each Crypto Suite implementation.
|
||||||
|
*
|
||||||
|
* @param properties
|
||||||
|
* @return
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
|
||||||
|
CryptoSuite getCryptoSuite(Properties properties) throws CryptoException, InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a default crypto suite
|
||||||
|
* @return
|
||||||
|
* @throws CryptoException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
|
||||||
|
CryptoSuite getCryptoSuite() throws CryptoException, InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will return the default Crypto Suite Factory implementation.
|
||||||
|
* Can be overwritten by org.hyperledger.fabric.sdk.crypto.default_crypto_suite_factory property.
|
||||||
|
* see
|
||||||
|
* Classes specified by this property must implement a public static method <b>instance</b> that
|
||||||
|
* returns back a single instance of this factory.
|
||||||
|
*
|
||||||
|
* @return A single instance of a CryptoSuiteFactory.
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws IllegalAccessException
|
||||||
|
* @throws InstantiationException
|
||||||
|
* @throws NoSuchMethodException
|
||||||
|
* @throws InvocationTargetException
|
||||||
|
*/
|
||||||
|
|
||||||
|
static CryptoSuiteFactory getDefault() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
|
||||||
|
return HLSDKJCryptoSuiteFactory.getDefault();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016,2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.security;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.CryptoException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.InvalidArgumentException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.helper.Config;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SDK's Default implementation of CryptoSuiteFactory.
|
||||||
|
*/
|
||||||
|
public class HLSDKJCryptoSuiteFactory implements CryptoSuiteFactory {
|
||||||
|
private static final Config config = Config.getConfig();
|
||||||
|
private static final int SECURITY_LEVEL = config.getSecurityLevel();
|
||||||
|
private static final String HASH_ALGORITHM = config.getHashAlgorithm();
|
||||||
|
|
||||||
|
private HLSDKJCryptoSuiteFactory() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Properties, CryptoSuite> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CryptoSuite getCryptoSuite(Properties properties) throws CryptoException, InvalidArgumentException {
|
||||||
|
|
||||||
|
CryptoSuite ret = cache.get(properties);
|
||||||
|
if (ret == null) {
|
||||||
|
try {
|
||||||
|
CryptoPrimitives cp = new CryptoPrimitives();
|
||||||
|
cp.setProperties(properties);
|
||||||
|
cp.init();
|
||||||
|
ret = cp;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CryptoException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.put(properties, ret);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CryptoSuite getCryptoSuite() throws CryptoException, InvalidArgumentException {
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.put(Config.SECURITY_LEVEL, SECURITY_LEVEL);
|
||||||
|
properties.put(Config.HASH_ALGORITHM, HASH_ALGORITHM);
|
||||||
|
|
||||||
|
return getCryptoSuite(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final HLSDKJCryptoSuiteFactory INSTANCE = new HLSDKJCryptoSuiteFactory();
|
||||||
|
|
||||||
|
static synchronized HLSDKJCryptoSuiteFactory instance() {
|
||||||
|
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CryptoSuiteFactory theFACTORY = null; // one and only factory.
|
||||||
|
|
||||||
|
static final synchronized CryptoSuiteFactory getDefault() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
|
||||||
|
if (null == theFACTORY) {
|
||||||
|
|
||||||
|
String cf = config.getDefaultCryptoSuiteFactory();
|
||||||
|
if (null == cf || cf.isEmpty() || cf.equals(HLSDKJCryptoSuiteFactory.class.getName())) { // Use this class as the factory.
|
||||||
|
|
||||||
|
theFACTORY = HLSDKJCryptoSuiteFactory.instance();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Invoke static method instance on factory class specified by config properties.
|
||||||
|
// In this case this class will no longer be used as the factory.
|
||||||
|
|
||||||
|
Class<?> aClass = Class.forName(cf);
|
||||||
|
|
||||||
|
Method method = aClass.getMethod("instance");
|
||||||
|
Object theFACTORYObject = method.invoke(null);
|
||||||
|
if (null == theFACTORYObject) {
|
||||||
|
throw new InstantiationException(String.format("Class specified by %s has instance method returning null. Expected object implementing CryptoSuiteFactory interface.", cf));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(theFACTORYObject instanceof CryptoSuiteFactory)) {
|
||||||
|
|
||||||
|
throw new InstantiationException(String.format("Class specified by %s has instance method returning a class %s which does not implement interface CryptoSuiteFactory ",
|
||||||
|
cf, theFACTORYObject.getClass().getName()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
theFACTORY = (CryptoSuiteFactory) theFACTORYObject;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return theFACTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package com.chinaunicom.mall.ebtp.common.crypto.tenderfee;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.exception.InvalidArgumentException;
|
||||||
|
import com.chinaunicom.mall.ebtp.common.crypto.service.CrypServiceImpl;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bouncycastle.crypto.CryptoException;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
@Slf4j
|
||||||
|
public class test {
|
||||||
|
|
||||||
|
// 私钥文件路径 - 加密
|
||||||
|
private static String PEM_PATH = "admin_certPrivate.pem";
|
||||||
|
|
||||||
|
// 证书文件路径 - 解密
|
||||||
|
private static String CRT_PATH = "admin.crt";
|
||||||
|
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, CryptoException, InvalidArgumentException, UnsupportedEncodingException {
|
||||||
|
|
||||||
|
//String token = AccessToken.tokenCreate();
|
||||||
|
|
||||||
|
URL pem = test.class.getClassLoader().getResource(PEM_PATH);
|
||||||
|
URL crt = test.class.getClassLoader().getResource(CRT_PATH);
|
||||||
|
|
||||||
|
PEM_PATH = pem.getPath();
|
||||||
|
CRT_PATH = crt.getPath();
|
||||||
|
|
||||||
|
|
||||||
|
// example of bean entity
|
||||||
|
String pemVal = "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
|
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjF+tq8oc1tNjot69\n" +
|
||||||
|
"9OQgzr7Dqg1OkVo4PY4tKBL8+82hRANCAARKLIuOloTZe1B0J0k+CITZdsx8Gham\n" +
|
||||||
|
"JTuxCRGLdCLpq6wHHGEqWn9VDkwk5eX6OYQxYuBRiPZp7gP/njpx5CkF\n" +
|
||||||
|
"-----END PRIVATE KEY-----";
|
||||||
|
|
||||||
|
// 需正确设置bean的@JSONField,以确保解析到的字段名称与文档一致
|
||||||
|
// List<BidTenderFeeBaseParam> paramList = new ArrayList<>();
|
||||||
|
// BidTenderFeeBaseParam bean = new BidTenderFeeBaseParam();
|
||||||
|
// bean.setTenderId("8533");
|
||||||
|
// bean.setShoppingCartId("L3307");
|
||||||
|
// bean.setAmount("1000");
|
||||||
|
// bean.setTpId("L3307A");
|
||||||
|
// bean.setSectionId("1111");
|
||||||
|
//
|
||||||
|
// paramList.add(bean);
|
||||||
|
// // 用于签名的Bean将被signObject转换为json(String,然后转换为byte[]),请确保该json只包含文档规定的业务字段,且“SGIN"不应包含其中
|
||||||
|
// String signatureOfBean = CrypServiceImpl.signObject2(paramList,pemVal);//CrypService.signObject(paramList);
|
||||||
|
// System.out.println("signature of Bean: "+signatureOfBean);
|
||||||
|
// boolean isOkBean = CrypServiceImpl.verifyObject(signatureOfBean,paramList);
|
||||||
|
// System.out.println("verify result of Bean: "+ isOkBean);
|
||||||
|
// String signatureOfBean = signObject(paramList,PEM_PATH);
|
||||||
|
// // 生成的签名现在可以追加到签名字段
|
||||||
|
// System.out.println("signature of Bean: "+signatureOfBean);
|
||||||
|
// boolean isOkBean = verifyObject(signatureOfBean,paramList,CRT_PATH);
|
||||||
|
// System.out.println("verify result of Bean: "+ isOkBean);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user