CORDA-4043: Generate 16-octets certificate serial numbers (#6746)

This commit is contained in:
Denis Rekalov 2020-09-30 13:39:26 +01:00 committed by GitHub
parent 2dd2029e24
commit 396671cb87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 2 deletions

View File

@ -44,6 +44,7 @@ import net.corda.coretesting.internal.NettyTestServer
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.checkValidity import net.corda.nodeapi.internal.crypto.checkValidity
import net.corda.nodeapi.internal.crypto.getSupportedKey import net.corda.nodeapi.internal.crypto.getSupportedKey
@ -51,6 +52,7 @@ import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.crypto.save
import net.corda.nodeapi.internal.crypto.toBc import net.corda.nodeapi.internal.crypto.toBc
import net.corda.nodeapi.internal.crypto.x509 import net.corda.nodeapi.internal.crypto.x509
import net.corda.nodeapi.internal.crypto.x509Certificates
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
@ -565,4 +567,16 @@ class X509UtilitiesTest {
cert.checkValidity({ "Error text" }, { }, Date.from(today.toInstant() + 51.days)) cert.checkValidity({ "Error text" }, { }, Date.from(today.toInstant() + 51.days))
} }
} }
@Test(timeout = 300_000)
fun `check certificate serial number`() {
val keyPair = generateKeyPair()
val subject = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val cert = X509Utilities.createSelfSignedCACertificate(subject, keyPair)
assertTrue(cert.serialNumber.signum() > 0)
assertEquals(127, cert.serialNumber.bitLength())
val serialized = X509Utilities.buildCertPath(cert).encoded
val deserialized = X509CertificateFactory().delegate.generateCertPath(serialized.inputStream()).x509Certificates.first()
assertEquals(cert.serialNumber, deserialized.serialNumber)
}
} }

View File

@ -2,7 +2,7 @@ package net.corda.nodeapi.internal.crypto
import net.corda.core.CordaOID import net.corda.core.CordaOID
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.newSecureRandom
import net.corda.core.internal.* 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
@ -35,6 +35,8 @@ import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.experimental.and
import kotlin.experimental.or
object X509Utilities { object X509Utilities {
// Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different // Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different
@ -58,6 +60,7 @@ object X509Utilities {
const val TLS_CERTIFICATE_DAYS_TO_EXPIRY_WARNING_THRESHOLD = 30 const val TLS_CERTIFICATE_DAYS_TO_EXPIRY_WARNING_THRESHOLD = 30
private const val KEY_ALIAS_REGEX = "[a-z0-9-]+" private const val KEY_ALIAS_REGEX = "[a-z0-9-]+"
private const val KEY_ALIAS_MAX_LENGTH = 100 private const val KEY_ALIAS_MAX_LENGTH = 100
private const val CERTIFICATE_SERIAL_NUMBER_LENGTH = 16
/** /**
* Checks if the provided key alias does not exceed maximum length and * Checks if the provided key alias does not exceed maximum length and
@ -184,7 +187,7 @@ object X509Utilities {
nameConstraints: NameConstraints? = null, nameConstraints: NameConstraints? = null,
crlDistPoint: String? = null, crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509v3CertificateBuilder { crlIssuer: X500Name? = null): X509v3CertificateBuilder {
val serial = BigInteger.valueOf(random63BitValue()) val serial = generateCertificateSerialNumber()
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))
val role = certificateType.role val role = certificateType.role
@ -364,6 +367,15 @@ object X509Utilities {
builder.addExtension(Extension.cRLDistributionPoints, false, CRLDistPoint(arrayOf(distPoint))) builder.addExtension(Extension.cRLDistributionPoints, false, CRLDistPoint(arrayOf(distPoint)))
} }
} }
@Suppress("MagicNumber")
private fun generateCertificateSerialNumber(): BigInteger {
val bytes = ByteArray(CERTIFICATE_SERIAL_NUMBER_LENGTH)
newSecureRandom().nextBytes(bytes)
// Set highest byte to 01xxxxxx to ensure positive sign and constant bit length.
bytes[0] = bytes[0].and(0x3F).or(0x40)
return BigInteger(bytes)
}
} }
// Assuming cert type to role is 1:1 // Assuming cert type to role is 1:1