Java tutorial
/* * 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 org.hyperledger.fabric.sdk.security; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.PKIXParameters; import java.security.cert.X509Certificate; import java.security.interfaces.ECPrivateKey; import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import javax.security.auth.x500.X500Principal; import javax.xml.bind.DatatypeConverter; import io.netty.util.internal.ConcurrentSet; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequenceGenerator; 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 org.hyperledger.fabric.sdk.exception.CryptoException; import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; import org.hyperledger.fabric.sdk.helper.Config; import org.hyperledger.fabric.sdk.helper.DiagnosticFileDumper; import static java.lang.String.format; import static org.hyperledger.fabric.sdk.helper.Utils.isNullOrEmpty; public class CryptoPrimitives implements CryptoSuite { private static final Log logger = LogFactory.getLog(CryptoPrimitives.class); private static final Config config = Config.getConfig(); private static final boolean IS_TRACE_LEVEL = logger.isTraceEnabled(); private static final DiagnosticFileDumper diagnosticFileDumper = IS_TRACE_LEVEL ? config.getDiagnosticFileDumper() : null; private String curveName; private CertificateFactory cf; private Provider SECURITY_PROVIDER; private String hashAlgorithm = config.getHashAlgorithm(); private int securityLevel = config.getSecurityLevel(); private String CERTIFICATE_FORMAT = config.getCertificateFormat(); private String DEFAULT_SIGNATURE_ALGORITHM = config.getSignatureAlgorithm(); private Map<Integer, String> securityCurveMapping = config.getSecurityCurveMapping(); // Following configuration settings are hardcoded as they don't deal with any interactions with Fabric MSP and BCCSP components // If you wish to make these customizable, follow the logic from setProperties(); //TODO May need this for TCERTS ? // private String ASYMMETRIC_KEY_TYPE = "EC"; // private String KEY_AGREEMENT_ALGORITHM = "ECDH"; // private String SYMMETRIC_KEY_TYPE = "AES"; // private int SYMMETRIC_KEY_BYTE_COUNT = 32; // private String SYMMETRIC_ALGORITHM = "AES/CFB/NoPadding"; // private int MAC_KEY_BYTE_COUNT = 32; public CryptoPrimitives() throws ClassNotFoundException, IllegalAccessException, InstantiationException { String securityProviderClassName = config.getSecurityProviderClassName(); SECURITY_PROVIDER = setUpExplicitProvider(securityProviderClassName); //Decided TO NOT do this as it can have affects over the whole JVM and could have // unexpected results. The embedding application can easily do this! // Leaving this here as a warning. // Security.insertProviderAt(SECURITY_PROVIDER, 1); // 1 is top not 0 :) } Provider setUpExplicitProvider(String securityProviderClassName) throws InstantiationException, ClassNotFoundException, IllegalAccessException { if (null == securityProviderClassName) { throw new InstantiationException(format("Security provider class name property (%s) set to null ", Config.SECURITY_PROVIDER_CLASS_NAME)); } if (CryptoSuiteFactory.DEFAULT_JDK_PROVIDER.equals(securityProviderClassName)) { return null; } Class<?> aClass = Class.forName(securityProviderClassName); if (null == aClass) { throw new InstantiationException( format("Getting class for security provider %s returned null ", securityProviderClassName)); } if (!Provider.class.isAssignableFrom(aClass)) { throw new InstantiationException( format("Class for security provider %s is not a Java security provider", aClass.getName())); } Provider securityProvider = (Provider) aClass.newInstance(); if (securityProvider == null) { throw new InstantiationException( format("Creating instance of security %s returned null ", aClass.getName())); } return securityProvider; } // /** // * sets the signature algorithm used for signing/verifying. // * // * @param sigAlg the name of the signature algorithm. See the list of valid names in the JCA Standard Algorithm Name documentation // */ // public void setSignatureAlgorithm(String sigAlg) { // this.DEFAULT_SIGNATURE_ALGORITHM = sigAlg; // } // /** // * returns the signature algorithm used by this instance of CryptoPrimitives. // * Note that fabric and fabric-ca have not yet standardized on which algorithms are supported. // * While that plays out, CryptoPrimitives will try the algorithm specified in the certificate and // * the default SHA256withECDSA that's currently hardcoded for fabric and fabric-ca // * // * @return the name of the signature algorithm // */ // public String getSignatureAlgorithm() { // return this.DEFAULT_SIGNATURE_ALGORITHM; // } public Certificate bytesToCertificate(byte[] certBytes) throws CryptoException { if (certBytes == null || certBytes.length == 0) { throw new CryptoException("bytesToCertificate: input null or zero length"); } return getX509Certificate(certBytes); // X509Certificate certificate; // try { // BufferedInputStream pem = new BufferedInputStream(new ByteArrayInputStream(certBytes)); // CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT); // certificate = (X509Certificate) certFactory.generateCertificate(pem); // } catch (CertificateException e) { // String emsg = "Unable to converts byte array to certificate. error : " + e.getMessage(); // logger.error(emsg); // logger.debug("input bytes array :" + new String(certBytes)); // throw new CryptoException(emsg, e); // } // // return certificate; } /** * Return X509Certificate from pem bytes. * So you may ask why this ? Well some providers (BC) seems to have problems with creating the * X509 cert from bytes so here we go through all available providers till one can convert. :) * * @param pemCertificate * @return */ private X509Certificate getX509Certificate(byte[] pemCertificate) throws CryptoException { X509Certificate ret = null; CryptoException rete = null; List<Provider> providerList = new LinkedList<>(Arrays.asList(Security.getProviders())); if (SECURITY_PROVIDER != null) { //Add if overridden providerList.add(SECURITY_PROVIDER); } try { providerList.add(BouncyCastleProvider.class.newInstance()); // bouncy castle is there always. } catch (Exception e) { logger.warn(e); } for (Provider provider : providerList) { try { if (null == provider) { continue; } CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT, provider); if (null != certFactory) { try (ByteArrayInputStream bis = new ByteArrayInputStream(pemCertificate)) { Certificate certificate = certFactory.generateCertificate(bis); if (certificate instanceof X509Certificate) { ret = (X509Certificate) certificate; rete = null; break; } } } } catch (Exception e) { rete = new CryptoException(e.getMessage(), e); } } if (null != rete) { throw rete; } if (ret == null) { logger.error("Could not convert pem bytes"); } return ret; } /** * Return PrivateKey from pem bytes. * * @param pemKey pem-encoded private key * @return */ public PrivateKey bytesToPrivateKey(byte[] pemKey) throws CryptoException { PrivateKey pk = null; CryptoException ce = null; 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) { StringBuilder sb = new StringBuilder(10000); sb.append("plaintext in hex: ").append(DatatypeConverter.printHexBinary(plainText)).append("\n") .append("signature in hex: " + DatatypeConverter.printHexBinary(signature)).append("\n") .append("PEM cert in hex: " + DatatypeConverter.printHexBinary(pemCertificate)); logger.trace("verify : " + diagnosticFileDumper.createDiagnosticFile(sb.toString())); } } try { X509Certificate certificate = getX509Certificate(pemCertificate); if (certificate != null) { isVerified = validateCertificate(certificate); if (isVerified) { // only proceed if cert is trusted Signature sig = Signature.getInstance(signatureAlgorithm); sig.initVerify(certificate); sig.update(plainText); isVerified = sig.verify(signature); } } } catch (InvalidKeyException e) { CryptoException ex = new CryptoException("Cannot verify signature. Error is: " + e.getMessage() + "\r\nCertificate: " + DatatypeConverter.printHexBinary(pemCertificate), e); logger.error(ex.getMessage(), ex); throw ex; } catch (NoSuchAlgorithmException | SignatureException e) { CryptoException ex = new CryptoException( "Cannot verify. Signature algorithm is invalid. Error is: " + e.getMessage(), e); logger.error(ex.getMessage(), ex); throw ex; } return isVerified; } // verify private KeyStore trustStore = null; private void createTrustStore() throws CryptoException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); setTrustStore(keyStore); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | InvalidArgumentException e) { throw new CryptoException("Cannot create trust store. Error: " + e.getMessage(), e); } } /** * setTrustStore uses the given KeyStore object as the container for trusted * certificates * * @param keyStore the KeyStore which will be used to hold trusted certificates * @throws InvalidArgumentException */ private void setTrustStore(KeyStore keyStore) throws InvalidArgumentException { if (keyStore == null) { throw new InvalidArgumentException("Need to specify a java.security.KeyStore input parameter"); } trustStore = keyStore; } /** * getTrustStore returns the KeyStore object where we keep trusted certificates. * If no trust store has been set, this method will create one. * * @return the trust store as a java.security.KeyStore object * @throws CryptoException * @see KeyStore */ public KeyStore getTrustStore() throws CryptoException { if (trustStore == null) { createTrustStore(); } return trustStore; } /** * addCACertificateToTrustStore adds a CA cert to the set of certificates used for signature validation * * @param caCertPem an X.509 certificate in PEM format * @param alias an alias associated with the certificate. Used as shorthand for the certificate during crypto operations * @throws CryptoException * @throws InvalidArgumentException */ public void addCACertificateToTrustStore(File caCertPem, String alias) throws CryptoException, InvalidArgumentException { if (caCertPem == null) { throw new InvalidArgumentException("The certificate cannot be null"); } if (alias == null || alias.isEmpty()) { throw new InvalidArgumentException( "You must assign an alias to a certificate when adding to the trust store"); } try { try (BufferedInputStream bis = new BufferedInputStream( new ByteArrayInputStream(FileUtils.readFileToByteArray(caCertPem)))) { Certificate caCert = cf.generateCertificate(bis); addCACertificateToTrustStore(caCert, alias); } } catch (CertificateException | IOException e) { throw new CryptoException("Unable to add CA certificate to trust store. Error: " + e.getMessage(), e); } } /** * addCACertificatesToTrustStore adds a CA certs in a stream to the trust store used for signature validation * * @param bis an X.509 certificate stream in PEM format in bytes * @throws CryptoException * @throws InvalidArgumentException */ public void addCACertificatesToTrustStore(BufferedInputStream bis) throws CryptoException, InvalidArgumentException { if (bis == null) { throw new InvalidArgumentException("The certificate stream bis cannot be null"); } try { final Collection<? extends Certificate> certificates = cf.generateCertificates(bis); for (Certificate certificate : certificates) { addCACertificateToTrustStore(certificate); } } catch (CertificateException e) { throw new CryptoException("Unable to add CA certificate to trust store. Error: " + e.getMessage(), e); } } ConcurrentSet<String> certificateSet = new ConcurrentSet<>(); private void addCACertificateToTrustStore(Certificate certificate) throws InvalidArgumentException, CryptoException { String alias; if (certificate instanceof X509Certificate) { alias = ((X509Certificate) certificate).getSerialNumber().toString(); } else { // not likely ... alias = Integer.toString(certificate.hashCode()); } addCACertificateToTrustStore(certificate, alias); } /** * addCACertificateToTrustStore adds a CA cert to the set of certificates used for signature validation * * @param caCert an X.509 certificate * @param alias an alias associated with the certificate. Used as shorthand for the certificate during crypto operations * @throws CryptoException * @throws InvalidArgumentException */ void addCACertificateToTrustStore(Certificate caCert, String alias) throws InvalidArgumentException, CryptoException { if (alias == null || alias.isEmpty()) { throw new InvalidArgumentException( "You must assign an alias to a certificate when adding to the trust store."); } if (caCert == null) { throw new InvalidArgumentException("Certificate cannot be null."); } try { if (config.extraLogLevel(10)) { if (null != diagnosticFileDumper) { logger.trace(format("Adding cert to trust store. alias: %s. certificate:", alias) + diagnosticFileDumper.createDiagnosticFile(alias + "cert: " + caCert.toString())); } } synchronized (certificateSet) { if (certificateSet.contains(alias)) { return; } getTrustStore().setCertificateEntry(alias, caCert); certificateSet.add(alias); } } catch (KeyStoreException e) { String emsg = "Unable to add CA certificate to trust store. Error: " + e.getMessage(); logger.error(emsg, e); throw new CryptoException(emsg, e); } } @Override public void loadCACertificates(Collection<Certificate> certificates) throws CryptoException { if (certificates == null || certificates.size() == 0) { throw new CryptoException("Unable to load CA certificates. List is empty"); } try { for (Certificate cert : certificates) { addCACertificateToTrustStore(cert); } } catch (InvalidArgumentException e) { // Note: This can currently never happen (as cert<>null and alias<>null) throw new CryptoException("Unable to add certificate to trust store. Error: " + e.getMessage(), e); } } /* (non-Javadoc) * @see org.hyperledger.fabric.sdk.security.CryptoSuite#loadCACertificatesAsBytes(java.util.Collection) */ @Override public void loadCACertificatesAsBytes(Collection<byte[]> certificatesBytes) throws CryptoException { if (certificatesBytes == null || certificatesBytes.size() == 0) { throw new CryptoException("List of CA certificates is empty. Nothing to load."); } ArrayList<Certificate> certList = new ArrayList<>(); for (byte[] certBytes : certificatesBytes) { certList.add(bytesToCertificate(certBytes)); } loadCACertificates(certList); } /** * validateCertificate checks whether the given certificate is trusted. It * checks if the certificate is signed by one of the trusted certs in the * trust store. * * @param certPEM the certificate in PEM format * @return true if the certificate is trusted */ boolean validateCertificate(byte[] certPEM) { if (certPEM == null) { return false; } try { X509Certificate certificate = getX509Certificate(certPEM); if (null == certificate) { throw new Exception("Certificate transformation returned null"); } return validateCertificate(certificate); } catch (Exception e) { logger.error("Cannot validate certificate. Error is: " + e.getMessage() + "\r\nCertificate (PEM, hex): " + DatatypeConverter.printHexBinary(certPEM)); return false; } } boolean validateCertificate(Certificate cert) { boolean isValidated; if (cert == null) { return false; } try { KeyStore keyStore = getTrustStore(); PKIXParameters parms = new PKIXParameters(keyStore); parms.setRevocationEnabled(false); CertPathValidator certValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType()); // PKIX ArrayList<Certificate> start = new ArrayList<>(); start.add(cert); CertificateFactory certFactory = CertificateFactory.getInstance(CERTIFICATE_FORMAT); CertPath certPath = certFactory.generateCertPath(start); certValidator.validate(certPath, parms); isValidated = true; } catch (KeyStoreException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | CertificateException | CertPathValidatorException | CryptoException e) { logger.error("Cannot validate certificate. Error is: " + e.getMessage() + "\r\nCertificate" + cert.toString()); isValidated = false; } return isValidated; } // validateCertificate /** * Security Level determines the elliptic curve used in key generation * * @param securityLevel currently 256 or 384 * @throws InvalidArgumentException */ void setSecurityLevel(final int securityLevel) throws InvalidArgumentException { logger.trace(format("setSecurityLevel to %d", securityLevel)); if (securityCurveMapping.isEmpty()) { throw new InvalidArgumentException("Security curve mapping has no entries."); } if (!securityCurveMapping.containsKey(securityLevel)) { StringBuilder sb = new StringBuilder(); String sp = ""; for (int x : securityCurveMapping.keySet()) { sb.append(sp).append(x); sp = ", "; } throw new InvalidArgumentException( format("Illegal security level: %d. Valid values are: %s", securityLevel, sb.toString())); } String lcurveName = securityCurveMapping.get(securityLevel); logger.debug(format("Mapped curve strength %d to %s", securityLevel, lcurveName)); X9ECParameters params = ECNamedCurveTable.getByName(lcurveName); //Check if can match curve name to requested strength. if (params == null) { InvalidArgumentException invalidArgumentException = new InvalidArgumentException( format("Curve %s defined for security strength %d was not found.", curveName, securityLevel)); logger.error(invalidArgumentException); throw invalidArgumentException; } curveName = lcurveName; this.securityLevel = securityLevel; } void setHashAlgorithm(String algorithm) throws InvalidArgumentException { if (isNullOrEmpty(algorithm) || !("SHA2".equals(algorithm) || "SHA3".equals(algorithm))) { throw new InvalidArgumentException( "Illegal Hash function family: " + algorithm + " - must be either SHA2 or SHA3"); } hashAlgorithm = algorithm; } @Override public KeyPair keyGen() throws CryptoException { return ecdsaKeyGen(); } private KeyPair ecdsaKeyGen() throws CryptoException { return generateKey("EC", curveName); } private KeyPair generateKey(String encryptionName, String curveName) throws CryptoException { try { ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(curveName); KeyPairGenerator g = SECURITY_PROVIDER == null ? KeyPairGenerator.getInstance(encryptionName) : KeyPairGenerator.getInstance(encryptionName, SECURITY_PROVIDER); g.initialize(ecGenSpec, new SecureRandom()); return g.generateKeyPair(); } catch (Exception exp) { throw new CryptoException("Unable to generate key pair", exp); } } /** * Decodes an ECDSA signature and returns a two element BigInteger array. * * @param signature ECDSA signature bytes. * @return BigInteger array for the signature's r and s values * @throws Exception */ private static BigInteger[] decodeECDSASignature(byte[] signature) throws Exception { try (ByteArrayInputStream inStream = new ByteArrayInputStream(signature)) { ASN1InputStream asnInputStream = new ASN1InputStream(inStream); ASN1Primitive asn1 = asnInputStream.readObject(); BigInteger[] sigs = new BigInteger[2]; int count = 0; if (asn1 instanceof ASN1Sequence) { ASN1Sequence asn1Sequence = (ASN1Sequence) asn1; ASN1Encodable[] asn1Encodables = asn1Sequence.toArray(); for (ASN1Encodable asn1Encodable : asn1Encodables) { ASN1Primitive asn1Primitive = asn1Encodable.toASN1Primitive(); if (asn1Primitive instanceof ASN1Integer) { ASN1Integer asn1Integer = (ASN1Integer) asn1Primitive; BigInteger integer = asn1Integer.getValue(); if (count < 2) { sigs[count] = integer; } count++; } } } if (count != 2) { throw new CryptoException( format("Invalid ECDSA signature. Expected count of 2 but got: %d. Signature is: %s", count, DatatypeConverter.printHexBinary(signature))); } return sigs; } } /** * Sign data with the specified elliptic curve private key. * * @param privateKey elliptic curve private key. * @param data data to sign * @return the signed data. * @throws CryptoException */ private byte[] ecdsaSignToBytes(ECPrivateKey privateKey, byte[] data) throws CryptoException { if (data == null) { throw new CryptoException("Data that to be signed is null."); } if (data.length == 0) { throw new CryptoException("Data to be signed was empty."); } try { X9ECParameters params = ECNamedCurveTable.getByName(curveName); BigInteger curveN = params.getN(); Signature sig = SECURITY_PROVIDER == null ? Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM) : Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM, SECURITY_PROVIDER); sig.initSign(privateKey); sig.update(data); byte[] signature = sig.sign(); BigInteger[] sigs = decodeECDSASignature(signature); sigs = preventMalleability(sigs, 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) == 1) { 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 */ public String generateCertificationRequest(String subject, KeyPair pair) throws InvalidArgumentException { try { PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( new X500Principal("CN=" + subject), pair.getPublic()); JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withECDSA"); if (null != SECURITY_PROVIDER) { csBuilder.setProvider(SECURITY_PROVIDER); } ContentSigner signer = csBuilder.build(pair.getPrivate()); return certificationRequestToPEM(p10Builder.build(signer)); } catch (Exception e) { logger.error(e); throw new InvalidArgumentException(e); } } /** * certificationRequestToPEM - Convert a PKCS10CertificationRequest to PEM * format. * * @param csr The Certificate to convert * @return An equivalent PEM format certificate. * @throws IOException */ private String certificationRequestToPEM(PKCS10CertificationRequest csr) throws IOException { PemObject pemCSR = new PemObject("CERTIFICATE REQUEST", csr.getEncoded()); StringWriter str = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(str); pemWriter.writeObject(pemCSR); pemWriter.close(); str.close(); return str.toString(); } // public PrivateKey ecdsaKeyFromPrivate(byte[] key) throws CryptoException { // try { // EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(key); // KeyFactory generator = KeyFactory.getInstance("ECDSA", SECURITY_PROVIDER_NAME); // PrivateKey privateKey = generator.generatePrivate(privateKeySpec); // // return privateKey; // } catch (Exception exp) { // throw new CryptoException("Unable to convert byte[] into PrivateKey", exp); // } // } @Override public byte[] hash(byte[] input) { Digest digest = getHashDigest(); byte[] retValue = new byte[digest.getDigestSize()]; digest.update(input, 0, input.length); digest.doFinal(retValue, 0); return retValue; } @Override public CryptoSuiteFactory getCryptoSuiteFactory() { return HLSDKJCryptoSuiteFactory.instance(); //Factory for this crypto suite. } private final AtomicBoolean inited = new AtomicBoolean(false); // @Override public void init() throws CryptoException, InvalidArgumentException { if (inited.getAndSet(true)) { throw new InvalidArgumentException("Crypto suite already initialized"); } else { resetConfiguration(); } } private Digest getHashDigest() { if ("SHA3".equals(hashAlgorithm)) { return new SHA3Digest(); } else { // Default to SHA2 return new SHA256Digest(); } } // /** // * Shake256 hash the supplied byte data. // * // * @param in byte array to be hashed. // * @param bitLength of the result. // * @return the hashed byte data. // */ // public byte[] shake256(byte[] in, int bitLength) { // // if (bitLength % 8 != 0) { // throw new IllegalArgumentException("bit length not modulo 8"); // // } // // final int byteLen = bitLength / 8; // // SHAKEDigest sd = new SHAKEDigest(256); // // sd.update(in, 0, in.length); // // byte[] out = new byte[byteLen]; // // sd.doFinal(out, 0, byteLen); // // return out; // // } /** * Resets curve name, hash algorithm and cert factory. Call this method when a config value changes * * @throws CryptoException * @throws InvalidArgumentException */ private void resetConfiguration() throws CryptoException, InvalidArgumentException { setSecurityLevel(securityLevel); setHashAlgorithm(hashAlgorithm); try { cf = CertificateFactory.getInstance(CERTIFICATE_FORMAT); } catch (CertificateException e) { CryptoException ex = new CryptoException( "Cannot initialize " + CERTIFICATE_FORMAT + " certificate factory. Error = " + e.getMessage(), e); logger.error(ex.getMessage(), ex); throw ex; } } // /* (non-Javadoc) // * @see org.hyperledger.fabric.sdk.security.CryptoSuite#setProperties(java.util.Properties) // */ // @Override void setProperties(Properties properties) throws CryptoException, InvalidArgumentException { if (properties == null) { throw new InvalidArgumentException("properties must not be null"); } // if (properties != null) { hashAlgorithm = Optional.ofNullable(properties.getProperty(Config.HASH_ALGORITHM)).orElse(hashAlgorithm); String secLevel = Optional.ofNullable(properties.getProperty(Config.SECURITY_LEVEL)) .orElse(Integer.toString(securityLevel)); securityLevel = Integer.parseInt(secLevel); if (properties.containsKey(Config.SECURITY_CURVE_MAPPING)) { securityCurveMapping = Config .parseSecurityCurveMappings(properties.getProperty(Config.SECURITY_CURVE_MAPPING)); } else { securityCurveMapping = config.getSecurityCurveMapping(); } final String providerName = properties.containsKey(Config.SECURITY_PROVIDER_CLASS_NAME) ? properties.getProperty(Config.SECURITY_PROVIDER_CLASS_NAME) : config.getSecurityProviderClassName(); try { SECURITY_PROVIDER = setUpExplicitProvider(providerName); } catch (Exception e) { throw new InvalidArgumentException(format("Getting provider for class name: %s", providerName), e); } CERTIFICATE_FORMAT = Optional.ofNullable(properties.getProperty(Config.CERTIFICATE_FORMAT)) .orElse(CERTIFICATE_FORMAT); DEFAULT_SIGNATURE_ALGORITHM = Optional.ofNullable(properties.getProperty(Config.SIGNATURE_ALGORITHM)) .orElse(DEFAULT_SIGNATURE_ALGORITHM); resetConfiguration(); } /* (non-Javadoc) * @see org.hyperledger.fabric.sdk.security.CryptoSuite#getProperties() */ @Override public Properties getProperties() { Properties properties = new Properties(); properties.setProperty(Config.HASH_ALGORITHM, hashAlgorithm); properties.setProperty(Config.SECURITY_LEVEL, Integer.toString(securityLevel)); properties.setProperty(Config.CERTIFICATE_FORMAT, CERTIFICATE_FORMAT); properties.setProperty(Config.SIGNATURE_ALGORITHM, DEFAULT_SIGNATURE_ALGORITHM); return properties; } public byte[] certificateToDER(String certificatePEM) { byte[] content = null; try (PemReader pemReader = new PemReader(new StringReader(certificatePEM))) { final PemObject pemObject = pemReader.readPemObject(); content = pemObject.getContent(); } catch (IOException e) { // best attempt } return content; } }