Merge remote-tracking branch 'open/master' into os-merge-1b37cef

This commit is contained in:
Shams Asari 2018-04-05 17:23:03 +01:00
commit a97abbc8c0
27 changed files with 198 additions and 109 deletions

View File

@ -13,6 +13,7 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
import net.corda.core.internal.readFully
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
@ -70,7 +71,7 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any)
serviceHub.validatedTransactions.getTransaction(it) ?: throw FetchDataFlow.HashNotFound(it) serviceHub.validatedTransactions.getTransaction(it) ?: throw FetchDataFlow.HashNotFound(it)
} }
FetchDataFlow.DataType.ATTACHMENT -> dataRequest.hashes.map { FetchDataFlow.DataType.ATTACHMENT -> dataRequest.hashes.map {
serviceHub.attachments.openAttachment(it)?.open()?.readBytes() ?: throw FetchDataFlow.HashNotFound(it) serviceHub.attachments.openAttachment(it)?.open()?.readFully() ?: throw FetchDataFlow.HashNotFound(it)
} }
} }
} }

View File

@ -38,7 +38,7 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray { fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray {
return { return {
val a = serviceHub.attachments.openAttachment(id) ?: throw MissingAttachmentsException(listOf(id)) val a = serviceHub.attachments.openAttachment(id) ?: throw MissingAttachmentsException(listOf(id))
(a as? AbstractAttachment)?.attachmentData ?: a.open().use { it.readBytes() } (a as? AbstractAttachment)?.attachmentData ?: a.open().readFully()
} }
} }

View File

@ -12,6 +12,8 @@
package net.corda.core.internal package net.corda.core.internal
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappConfig import net.corda.core.cordapp.CordappConfig
import net.corda.core.cordapp.CordappContext import net.corda.core.cordapp.CordappContext
@ -50,6 +52,7 @@ import java.nio.file.attribute.FileAttribute
import java.nio.file.attribute.FileTime import java.nio.file.attribute.FileTime
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.time.temporal.Temporal import java.time.temporal.Temporal
@ -160,8 +163,31 @@ fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, var
inline fun <reified T : Any> Path.readObject(): T = readAll().deserialize() inline fun <reified T : Any> Path.readObject(): T = readAll().deserialize()
/** Calculate the hash of the contents of this file. */
val Path.hash: SecureHash get() = read { it.hash() }
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options) fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)
/** Same as [InputStream.readBytes] but also closes the stream. */
fun InputStream.readFully(): ByteArray = use { it.readBytes() }
/** Calculate the hash of the remaining bytes in this input stream. The stream is closed at the end. */
fun InputStream.hash(): SecureHash {
return use {
val his = HashingInputStream(Hashing.sha256(), it)
his.copyTo(NullOutputStream) // To avoid reading in the entire stream into memory just write out the bytes to /dev/null
SecureHash.SHA256(his.hash().asBytes())
}
}
inline fun <reified T : Any> InputStream.readObject(): T = readFully().deserialize()
object NullOutputStream : OutputStream() {
override fun write(b: Int) = Unit
override fun write(b: ByteArray) = Unit
override fun write(b: ByteArray, off: Int, len: Int) = Unit
}
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + "" fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + ""
/** Return the sum of an Iterable of [BigDecimal]s. */ /** Return the sum of an Iterable of [BigDecimal]s. */
@ -215,7 +241,7 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
val bytes = toByteArray() val bytes = toByteArray()
return InputStreamAndHash(ByteArrayInputStream(bytes), bytes.sha256()) return InputStreamAndHash(bytes.inputStream(), bytes.sha256())
} }
data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHash.SHA256) { data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHash.SHA256) {
@ -343,7 +369,7 @@ fun URL.post(serializedData: OpaqueBytes): ByteArray {
setRequestProperty("Content-Type", "application/octet-stream") setRequestProperty("Content-Type", "application/octet-stream")
outputStream.use { serializedData.open().copyTo(it) } outputStream.use { serializedData.open().copyTo(it) }
checkOkResponse() checkOkResponse()
inputStream.use { it.readBytes() } inputStream.readFully()
} }
} }
@ -356,7 +382,7 @@ fun HttpURLConnection.checkOkResponse() {
inline fun <reified T : Any> HttpURLConnection.responseAs(): T { inline fun <reified T : Any> HttpURLConnection.responseAs(): T {
checkOkResponse() checkOkResponse()
return inputStream.use { it.readBytes() }.deserialize() return inputStream.readObject()
} }
/** Analogous to [Thread.join]. */ /** Analogous to [Thread.join]. */
@ -427,3 +453,5 @@ fun NotarisationRequest.generateSignature(serviceHub: ServiceHub): NotarisationR
} }
return NotarisationRequestSignature(signature, serviceHub.myInfo.platformVersion) return NotarisationRequestSignature(signature, serviceHub.myInfo.platformVersion)
} }
val PublicKey.hash: SecureHash get() = encoded.sha256()

View File

@ -14,7 +14,7 @@ package net.corda.core.utilities
import net.corda.core.crypto.Base58 import net.corda.core.crypto.Base58
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sha256 import net.corda.core.internal.hash
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
@ -95,4 +95,4 @@ fun parsePublicKeyBase58(base58String: String): PublicKey = Crypto.decodePublicK
fun PublicKey.toBase58String(): String = this.encoded.toBase58() fun PublicKey.toBase58String(): String = this.encoded.toBase58()
/** Return the bytes of the SHA-256 output for this public key. */ /** Return the bytes of the SHA-256 output for this public key. */
fun PublicKey.toSHA256Bytes(): ByteArray = this.encoded.sha256().bytes fun PublicKey.toSHA256Bytes(): ByteArray = this.hash.bytes

View File

@ -17,6 +17,7 @@ import net.corda.core.crypto.sha256
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchAttachmentsFlow
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
import net.corda.core.internal.hash
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
@ -29,7 +30,6 @@ import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
@ -68,7 +68,7 @@ class AttachmentTests {
bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
// Insert an attachment into node zero's store directly. // Insert an attachment into node zero's store directly.
val id = aliceNode.database.transaction { val id = aliceNode.database.transaction {
aliceNode.attachments.importAttachment(ByteArrayInputStream(fakeAttachment())) aliceNode.attachments.importAttachment(fakeAttachment().inputStream())
} }
// Get node one to run a flow to fetch it and insert it. // Get node one to run a flow to fetch it and insert it.
@ -82,7 +82,7 @@ class AttachmentTests {
bobNode.attachments.openAttachment(id)!! bobNode.attachments.openAttachment(id)!!
} }
assertEquals(id, attachment.open().readBytes().sha256()) assertEquals(id, attachment.open().hash())
// Shut down node zero and ensure node one can still resolve the attachment. // Shut down node zero and ensure node one can still resolve the attachment.
aliceNode.dispose() aliceNode.dispose()
@ -121,7 +121,7 @@ class AttachmentTests {
val attachment = fakeAttachment() val attachment = fakeAttachment()
// Insert an attachment into node zero's store directly. // Insert an attachment into node zero's store directly.
val id = aliceNode.database.transaction { val id = aliceNode.database.transaction {
aliceNode.attachments.importAttachment(ByteArrayInputStream(attachment)) aliceNode.attachments.importAttachment(attachment.inputStream())
} }
// Corrupt its store. // Corrupt its store.

View File

@ -15,13 +15,10 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import javassist.ClassPool import javassist.ClassPool
import javassist.CtClass import javassist.CtClass
import java.io.ByteArrayInputStream
import java.lang.StringBuilder
import java.lang.instrument.ClassFileTransformer import java.lang.instrument.ClassFileTransformer
import java.lang.instrument.Instrumentation import java.lang.instrument.Instrumentation
import java.security.ProtectionDomain import java.security.ProtectionDomain
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
class KryoHookAgent { class KryoHookAgent {
companion object { companion object {
@ -79,7 +76,7 @@ object KryoHook : ClassFileTransformer {
return null return null
} }
return try { return try {
val clazz = classPool.makeClass(ByteArrayInputStream(classfileBuffer)) val clazz = classPool.makeClass(classfileBuffer.inputStream())
instrumentClass(clazz)?.toBytecode() instrumentClass(clazz)?.toBytecode()
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
println("SOMETHING WENT WRONG") println("SOMETHING WENT WRONG")

View File

@ -12,7 +12,6 @@ package net.corda.quasarhook
import javassist.ClassPool import javassist.ClassPool
import javassist.CtClass import javassist.CtClass
import java.io.ByteArrayInputStream
import java.lang.instrument.ClassFileTransformer import java.lang.instrument.ClassFileTransformer
import java.lang.instrument.Instrumentation import java.lang.instrument.Instrumentation
import java.security.ProtectionDomain import java.security.ProtectionDomain
@ -193,9 +192,9 @@ object QuasarInstrumentationHook : ClassFileTransformer {
classfileBuffer: ByteArray classfileBuffer: ByteArray
): ByteArray { ): ByteArray {
return try { return try {
val instrument = instrumentMap.get(className) val instrument = instrumentMap[className]
return instrument?.let { return instrument?.let {
val clazz = classPool.makeClass(ByteArrayInputStream(classfileBuffer)) val clazz = classPool.makeClass(classfileBuffer.inputStream())
it(clazz) it(clazz)
clazz.toBytecode() clazz.toBytecode()
} ?: classfileBuffer } ?: classfileBuffer

View File

@ -15,7 +15,6 @@ import net.corda.core.contracts.ContractAttachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.isUploaderTrusted import net.corda.core.internal.isUploaderTrusted
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.InputStream import java.io.InputStream
@ -111,12 +110,12 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
if (url.protocol != "attachment") return null if (url.protocol != "attachment") return null
val attachment = idsToAttachments[SecureHash.parse(url.host)] ?: return null val attachment = idsToAttachments[SecureHash.parse(url.host)] ?: return null
val path = url.path?.substring(1) ?: return null // Chop off the leading slash. val path = url.path?.substring(1) ?: return null // Chop off the leading slash.
try { return try {
val stream = ByteArrayOutputStream() val stream = ByteArrayOutputStream()
attachment.extractFile(path, stream) attachment.extractFile(path, stream)
return ByteArrayInputStream(stream.toByteArray()) stream.toByteArray().inputStream()
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
return null null
} }
} }
} }

View File

@ -18,12 +18,14 @@ import net.corda.core.internal.*
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import org.bouncycastle.asn1.* import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.bc.BcX509ExtensionUtils import org.bouncycastle.cert.bc.BcX509ExtensionUtils
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.operator.ContentSigner import org.bouncycastle.operator.ContentSigner
@ -159,18 +161,25 @@ $trustedRoot""", e, certPath, e.index)
/** /**
* Build a partial X.509 certificate ready for signing. * Build a partial X.509 certificate ready for signing.
* *
* @param certificateType type of the certificate.
* @param issuer name of the issuing entity. * @param issuer name of the issuing entity.
* @param issuerPublicKey public key of the issuing entity.
* @param subject name of the certificate subject. * @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject. * @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for. * @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
* @param crlDistPoint CRL distribution point.
* @param crlIssuer X500Name of the CRL issuer.
*/ */
fun createPartialCertificate(certificateType: CertificateType, private fun createPartialCertificate(certificateType: CertificateType,
issuer: X500Principal, issuer: X500Principal,
issuerPublicKey: PublicKey,
subject: X500Principal, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { nameConstraints: NameConstraints? = null,
crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509v3CertificateBuilder {
val serial = BigInteger.valueOf(random63BitValue()) val serial = BigInteger.valueOf(random63BitValue())
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
@ -181,10 +190,12 @@ $trustedRoot""", e, certPath, e.index)
.addExtension(Extension.basicConstraints, true, BasicConstraints(certificateType.isCA)) .addExtension(Extension.basicConstraints, true, BasicConstraints(certificateType.isCA))
.addExtension(Extension.keyUsage, false, certificateType.keyUsage) .addExtension(Extension.keyUsage, false, certificateType.keyUsage)
.addExtension(Extension.extendedKeyUsage, false, keyPurposes) .addExtension(Extension.extendedKeyUsage, false, keyPurposes)
.addExtension(Extension.authorityKeyIdentifier, false, JcaX509ExtensionUtils().createAuthorityKeyIdentifier(issuerPublicKey))
if (role != null) { if (role != null) {
builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role) builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role)
} }
addCrlInfo(builder, crlDistPoint, crlIssuer)
if (nameConstraints != null) { if (nameConstraints != null) {
builder.addExtension(Extension.nameConstraints, true, nameConstraints) builder.addExtension(Extension.nameConstraints, true, nameConstraints)
} }
@ -195,11 +206,15 @@ $trustedRoot""", e, certPath, e.index)
/** /**
* Create a X509 v3 certificate using the given issuer certificate and key pair. * Create a X509 v3 certificate using the given issuer certificate and key pair.
* *
* @param certificateType type of the certificate.
* @param issuerCertificate The Public certificate of the root CA above this used to sign it. * @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it. * @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate. * @param subject subject of the generated certificate.
* @param subjectPublicKey subject's public key. * @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
* @param crlDistPoint CRL distribution point.
* @param crlIssuer X500Name of the CRL issuer.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/ */
@ -210,7 +225,9 @@ $trustedRoot""", e, certPath, e.index)
subject: X500Principal, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509Certificate { nameConstraints: NameConstraints? = null,
crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509Certificate {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
return createCertificate( return createCertificate(
certificateType, certificateType,
@ -219,28 +236,36 @@ $trustedRoot""", e, certPath, e.index)
subject, subject,
subjectPublicKey, subjectPublicKey,
window, window,
nameConstraints nameConstraints,
crlDistPoint,
crlIssuer
) )
} }
/** /**
* Build and sign an X.509 certificate with the given signer. * Build and sign an X.509 certificate with the given signer.
* *
* @param certificateType type of the certificate.
* @param issuer name of the issuing entity. * @param issuer name of the issuing entity.
* @param issuerSigner content signer to sign the certificate with. * @param issuerSigner content signer to sign the certificate with.
* @param subject name of the certificate subject. * @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject. * @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for. * @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
* @param crlDistPoint CRL distribution point.
* @param crlIssuer X500Name of the CRL issuer.
*/ */
fun createCertificate(certificateType: CertificateType, fun createCertificate(certificateType: CertificateType,
issuer: X500Principal, issuer: X500Principal,
issuerPublicKey: PublicKey,
issuerSigner: ContentSigner, issuerSigner: ContentSigner,
subject: X500Principal, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509Certificate { nameConstraints: NameConstraints? = null,
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509Certificate {
val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer)
return builder.build(issuerSigner).run { return builder.build(issuerSigner).run {
require(isValidOn(Date())) require(isValidOn(Date()))
toJca() toJca()
@ -250,6 +275,7 @@ $trustedRoot""", e, certPath, e.index)
/** /**
* Build and sign an X.509 certificate with CA cert private key. * Build and sign an X.509 certificate with CA cert private key.
* *
* @param certificateType type of the certificate.
* @param issuer name of the issuing entity. * @param issuer name of the issuing entity.
* @param issuerKeyPair the public & private key to sign the certificate with. * @param issuerKeyPair the public & private key to sign the certificate with.
* @param subject name of the certificate subject. * @param subject name of the certificate subject.
@ -263,11 +289,21 @@ $trustedRoot""", e, certPath, e.index)
subject: X500Principal, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509Certificate { nameConstraints: NameConstraints? = null,
crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509Certificate {
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
val provider = Crypto.findProvider(signatureScheme.providerName) val provider = Crypto.findProvider(signatureScheme.providerName)
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) val builder = createPartialCertificate(
certificateType,
issuer,
issuerKeyPair.public,
subject, subjectPublicKey,
validityWindow,
nameConstraints,
crlDistPoint,
crlIssuer)
return builder.build(signer).run { return builder.build(signer).run {
require(isValidOn(Date())) require(isValidOn(Date()))
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
@ -312,6 +348,21 @@ $trustedRoot""", e, certPath, e.index)
fun buildCertPath(certificates: List<X509Certificate>): CertPath { fun buildCertPath(certificates: List<X509Certificate>): CertPath {
return X509CertificateFactory().generateCertPath(certificates) return X509CertificateFactory().generateCertPath(certificates)
} }
private fun addCrlInfo(builder: X509v3CertificateBuilder, crlDistPoint: String?, crlIssuer: X500Name?) {
if (crlDistPoint != null) {
val distPointName = DistributionPointName(GeneralNames(GeneralName(GeneralName.uniformResourceIdentifier, crlDistPoint)))
val crlIssuerGeneralNames = crlIssuer?.let {
GeneralNames(GeneralName(crlIssuer))
}
// The second argument is flag that allows you to define what reason of certificate revocation is served by this distribution point see [ReasonFlags].
// The idea is that you have different revocation per revocation reason. Since we won't go into such a granularity, we can skip that parameter.
// The third argument allows you to specify the name of the CRL issuer, it needs to be consistent with the crl (IssuingDistributionPoint) extension and the idp argument.
// If idp == true, set it, if idp == false, leave it null as done here.
val distPoint = DistributionPoint(distPointName, null, crlIssuerGeneralNames)
builder.addExtension(Extension.cRLDistributionPoints, false, CRLDistPoint(arrayOf(distPoint)))
}
}
} }
// Assuming cert type to role is 1:1 // Assuming cert type to role is 1:1

View File

@ -10,12 +10,8 @@
package net.corda.nodeapi.internal.network package net.corda.nodeapi.internal.network
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode import net.corda.cordform.CordformNode
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.parse import net.corda.core.crypto.SecureHash.Companion.parse
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.* import net.corda.core.internal.*
@ -37,7 +33,6 @@ import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import java.io.File
import java.io.PrintStream import java.io.PrintStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -201,7 +196,7 @@ class NetworkBootstrapper {
private fun generateWhitelist(whitelistFile: Path, excludeWhitelistFile: Path, cordapps: List<String>?): Map<String, List<AttachmentId>> { private fun generateWhitelist(whitelistFile: Path, excludeWhitelistFile: Path, cordapps: List<String>?): Map<String, List<AttachmentId>> {
val existingWhitelist = if (whitelistFile.exists()) readContractWhitelist(whitelistFile) else emptyMap() val existingWhitelist = if (whitelistFile.exists()) readContractWhitelist(whitelistFile) else emptyMap()
println(if (existingWhitelist.isEmpty()) "No existing whitelist file found." else "Found existing whitelist: ${whitelistFile}") println(if (existingWhitelist.isEmpty()) "No existing whitelist file found." else "Found existing whitelist: $whitelistFile")
val excludeContracts = if (excludeWhitelistFile.exists()) readExcludeWhitelist(excludeWhitelistFile) else emptyList() val excludeContracts = if (excludeWhitelistFile.exists()) readExcludeWhitelist(excludeWhitelistFile) else emptyList()
if (excludeContracts.isNotEmpty()) { if (excludeContracts.isNotEmpty()) {
@ -209,7 +204,7 @@ class NetworkBootstrapper {
} }
val newWhiteList = cordapps?.flatMap { cordappJarPath -> val newWhiteList = cordapps?.flatMap { cordappJarPath ->
val jarHash = getJarHash(cordappJarPath) val jarHash = Paths.get(cordappJarPath).hash
scanJarForContracts(cordappJarPath).map { contract -> scanJarForContracts(cordappJarPath).map { contract ->
contract to jarHash contract to jarHash
} }
@ -223,30 +218,26 @@ class NetworkBootstrapper {
contractClassName to (if (newHash == null || newHash in existing) existing else existing + newHash) contractClassName to (if (newHash == null || newHash in existing) existing else existing + newHash)
}.toMap() }.toMap()
println("CorDapp whitelist " + (if (existingWhitelist.isEmpty()) "generated" else "updated") + " in ${whitelistFile}") println("CorDapp whitelist " + (if (existingWhitelist.isEmpty()) "generated" else "updated") + " in $whitelistFile")
return merged return merged
} }
private fun overwriteWhitelist(whitelistFile: Path, mergedWhiteList: Map<String, List<AttachmentId>>) { private fun overwriteWhitelist(whitelistFile: Path, mergedWhiteList: Map<String, List<AttachmentId>>) {
PrintStream(whitelistFile.toFile().outputStream()).use { out -> PrintStream(whitelistFile.toFile().outputStream()).use { out ->
mergedWhiteList.forEach { (contract, attachments) -> mergedWhiteList.forEach { (contract, attachments) ->
out.println("${contract}:${attachments.joinToString(",")}") out.println("$contract:${attachments.joinToString(",")}")
} }
} }
} }
private fun getJarHash(cordappPath: String): AttachmentId = File(cordappPath).inputStream().use { jar -> private fun readContractWhitelist(file: Path): Map<String, List<AttachmentId>> {
val hs = HashingInputStream(Hashing.sha256(), jar) return file.readAllLines()
hs.readBytes() .map { line -> line.split(":") }
SecureHash.SHA256(hs.hash().asBytes()) .map { (contract, attachmentIds) ->
contract to (attachmentIds.split(",").map(::parse))
}.toMap()
} }
private fun readContractWhitelist(file: Path): Map<String, List<AttachmentId>> = file.readAllLines()
.map { line -> line.split(":") }
.map { (contract, attachmentIds) ->
contract to (attachmentIds.split(",").map(::parse))
}.toMap()
private fun readExcludeWhitelist(file: Path): List<String> = file.readAllLines().map(String::trim) private fun readExcludeWhitelist(file: Path): List<String> = file.readAllLines().map(String::trim)
private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)" private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)"

View File

@ -13,6 +13,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.internal.readFully
import net.corda.core.serialization.MissingAttachmentsException import net.corda.core.serialization.MissingAttachmentsException
import net.corda.nodeapi.internal.serialization.GeneratedAttachment import net.corda.nodeapi.internal.serialization.GeneratedAttachment
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
@ -27,7 +28,7 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
ContractAttachmentProxy::class.java, factory) { ContractAttachmentProxy::class.java, factory) {
override fun toProxy(obj: ContractAttachment): ContractAttachmentProxy { override fun toProxy(obj: ContractAttachment): ContractAttachmentProxy {
val bytes = try { val bytes = try {
obj.attachment.open().readBytes() obj.attachment.open().readFully()
} catch (e: Exception) { } catch (e: Exception) {
throw MissingAttachmentsException(listOf(obj.id)) throw MissingAttachmentsException(listOf(obj.id))
} }

View File

@ -46,6 +46,6 @@ object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStr
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): InputStream { override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): InputStream {
val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
return ByteArrayInputStream(bits) return bits.inputStream()
} }
} }

View File

@ -28,6 +28,7 @@ import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.readFully
import net.corda.core.serialization.MissingAttachmentsException import net.corda.core.serialization.MissingAttachmentsException
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
@ -233,7 +234,7 @@ object DefaultKryoCustomizer {
val lazyAttachment = object : AbstractAttachment({ val lazyAttachment = object : AbstractAttachment({
val attachment = attachmentStorage.openAttachment(attachmentHash) val attachment = attachmentStorage.openAttachment(attachmentHash)
?: throw MissingAttachmentsException(listOf(attachmentHash)) ?: throw MissingAttachmentsException(listOf(attachmentHash))
attachment.open().readBytes() attachment.open().readFully()
}) { }) {
override val id = attachmentHash override val id = attachmentHash
} }

View File

@ -39,7 +39,6 @@ import net.corda.nodeapi.internal.serialization.serializationContextKey
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import rx.Observable import rx.Observable
import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.security.PrivateKey import java.security.PrivateKey
@ -222,7 +221,7 @@ object InputStreamSerializer : Serializer<InputStream>() {
System.arraycopy(chunk, 0, flattened, offset, chunk.size) System.arraycopy(chunk, 0, flattened, offset, chunk.size)
offset += chunk.size offset += chunk.size
} }
return ByteArrayInputStream(flattened) return flattened.inputStream()
} }
} }

View File

@ -76,5 +76,5 @@ internal fun Output.substitute(transform: (OutputStream) -> OutputStream) {
} }
internal fun Input.substitute(transform: (InputStream) -> InputStream) { internal fun Input.substitute(transform: (InputStream) -> InputStream) {
inputStream = transform(SequenceInputStream(ByteArrayInputStream(buffer.copyOfRange(position(), limit())), inputStream)) inputStream = transform(SequenceInputStream(buffer.copyOfRange(position(), limit()).inputStream(), inputStream))
} }

View File

@ -12,7 +12,8 @@ package net.corda.nodeapi.internal
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.Attachment
import net.corda.core.contracts.Contract
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
@ -32,9 +33,9 @@ import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.MockCordappConfigProvider
import net.corda.testing.internal.kryoSpecific import net.corda.testing.internal.kryoSpecific
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.MockCordappConfigProvider
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.junit.Assert.* import org.junit.Assert.*
@ -137,8 +138,8 @@ class AttachmentsClassLoaderTests {
fun `test overlapping file exception`() { fun `test overlapping file exception`() {
val storage = attachments val storage = attachments
val att0 = attachmentId val att0 = attachmentId
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("file.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("file.txt", "some other data").inputStream())
assertFailsWith(AttachmentsClassLoader.OverlappingAttachments::class) { assertFailsWith(AttachmentsClassLoader.OverlappingAttachments::class) {
AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! })
@ -149,8 +150,8 @@ class AttachmentsClassLoaderTests {
fun `basic`() { fun `basic`() {
val storage = attachments val storage = attachments
val att0 = attachmentId val att0 = attachmentId
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream())
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! })
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
@ -161,8 +162,8 @@ class AttachmentsClassLoaderTests {
fun `Check platform independent path handling in attachment jars`() { fun `Check platform independent path handling in attachment jars`() {
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("/folder1/foldera/file1.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("/folder1/foldera/file1.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("\\folder1\\folderb\\file2.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("\\folder1\\folderb\\file2.txt", "some other data").inputStream())
val data1a = readAttachment(storage.openAttachment(att1)!!, "/folder1/foldera/file1.txt") val data1a = readAttachment(storage.openAttachment(att1)!!, "/folder1/foldera/file1.txt")
assertArrayEquals("some data".toByteArray(), data1a) assertArrayEquals("some data".toByteArray(), data1a)
@ -181,8 +182,8 @@ class AttachmentsClassLoaderTests {
fun `loading class AnotherDummyContract`() { fun `loading class AnotherDummyContract`() {
val storage = attachments val storage = attachments
val att0 = attachmentId val att0 = attachmentId
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream())
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)
@ -205,8 +206,8 @@ class AttachmentsClassLoaderTests {
val bytes = contract.serialize() val bytes = contract.serialize()
val storage = attachments val storage = attachments
val att0 = attachmentId val att0 = attachmentId
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream())
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
@ -231,8 +232,8 @@ class AttachmentsClassLoaderTests {
val bytes = data.serialize(context = context2) val bytes = data.serialize(context = context2)
val storage = attachments val storage = attachments
val att0 = attachmentId val att0 = attachmentId
val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream())
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream())
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)

View File

@ -30,9 +30,7 @@ import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.*
import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.KeyUsage
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -113,6 +111,28 @@ class X509UtilitiesTest {
} }
} }
@Test
fun `create valid server certificate chain includes CRL info`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
val caSubjectKeyIdentifier = SubjectKeyIdentifier.getInstance(caCert.toBc().getExtension(Extension.subjectKeyIdentifier).parsedValue)
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val crlDistPoint = "http://test.com"
val serverCert = X509Utilities.createCertificate(
CertificateType.TLS,
caCert,
caKey,
X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB"),
keyPair.public,
crlDistPoint = crlDistPoint)
serverCert.toBc().run {
val certCrlDistPoint = CRLDistPoint.getInstance(getExtension(Extension.cRLDistributionPoints).parsedValue)
assertTrue(certCrlDistPoint.distributionPoints.first().distributionPoint.toString().contains(crlDistPoint))
val certCaAuthorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(getExtension(Extension.authorityKeyIdentifier).parsedValue)
assertTrue(Arrays.equals(caSubjectKeyIdentifier.keyIdentifier, certCaAuthorityKeyIdentifier.keyIdentifier))
}
}
@Test @Test
fun `storing EdDSA key in java keystore`() { fun `storing EdDSA key in java keystore`() {
val tmpKeyStore = tempFile("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")

View File

@ -53,7 +53,6 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters import org.junit.runners.Parameterized.Parameters
import java.io.ByteArrayInputStream
import java.io.IOException import java.io.IOException
import java.io.NotSerializableException import java.io.NotSerializableException
import java.math.BigDecimal import java.math.BigDecimal
@ -1093,9 +1092,9 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer) factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer)
val bytes = ByteArray(10) { it.toByte() } val bytes = ByteArray(10) { it.toByte() }
val obj = ByteArrayInputStream(bytes) val obj = bytes.inputStream()
val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false) val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
val obj3 = ByteArrayInputStream(bytes) // Can't use original since the stream pointer has moved. val obj3 = bytes.inputStream() // Can't use original since the stream pointer has moved.
assertEquals(obj3.available(), obj2.available()) assertEquals(obj3.available(), obj2.available())
assertEquals(obj3.read(), obj2.read()) assertEquals(obj3.read(), obj2.read())
} }

View File

@ -39,7 +39,6 @@ import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters import org.junit.runners.Parameterized.Parameters
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -200,7 +199,11 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test @Test
fun `HashCheckingStream (de)serialize`() { fun `HashCheckingStream (de)serialize`() {
val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() }) val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() })
val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream(SecureHash.sha256(rubbish), rubbish.size, ByteArrayInputStream(rubbish)).serialize(factory, context).deserialize(factory, context) val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream(
SecureHash.sha256(rubbish),
rubbish.size,
rubbish.inputStream()
).serialize(factory, context).deserialize(factory, context)
for (i in 0..12344) { for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte()) assertEquals(rubbish[i], readRubbishStream.read().toByte())
} }

View File

@ -13,6 +13,7 @@ package net.corda.node.utilities.registration
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.logElapsedTime import net.corda.core.internal.logElapsedTime
import net.corda.core.internal.readFully
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
@ -143,7 +144,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
fun registration(input: InputStream): Response { fun registration(input: InputStream): Response {
return log.logElapsedTime("Registration") { return log.logElapsedTime("Registration") {
val certificationRequest = input.use { JcaPKCS10CertificationRequest(it.readBytes()) } val certificationRequest = JcaPKCS10CertificationRequest(input.readFully())
val (certPath, name) = createSignedClientCertificate( val (certPath, name) = createSignedClientCertificate(
certificationRequest, certificationRequest,
rootCertAndKeyPair.keyPair, rootCertAndKeyPair.keyPair,

View File

@ -15,6 +15,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.* import net.corda.core.identity.*
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.internal.hash
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.MAX_HASH_HEX_SIZE
@ -72,7 +73,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
) )
} }
private fun mapToKey(owningKey: PublicKey) = SecureHash.sha256(owningKey.encoded) private fun mapToKey(owningKey: PublicKey) = owningKey.hash
private fun mapToKey(party: PartyAndCertificate) = mapToKey(party.owningKey) private fun mapToKey(party: PartyAndCertificate) = mapToKey(party.owningKey)
} }

View File

@ -50,6 +50,7 @@ fun freshCertificate(identityService: IdentityService,
val ourCertificate = X509Utilities.createCertificate( val ourCertificate = X509Utilities.createCertificate(
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
issuerCert.subjectX500Principal, issuerCert.subjectX500Principal,
issuerCert.publicKey,
issuerSigner, issuerSigner,
issuer.name.x500Principal, issuer.name.x500Principal,
subjectPublicKey, subjectPublicKey,

View File

@ -21,9 +21,11 @@ import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.UNKNOWN_UPLOADER import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.readFully
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
@ -37,7 +39,10 @@ import net.corda.node.utilities.NonInvalidatingWeightBasedCache
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.currentDBSession import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.nodeapi.internal.withContractsInJar import net.corda.nodeapi.internal.withContractsInJar
import java.io.* import java.io.FilterInputStream
import java.io.IOException
import java.io.InputStream
import java.io.Serializable
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -272,14 +277,6 @@ class NodeAttachmentService(
return import(jar, uploader, filename) return import(jar, uploader, filename)
} }
fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> {
val hs = HashingInputStream(Hashing.sha256(), jar)
val bytes = hs.readBytes()
checkIsAValidJAR(ByteArrayInputStream(bytes))
val id = SecureHash.SHA256(hs.hash().asBytes())
return Pair(id, bytes)
}
override fun hasAttachment(attachmentId: AttachmentId): Boolean = override fun hasAttachment(attachmentId: AttachmentId): Boolean =
currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
@ -288,15 +285,16 @@ class NodeAttachmentService(
return withContractsInJar(jar) { contractClassNames, inputStream -> return withContractsInJar(jar) { contractClassNames, inputStream ->
require(inputStream !is JarInputStream) require(inputStream !is JarInputStream)
// Read the file into RAM, hashing it to find the ID as we go. The attachment must fit into memory. // Read the file into RAM and then calculate its hash. The attachment must fit into memory.
// TODO: Switch to a two-phase insert so we can handle attachments larger than RAM. // TODO: Switch to a two-phase insert so we can handle attachments larger than RAM.
// To do this we must pipe stream into the database without knowing its hash, which we will learn only once // To do this we must pipe stream into the database without knowing its hash, which we will learn only once
// the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not // the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not
// set the hash field of the new attachment record. // set the hash field of the new attachment record.
val (id, bytes) = getAttachmentIdAndBytes(inputStream) val bytes = inputStream.readFully()
val id = bytes.sha256()
if (!hasAttachment(id)) { if (!hasAttachment(id)) {
checkIsAValidJAR(ByteArrayInputStream(bytes)) checkIsAValidJAR(bytes.inputStream())
val session = currentDBSession() val session = currentDBSession()
val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename, contractClassNames = contractClassNames) val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename, contractClassNames = contractClassNames)
session.save(attachment) session.save(attachment)

View File

@ -45,24 +45,26 @@ import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Buyer
import net.corda.finance.flows.TwoPartyTradeFlow.Seller import net.corda.finance.flows.TwoPartyTradeFlow.Seller
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.checkpoints import net.corda.node.services.persistence.checkpoints
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.internal.LogHelper
import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.TEST_TX_TIME import net.corda.testing.internal.TEST_TX_TIME
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.internal.vault.VaultFiller
import net.corda.testing.node.* import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockServices
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.pumpReceive import net.corda.testing.node.internal.pumpReceive
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -70,7 +72,6 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import rx.Observable import rx.Observable
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.* import java.util.*
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
@ -357,7 +358,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
it.closeEntry() it.closeEntry()
} }
val attachmentID = aliceNode.database.transaction { val attachmentID = aliceNode.database.transaction {
attachment(ByteArrayInputStream(stream.toByteArray())) attachment(stream.toByteArray().inputStream())
} }
val bobsFakeCash = bobNode.database.transaction { val bobsFakeCash = bobNode.database.transaction {
@ -461,7 +462,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
it.closeEntry() it.closeEntry()
} }
val attachmentID = aliceNode.database.transaction { val attachmentID = aliceNode.database.transaction {
attachment(ByteArrayInputStream(stream.toByteArray())) attachment(stream.toByteArray().inputStream())
} }
val bobsKey = bobNode.services.keyManagementService.keys.single() val bobsKey = bobNode.services.keyManagementService.keys.single()

View File

@ -15,10 +15,7 @@ import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.read import net.corda.core.internal.*
import net.corda.core.internal.readAll
import net.corda.core.internal.write
import net.corda.core.internal.writeLines
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.node.services.vault.Builder import net.corda.core.node.services.vault.Builder
@ -240,7 +237,7 @@ class NodeAttachmentStorageTest {
database.transaction { database.transaction {
val storage = NodeAttachmentService(MetricRegistry()) val storage = NodeAttachmentService(MetricRegistry())
val e = assertFailsWith<NodeAttachmentService.HashMismatchException> { val e = assertFailsWith<NodeAttachmentService.HashMismatchException> {
storage.openAttachment(id)!!.open().use { it.readBytes() } storage.openAttachment(id)!!.open().readFully()
} }
assertEquals(e.expected, id) assertEquals(e.expected, id)

View File

@ -12,14 +12,14 @@ package net.corda.testing.node.internal.network
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.internal.readObject
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.core.node.NetworkParameters
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.ParametersUpdate import net.corda.nodeapi.internal.network.ParametersUpdate
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
@ -120,7 +120,7 @@ class NetworkMapServer(private val pollInterval: Duration,
@Consumes(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_OCTET_STREAM)
fun publishNodeInfo(input: InputStream): Response { fun publishNodeInfo(input: InputStream): Response {
return try { return try {
val signedNodeInfo = input.readBytes().deserialize<SignedNodeInfo>() val signedNodeInfo = input.readObject<SignedNodeInfo>()
signedNodeInfo.verified() signedNodeInfo.verified()
nodeInfoMap[signedNodeInfo.raw.hash] = signedNodeInfo nodeInfoMap[signedNodeInfo.raw.hash] = signedNodeInfo
ok() ok()
@ -136,7 +136,7 @@ class NetworkMapServer(private val pollInterval: Duration,
@Path("ack-parameters") @Path("ack-parameters")
@Consumes(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_OCTET_STREAM)
fun ackNetworkParameters(input: InputStream): Response { fun ackNetworkParameters(input: InputStream): Response {
val signedParametersHash = input.readBytes().deserialize<SignedData<SecureHash>>() val signedParametersHash = input.readObject<SignedData<SecureHash>>()
val hash = signedParametersHash.verified() val hash = signedParametersHash.verified()
latestAcceptedParametersMap[signedParametersHash.sig.by] = hash latestAcceptedParametersMap[signedParametersHash.sig.by] = hash
return ok().build() return ok().build()

View File

@ -17,13 +17,13 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.UNKNOWN_UPLOADER import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.internal.readFully
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.nodeapi.internal.withContractsInJar import net.corda.nodeapi.internal.withContractsInJar
import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
@ -63,7 +63,7 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
fun importContractAttachment(contractClassNames: List<ContractClassName>, uploader: String, jar: InputStream): AttachmentId = importAttachmentInternal(jar, uploader, null, contractClassNames) fun importContractAttachment(contractClassNames: List<ContractClassName>, uploader: String, jar: InputStream): AttachmentId = importAttachmentInternal(jar, uploader, null, contractClassNames)
fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> = jar.readBytes().let { bytes -> Pair(bytes.sha256(), bytes) } fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> = jar.readFully().let { bytes -> Pair(bytes.sha256(), bytes) }
private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash) : AbstractAttachment(dataLoader) private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash) : AbstractAttachment(dataLoader)
@ -71,7 +71,7 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
// JIS makes read()/readBytes() return bytes of the current file, but we want to hash the entire container here. // JIS makes read()/readBytes() return bytes of the current file, but we want to hash the entire container here.
require(jar !is JarInputStream) require(jar !is JarInputStream)
val bytes = jar.readBytes() val bytes = jar.readFully()
val sha256 = bytes.sha256() val sha256 = bytes.sha256()
if (sha256 !in files.keys) { if (sha256 !in files.keys) {