From d9ca753151169ec00df774e0a00802e0914c4e1f Mon Sep 17 00:00:00 2001 From: zhangqinbin <181961702@qq.com> Date: Mon, 24 Jan 2022 17:04:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8C=BA=E5=9D=97=E9=93=BE=E5=8A=A0=E5=AF=86?= =?UTF-8?q?=20=E4=BB=8E=E8=84=9A=E6=89=8B=E6=9E=B6=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=88=B0=E6=89=A9=E5=B1=95=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crypto/exception/BaseException.java | 33 + .../crypto/exception/CryptoException.java | 29 + .../exception/InvalidArgumentException.java | 34 + .../crypconfigure/crypto/helper/Config.java | 606 +++++++++++ .../crypto/helper/DiagnosticFileDumper.java | 204 ++++ .../crypto/security/CryptoPrimitives.java | 976 ++++++++++++++++++ .../crypto/security/CryptoSuite.java | 170 +++ .../crypto/security/CryptoSuiteFactory.java | 82 ++ .../security/HLSDKJCryptoSuiteFactory.java | 121 +++ .../crypto/service/CrypServiceImpl.java | 248 +++++ .../crypconfigure/crypto/tenderfee/test.java | 75 ++ 11 files changed, 2578 insertions(+) create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/BaseException.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/CryptoException.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/InvalidArgumentException.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/Config.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/DiagnosticFileDumper.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoPrimitives.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuite.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuiteFactory.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/HLSDKJCryptoSuiteFactory.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/service/CrypServiceImpl.java create mode 100644 src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/tenderfee/test.java diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/BaseException.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/BaseException.java new file mode 100644 index 0000000..323e3c1 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/BaseException.java @@ -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.extend.crypconfigure.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); + } + +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/CryptoException.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/CryptoException.java new file mode 100644 index 0000000..638c0fb --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/CryptoException.java @@ -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.extend.crypconfigure.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); + } + +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/InvalidArgumentException.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/InvalidArgumentException.java new file mode 100644 index 0000000..47fcdd0 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/exception/InvalidArgumentException.java @@ -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.extend.crypconfigure.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); + } +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/Config.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/Config.java new file mode 100644 index 0000000..35833f9 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/Config.java @@ -0,0 +1,606 @@ +/* + * 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.extend.crypconfigure.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.com.chinaunicom.ebtp.mall.cloud.attachment.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.com.chinaunicom.ebtp.mall.cloud.attachment.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.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.proposal.wait.time"; + public static final String CHANNEL_CONFIG_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.channelconfig.wait_time"; + public static final String TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.client.transaction_cleanup_up_timeout_wait_time"; + public static final String ORDERER_RETRY_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.orderer_retry.wait_time"; + public static final String ORDERER_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.orderer.ordererWaitTimeMilliSecs"; + public static final String PEER_EVENT_REGISTRATION_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.peer.eventRegistration.wait_time"; + public static final String PEER_EVENT_RETRY_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.peer.retry_wait_time"; + public static final String PEER_EVENT_RECONNECTION_WARNING_RATE = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.peer.reconnection_warning_rate"; + public static final String GENESISBLOCK_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.channel.genesisblock_wait_time"; + /** + * Crypto configuration settings -- settings should not be changed. + **/ + public static final String DEFAULT_CRYPTO_SUITE_FACTORY = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.crypto.default_crypto_suite_factory"; + public static final String SECURITY_LEVEL = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.security_level"; + public static final String SECURITY_PROVIDER_CLASS_NAME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.security_provider_class_name"; + public static final String SECURITY_CURVE_MAPPING = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.security_curve_mapping"; + public static final String HASH_ALGORITHM = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.hash_algorithm"; + public static final String ASYMMETRIC_KEY_TYPE = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.crypto.asymmetric_key_type"; + public static final String CERTIFICATE_FORMAT = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.crypto.certificate_format"; + public static final String SIGNATURE_ALGORITHM = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.crypto.default_signature_algorithm"; + /** + * Logging settings + **/ + public static final String MAX_LOG_STRING_LENGTH = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.log.stringlengthmax"; + public static final String EXTRALOGLEVEL = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.log.extraloglevel"; // ORG_HYPERLEDGER_FABRIC_SDK_LOG_EXTRALOGLEVEL + public static final String LOGGERLEVEL = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.loglevel"; // ORG_HYPERLEDGER_FABRIC_SDK_LOGLEVEL=TRACE,DEBUG + public static final String DIAGNOTISTIC_FILE_DIRECTORY = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.diagnosticFileDir"; //ORG_HYPERLEDGER_FABRIC_SDK_DIAGNOSTICFILEDIR + + /** + * Connections settings + */ + + public static final String CONN_SSL_PROVIDER = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.connections.ssl.sslProvider"; + public static final String CONN_SSL_NEGTYPE = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.connections.ssl.negotiationType"; + + /** + * Default HFClient thread executor settings. + */ + + public static final String CLIENT_THREAD_EXECUTOR_COREPOOLSIZE = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.client.thread_executor_corepoolsize"; + public static final String CLIENT_THREAD_EXECUTOR_MAXIMUMPOOLSIZE = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.client.thread_executor_maximumpoolsize"; + public static final String CLIENT_THREAD_EXECUTOR_KEEPALIVETIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.client.thread_executor_keepalivetime"; + public static final String CLIENT_THREAD_EXECUTOR_KEEPALIVETIMEUNIT = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.client.thread_executor_keepalivetimeunit"; + + /** + * Miscellaneous settings + **/ + public static final String PROPOSAL_CONSISTENCY_VALIDATION = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.proposal.consistency_validation"; + + public static final String SERVICE_DISCOVER_FREQ_SECONDS = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.service_discovery.frequency_sec"; + public static final String SERVICE_DISCOVER_WAIT_TIME = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.service_discovery.discovery_wait_time"; + public static final String SERVICE_DISCOVER_AS_LOCALHOST = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.service_discovery.as_localhost"; + + public static final String LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.lifecycle.chaincode_endorsement_plugin"; //ORG_HYPERLEDGER_FABRIC_SDK_LIFECYCLE_CHAINCODE_ENDORSEMENT_PLUGIN + + public static final String LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.lifecycle.chaincode_validation_plugin"; //ORG_HYPERLEDGER_FABRIC_SDK_LIFECYCLE_CHAINCODE_VALIDATION_PLUGIN + public static final String LIFECYCLE_INITREQUIREDDEFAULT = "org.hyperledger.fabric.com.chinaunicom.ebtp.mall.cloud.attachment.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.com.chinaunicom.ebtp.mall.cloud.attachment.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 curveMapping = null; + + /** + * Get a mapping from strength to curve desired. + * + * @return mapping from strength to curve name to use. + */ + public Map getSecurityCurveMapping() { + + if (curveMapping == null) { + + curveMapping = parseSecurityCurveMappings(getProperty(SECURITY_CURVE_MAPPING)); + } + + return Collections.unmodifiableMap(curveMapping); + } + + public static Map parseSecurityCurveMappings(final String property) { + Map 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 + *

+ * 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/DiagnosticFileDumper.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/DiagnosticFileDumper.java new file mode 100644 index 0000000..a5b64ba --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/helper/DiagnosticFileDumper.java @@ -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.extend.crypconfigure.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 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 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() { + @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]); + } + +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoPrimitives.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoPrimitives.java new file mode 100644 index 0000000..445d150 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoPrimitives.java @@ -0,0 +1,976 @@ +/* + * 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.extend.crypconfigure.crypto.security; + +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.InvalidArgumentException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.helper.Config; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.helper.DiagnosticFileDumper; +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.extend.crypconfigure.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 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 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 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 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 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.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.security.CryptoSuite#loadCACertificatesAsBytes(java.util.Collection) + */ + @Override + public void loadCACertificatesAsBytes(Collection certificatesBytes) throws CryptoException { + if (certificatesBytes == null || certificatesBytes.size() == 0) { + throw new CryptoException("List of CA certificates is empty. Nothing to load."); + } + + ArrayList 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 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.com.chinaunicom.ebtp.mall.cloud.attachment.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.com.chinaunicom.ebtp.mall.cloud.attachment.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; + } + +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuite.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuite.java new file mode 100644 index 0000000..6067d4e --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuite.java @@ -0,0 +1,170 @@ +/* + * 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.extend.crypconfigure.crypto.security; + + + +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.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 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 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); + } + + } +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuiteFactory.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuiteFactory.java new file mode 100644 index 0000000..2b5803f --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/CryptoSuiteFactory.java @@ -0,0 +1,82 @@ +/* + * + * 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.extend.crypconfigure.crypto.security; + + +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.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.com.chinaunicom.ebtp.mall.cloud.attachment.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.com.chinaunicom.ebtp.mall.cloud.attachment.sdk.crypto.default_crypto_suite_factory property. + * see + * Classes specified by this property must implement a public static method instance 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(); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/HLSDKJCryptoSuiteFactory.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/HLSDKJCryptoSuiteFactory.java new file mode 100644 index 0000000..b006c8f --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/security/HLSDKJCryptoSuiteFactory.java @@ -0,0 +1,121 @@ +/* + * 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.extend.crypconfigure.crypto.security; + + + +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.InvalidArgumentException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.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 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; + } + +} \ No newline at end of file diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/service/CrypServiceImpl.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/service/CrypServiceImpl.java new file mode 100644 index 0000000..e4f6181 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/service/CrypServiceImpl.java @@ -0,0 +1,248 @@ +package com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.InvalidArgumentException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.security.CryptoPrimitives; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.tenderfee.test; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.util.encoders.Base64; + +import java.io.*; +import java.net.URL; +import java.security.PrivateKey; + +@Slf4j +public class CrypServiceImpl { + + private static final String SIGNING_ALGORITHM = "SHA256withECDSA"; + private static final String DATA_PATH = "C:\\Users\\user\\Downloads\\debian-edu-10.8.0-amd64-netinst.iso.torrent"; + + // 私钥文件路径 - 加密 + 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 { + + 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 HashMap entity, treeMap can also work out, + // but LinkedHashMap is NOT supported +// Map map = new HashMap<>(1); +// map.put("SECTION_ID","L3307"); +// map.put("DOCUMENT_ID","8533"); +// map.put("CONTENT_FILE_HASH","12321123"); +// map.put("TP_ID","L3307A"); +// +// String signature = signObject(map,PEM_PATH); +// System.out.println("signature of Map: "+signature); +// boolean isOk = verifyObject(signature,map,CRT_PATH); +// System.out.println("verify result of Map: "+ isOk); + + // example of bean entity + + String pemVal = "-----BEGIN PRIVATE KEY-----\n" + + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjF+tq8oc1tNjot69\n" + + "9OQgzr7Dqg1OkVo4PY4tKBL8+82hRANCAARKLIuOloTZe1B0J0k+CITZdsx8Gham\n" + + "JTuxCRGLdCLpq6wHHGEqWn9VDkwk5eX6OYQxYuBRiPZp7gP/njpx5CkF\n" + + "-----END PRIVATE KEY-----"; + + String crtVal = "-----BEGIN CERTIFICATE-----\n" + + "MIICgzCCAimgAwIBAgIULDLuWrkCL3UaWO5u7yiu8UghEjEwCgYIKoZIzj0EAwIw\n" + + "ZzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFNoYW5Eb25nMQ4wDAYDVQQHEwVKaU5h\n" + + "bjEYMBYGA1UEChMPYWRtaW50MDgxMmFvcmczMRswGQYDVQQDExJjYS5hZG1pbnQw\n" + + "ODEyYW9yZzMwHhcNMjEwODE3MDExODAwWhcNMjIwODE3MDEyMzAwWjAzMRwwDQYD\n" + + "VQQLEwZjbGllbnQwCwYDVQQLEwRvcmczMRMwEQYDVQQDDAphZG1pbkBvcmczMFkw\n" + + "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESiyLjpaE2XtQdCdJPgiE2XbMfBoWpiU7\n" + + "sQkRi3Qi6ausBxxhKlp/VQ5MJOXl+jmEMWLgUYj2ae4D/546ceQpBaOB5jCB4zAO\n" + + "BgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUcyAwVSk9V615\n" + + "ryVrpykYwJbUs7UwKwYDVR0jBCQwIoAgOIS8Yvvj2hRg0V3+x659Mn60B09bt8Fl\n" + + "eRcf79zjGRkwFAYDVR0RBA0wC4IJSlpaSEpTLTcyMGEGCCoDBAUGBwgBBFV7ImF0\n" + + "dHJzIjp7ImhmLkFmZmlsaWF0aW9uIjoib3JnMyIsImhmLkVucm9sbG1lbnRJRCI6\n" + + "ImFkbWluQG9yZzMiLCJoZi5UeXBlIjoiY2xpZW50In19MAoGCCqGSM49BAMCA0gA\n" + + "MEUCIQCi/6V6gmt4k5MUSfym9RFGqPwmD1hyWmtWKj448PXALAIgQfHHiX+P2M6k\n" + + "GqneZYuHHxazU37s5ZaFBPylOJJEO2Y=\n" + + "-----END CERTIFICATE-----"; + + // 需正确设置bean的@JSONField,以确保解析到的字段名称与文档一致 +// List 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 = signObject2(paramList,pemVal); +// // 生成的签名现在可以追加到签名字段 +// System.out.println("signature of Bean: "+signatureOfBean); +// boolean isOkBean = verifyValue(signatureOfBean,paramList,crtVal); +// System.out.println("verify result of Bean: "+ isOkBean); + + } + + /** + * 验证数据实体 + * + * @param signatureString Base64加密的密钥 + * @param entity 数据实体 + * @param crtKey 证书路径 + * @return + */ + public static boolean verifyValue(String signatureString, Object entity, String crtKey) { + return verifyValue(signatureString, JSON.toJSONBytes(entity, SerializerFeature.MapSortField, SerializerFeature.SortField), crtKey); + } + + /** + * 验证二进制数据串 + * + * @param signatureString Base64加密的密钥 + * @param plainData 源数据 + * @param crtKey 证书路径 + * @return + */ + public static boolean verifyValue(String signatureString, byte[] plainData, String crtKey) { + byte[] signature = Base64.decode(signatureString); + CryptoPrimitives cp = null; + boolean result = false; + try { + cp = new CryptoPrimitives(); + cp.init(); + byte[] crtBytes = crtKey.getBytes(); + result = cp.verify(crtBytes, SIGNING_ALGORITHM, signature, plainData); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException e) { + e.printStackTrace(); + } + return result; + } + + /** + * 验证二进制数据串 + * + * @param signatureString Base64加密的密钥 + * @param plainData 源数据 + * @param crtPath 证书路径 + * @return + */ + public static boolean verifyBytes(String signatureString, byte[] plainData, String crtPath) { + byte[] signature = Base64.decode(signatureString); + CryptoPrimitives cp = null; + boolean result = false; + try { + cp = new CryptoPrimitives(); + cp.init(); + byte[] crtBytes = readAsBytes(crtPath); + result = cp.verify(crtBytes, SIGNING_ALGORITHM, signature, plainData); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException | InvalidArgumentException e) { + e.printStackTrace(); + } + return result; + } + + /** + * 验证数据实体 + * + * @param signatureString Base64加密的密钥 + * @param entity 数据实体 + * @return + */ + public static boolean verifyObject(String signatureString, Object entity) { + URL crt = test.class.getClassLoader().getResource(CRT_PATH); + return verifyObject(signatureString, entity, crt.getPath()); + } + + /** + * 验证数据实体 + * + * @param signatureString Base64加密的密钥 + * @param entity 数据实体 + * @param crtPath 证书路径 + * @return + */ + public static boolean verifyObject(String signatureString, Object entity, String crtPath) { + return verifyBytes(signatureString, JSON.toJSONBytes(entity, SerializerFeature.MapSortField, SerializerFeature.SortField), crtPath); + } + + /** + * 生成签名 + * + * @param object 数据实体 + * @return Base64加密的密钥 + */ + public static String signObject(Object object) { + URL pem = test.class.getClassLoader().getResource(PEM_PATH); + return signObject(object, pem.getPath()); + } + + /** + * 生成签名 + * + * @param object 数据实体 + * @param privateVal 密钥路径 + * @return Base64加密的密钥 + */ + public static String signObject2(Object object, String privateVal) { + CryptoPrimitives cp = null; + byte[] signature = null; + try { + cp = new CryptoPrimitives(); + cp.init(); + byte[] pemBytes = privateVal.getBytes(); + PrivateKey key = cp.bytesToPrivateKey(pemBytes); + signature = cp.sign(key, JSON.toJSONBytes(object, SerializerFeature.MapSortField, SerializerFeature.SortField)); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException | InvalidArgumentException e) { + e.printStackTrace(); + } + return signature == null ? "" : Base64.toBase64String(signature); + } + + /** + * 生成签名 + * + * @param object 数据实体 + * @param privatePath 密钥路径 + * @return Base64加密的密钥 + */ + public static String signObject(Object object, String privatePath) { + CryptoPrimitives cp = null; + byte[] signature = null; + try { + cp = new CryptoPrimitives(); + cp.init(); + byte[] pemBytes = readAsBytes(privatePath); + PrivateKey key = cp.bytesToPrivateKey(pemBytes); + signature = cp.sign(key, JSON.toJSONBytes(object, SerializerFeature.MapSortField, SerializerFeature.SortField)); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.CryptoException | InvalidArgumentException e) { + e.printStackTrace(); + } + return signature == null ? "" : Base64.toBase64String(signature); + } + + private static byte[] readAsBytes(String path) { + File file = new File(path); + byte[] result = {}; + try (FileInputStream is = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(is);) { + result = IOUtils.toByteArray(bis); + } catch (FileNotFoundException e) { + log.error(path + " not found"); + } catch (IOException e) { + log.error(e.toString()); + } + return result; + } +} diff --git a/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/tenderfee/test.java b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/tenderfee/test.java new file mode 100644 index 0000000..72b5666 --- /dev/null +++ b/src/main/java/com/chinaunicom/mall/ebtp/extend/crypconfigure/crypto/tenderfee/test.java @@ -0,0 +1,75 @@ +package com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.tenderfee; + + +import com.chinaunicom.mall.ebtp.extend.crypconfigure.crypto.exception.InvalidArgumentException; +import com.chinaunicom.mall.ebtp.extend.crypconfigure.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.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class test { + + // 私钥文件路径 - 加密 + private static String PEM_PATH = "admin_certPrivateNew.pem"; + + // 证书文件路径 - 解密 + private static String CRT_PATH = "adminNew.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 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); + List paramList = new ArrayList<>(); + Map bean = new HashMap<>(); + bean.put("tenderId","8533"); + bean.put("shoppingCartId","L3307"); + bean.put("amount","1000"); + bean.put("tpId","L3307A"); + bean.put("sectionId","1111"); + + // 用于签名的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 = CrypServiceImpl.signObject(paramList,PEM_PATH); + // 生成的签名现在可以追加到签名字段 + System.out.println("signature of Bean: "+signatureOfBean); + boolean isOkBean = CrypServiceImpl.verifyObject(signatureOfBean,paramList,CRT_PATH); + System.out.println("verify result of Bean: "+ isOkBean); + + + } + +}