Can't get ECparameters: namedcurve in subjectPublicKeyInfo of certificate - ssl

I'm struggling to make my way through implementing a DTLS 1.2 handshake using ECDSA, and I'm having trouble with the client certificate. When I generate it, it seems as though the subjectPublicKeyInfo is incorrect: in wireshark instead of seeing ECParameters: namedCurve inside the algorithm, I seem to get an unparsed OID.
What I expect:
What I see:
I'm generating the certificate like so (I expect this is full of errors, I'd love any guidance there!):
Generating the keypair:
private fun generateEcKeyPair(): AsymmetricCipherKeyPair {
val generator = ECKeyPairGenerator()
val curveSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
generator.init(
ECKeyGenerationParameters(
ECDomainParameters(
curveSpec.curve,
curveSpec.g,
curveSpec.n,
curveSpec.h,
curveSpec.seed
),
SecureRandom()
)
)
return generator.generateKeyPair()
}
Generating the certificate (which uses the keypair generated from the above method):
private fun generateX509Certificate(
subject: X500Name,
keyPair: AsymmetricCipherKeyPair
): org.bouncycastle.asn1.x509.Certificate {
val now = System.currentTimeMillis()
val notBefore = Date(now - Duration.ofDays(1).toMillis())
val notAfter = Date(now + Duration.ofDays(7).toMillis())
val certBuilder = X509v3CertificateBuilder(
subject,
BigInteger.valueOf(now),
notBefore,
notAfter,
subject,
SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyPair.public)
)
val signatureAlgoIdentifier =
DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgo)
val digestAlgoIdentifier =
DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgoIdentifier)
val signer =
BcECContentSignerBuilder(signatureAlgoIdentifier, digestAlgoIdentifier).build(keyPair.private)
return certBuilder.build(signer).toASN1Structure()
}
What am I doing wrong here? Am I roughly on the right track?

I think I managed to stumble onto the fix for this. I changed my generateEcKeyPair implementation to the following (after adding BouncyCastleProvider as a provider):
fun generateEcKeyPair(): KeyPair {
val keyGen = KeyPairGenerator.getInstance("EC", "BC")
val ecCurveSpec = ECNamedCurveTable.getParameterSpec("secp256r1")
keyGen.initialize(ecCurveSpec)
return keyGen.generateKeyPair()
}
And it looks like I'm getting the curve named properly in the cert now.
Maybe I was passing the parameters in incorrectly before? Or creating it manually didn't preserve the named curve correctly?

Related

Validate EC SHA 256 signature in .net without bouncy castle

I am implementing Apple's App Attestation service.
As part of the process, i receive a EC key and a signature.
Sample key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaF
z/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==
-----END PUBLIC KEY-----
Sample signature:
MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=
Sample sha256 hash:
S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=
If i put this into a couple of txt files like so:
System.IO.File.WriteAllBytes("/wherever/sig", Convert.FromBase64String(sampleSignature));
System.IO.File.WriteAllBytes("/wherever/hash", Convert.FromBase64String(sampleSha256Hash));
Then i can validate the signature with Openssl like so
openssl dgst -sha256 -verify sampleKey.pem -signature /wherever/sig /wherever/hash
(the above outputs)
Verified OK
I can verify the signature using Bouncy Castle like so:
var bouncyCert = DotNetUtilities.FromX509Certificate(certificate);
var bouncyPk = (ECPublicKeyParameters)bouncyCert.GetPublicKey();
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, bouncyPk);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // Happy days, this is true
Since i don't want to share my whole certificate here, the same sample may be achieved as follows:
// these are the values from the sample key shared at the start of the post
// as returned by BC. Note that .Net's Y byte array is completely different.
Org.BouncyCastle.Math.BigInteger x = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w="));
Org.BouncyCastle.Math.BigInteger y = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("ALFvFjNYNk96AbgUf6yddpdA9gPpAjNk1j+RwyMga1RO"));
X9ECParameters nistParams = NistNamedCurves.GetByName("P-256");
ECDomainParameters domainParameters = new ECDomainParameters(nistParams.Curve, nistParams.G, nistParams.N, nistParams.H, nistParams.GetSeed());
var G = nistParams.G;
Org.BouncyCastle.Math.EC.ECCurve curve = nistParams.Curve;
Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);
ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // again, happy days.
However, i really want to avoid using bouncy castle.
So i am trying to use ECDsa available in .net core:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
var certificate = new X509Certificate2(cert);
var publicKey = certificate.GetECDsaPublicKey();
var valid = publicKey.VerifyHash(sha256HashByteArray, signature); // FALSE :(
if you want to try to run the above here's the sample that creates the keys without the whole certificate:
using System.Security.Cryptography;
var ecParams = new ECParameters();
ecParams.Curve = ECCurve.CreateFromValue("1.2.840.10045.3.1.7");
ecParams.Q.X = Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w=");
// I KNOW that this is different from BC sample - i got each respective values from
// certificates in respective libraries, and it seems the way they format the coordinates
// are different.
ecParams.Q.Y = Convert.FromBase64String("sW8WM1g2T3oBuBR/rJ12l0D2A+kCM2TWP5HDIyBrVE4=");
var ecDsa = ECDsa.Create(ecParams);
var isValid = ecDsa.VerifyHash(nonce, signature); // FALSE :(
I tried using VerifyData() instead and feeding raw data and HashAlgorithmName.SHA256 with no luck.
I found a response here (https://stackoverflow.com/a/49449863/2057955) that seems to suggest that .net expects the signature as r,s concatenation, so i pulled them out of the DER sequence that i get back from my device (see sample signature) however that had no luck at all, i just can't get that 'true' back.
Question: how can i verify this EC signature using .Net Core on LINUX/MacOs (so unable to use ECDsaCng class)?
SignerUtilities.GetSigner() hashes implicitly, i.e. sha256HashByteArray is hashed again. Therefore instead of ECDsa#VerifyHash() (does not hash implicitly) the method ECDsa#VerifyData() (hashes implicitly) must be used.
Also, SignerUtilities.GetSigner() returns a signature in ASN.1 format, and ECDsa#VerifyData() expects a signature in r|s format (as you already figured out).
If both are taken into account, the verification is successful:
byte[] publicKey = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==");
byte[] sha256HashByteArray = Convert.FromBase64String("S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=");
byte[] signatureRS = Convert.FromBase64String("10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==");
var ecDsa = ECDsa.Create();
ecDsa.ImportSubjectPublicKeyInfo(publicKey, out _);
var isValid = ecDsa.VerifyData(sha256HashByteArray, signatureRS, HashAlgorithmName.SHA256);
Console.WriteLine(isValid); // True
Regarding the signature formats:
The posted signature in ASN.1 format
MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=
is hex encoded
3045022100d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c229022034bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75
From this, the signature in r|s format can be derived as (s. here)
d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c22934bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75
or Base64 encoded:
10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==

Encrypting large file with PGP (BouncyGPG) and sending it over SFTP (JSch) in Kotlin

I've been trying to encrypt large files with PGP (I'm using BouncyGPG for that) and sending it to a remote server using SFTP (using JSch). I'm using PipedOutputStream and PipedInputStream to avoid saving the final encrypted file locally before sending to the server. I believe BouncyGPG is misbehaving because of that.
Can anyone help me with this?
Here's my code:
BouncyGPG.registerProvider()
private val bufferSize: Int = 8 * 1024
fun sendFileSFTP(myFile: MyFile {
PipedInputStream().use { input ->
GlobalScope.launch {
PipedOutputStream(input).use { output ->
encrypt(myFile, output)
}
}
sendSFTP(input, "mysftp-directory/${myFile.filename}")
log.info("Finished Sending $myFile")
}
}
fun encrypt(myFile: MyFile output: OutputStream) {
log.info("ENCRYPTING - $myFile")
val pubkey = "/home/myhome/server.gpg"
val privkey = "/home/myhome/server.secret.gpg"
val password = "password"
val keyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withPassword(password))
keyring.addPublicKey(FileReader(File(pubkey)).readText().toByteArray(charset("US-ASCII")))
keyring.addSecretKey(FileReader(File(privkey)).readText().toByteArray(charset("US-ASCII")))
BouncyGPG.encryptToStream()
.withConfig(keyring)
.withStrongAlgorithms()
.toRecipients("contact#myserver.com")
.andDoNotSign()
.binaryOutput()
.andWriteTo(output)
val filepath = Paths.get(myFile.path, myFile.filename)
BufferedInputStream(FileInputStream(filepath.toString())).use { input ->
input.copyTo(output, bufferSize)
}
log.info("FINISHED ENCRYPTING - $myFile")
}
fun sendSFTP(inputStream: InputStream, dest: String) {
log.info("Sending $dest")
var jsch = JSch()
jsch.setKnownHosts(properties.knownHosts)
val jschSession: Session = jsch.getSession(properties.sFTPUsername, properties.sFTPHost, properties.sFTPPort)
jschSession.setPassword(properties.sFTPPassword)
jschSession.connect()
val channel = jschSession.openChannel("sftp") as ChannelSftp
channel.connect()
channel.put(inputStream, dest)
}
I'm assuming that BouncyGPG is not singing my file due to the lack of closing (I believe I've read somewhere that it only signs the file after the output is closed, but as far as I know, kotlin will close the output for me already. Am I missing something?
Thanks
After debating with this issue for a while, I gave a try and generated my own pgp keys (I was using a test key our client gave us) and guess what? It worked!
Apparently there was something wrong with them! ¯_(ツ)_/¯
So, my code is working!

Generate a key pair in "node-forge" using "exponent", "modulus" and "maxdigits"

I'm using "node-forge" to generate a publicKey to use with my AES symmetric key but I don't know how to use the data provided by my backend to create this publicKey. Currently, I receive from an authentication api the following:
e: "10001"
n:"c7c5dd235568711a943ebbdacac890ca2cf12c1ab539f77726e8874d2ab4220cf06369358b5eff0425fb17d4f696f741cf04c5ea874415e7f67d118a2e763e641e8675b8f42e9277b3f70f14e4de23fe16f51abdc427490f47e4b28ae3e5eb3563ba797fe90f9b70ba878646b1b297c52ba735827682b67309d38b423e31b50b"
maxdigits: "131"
Where "e" is my exponent, "n" is my module and "maxdigits" is the length my BigIntegers are supposed to have.
But when I try something like this:
const keys = forge.pki.rsa.generateKeyPair({ e: res.e, n: res.n });
My backend returns an error. What am I doing wrong?
forge.pki.rsa.generateKeyPair is the wrong method in this context. forge.pki.rsa.generateKeyPair creates a new key pair with random modulus. The first parameter specifies the modulus/key size in bits, the second the exponent ([1] and [2]):
// var forge = require('node-forge'); // in nodejs-context
var pki = forge.pki;
var rsa = forge.pki.rsa;
var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001});
var pubKeyPEM = pki.publicKeyToPem(keypair.publicKey);
var privKeyPEM = pki.privateKeyToPem(keypair.privateKey);
console.log(pubKeyPEM);
console.log(privKeyPEM);
The forge.pki.rsa.setPublicKey-method is used to generate a public key via modulus and exponent, where the modulus is the first parameter and the exponent is the second parameter ([2]), both of type forge.jsbn.BigInteger ([3]):
var BigInteger = forge.jsbn.BigInteger;
var n = new BigInteger('c7c5dd235568711a943ebbdacac890ca2cf12c1ab539f77726e8874d2ab4220cf06369358b5eff0425fb17d4f696f741cf04c5ea874415e7f67d118a2e763e641e8675b8f42e9277b3f70f14e4de23fe16f51abdc427490f47e4b28ae3e5eb3563ba797fe90f9b70ba878646b1b297c52ba735827682b67309d38b423e31b50b', 16);
var e = new BigInteger('10001', 16);
var pubKey = rsa.setPublicKey(n, e);
var pubKeyPEM = pki.publicKeyToPem(pubKey)
console.log(pubKeyPEM); // Check with e.g. https://lapo.it/asn1js/

Contract verification failure in corda Hello World pt 2

I'm following the tutorial in corda tutorial pt 2 using kotlin. Everytime I try to start a new flow via the CRaSH shell in PartyA using the next command:
start IOUFlow iouValue: 30, otherParty: "C=US, L=New York, O=PartyB"
I get a Contract Verification Failure:
Done
Contract verification failed: List has more than one element., contract: com.template.IOUContract#7c109db7, transaction: D08920023D788F80F289527BD9C27BCD54B7DAC6C53866BFA7B90B23E0E4749B
IOUFlow class:
#InitiatingFlow
#StartableByRPC
class IOUFlow(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
override val progressTracker = ProgressTracker()
#Suspendable
override fun call() {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), listOf(ourIdentity.owningKey, otherParty.owningKey))
val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
.addCommand(cmd)
txBuilder.withItems(outputContractAndState, cmd)
txBuilder.verify(serviceHub)
val signedTx = serviceHub.signInitialTransaction(txBuilder)
val otherpartySession = initiateFlow(otherParty)
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker()))
subFlow(FinalityFlow(fullySignedTx))
}
}
I've tried modifying App.kt to deal with this problem without luck. Does anyone know what the problem is?
Thanks in advance for your help.
The issue is that you're adding the command and output to the transaction twice:
val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
.addCommand(cmd)
txBuilder.withItems(outputContractAndState, cmd)
This causes the contract verification to fail, as you have two outputs instead of one.

TLS 1.2 ECDHE_RSA signature

I'm currently working on a Java TLS server. I'm trying to get the following CipherSuite to work : TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
When I test it using openssl s_client I get the following error after the ServerKeyExchange message :
140735242416208:error:1414D172:SSL
routines:tls12_check_peer_sigalg:wrong signature type:t1_lib.c:1130:
Here is the TLS message as seen in Wireshark
The Handshake fails on a decode_error fatal error.
So I guess the client doesn't like the signature algorithm chosen.
But I am only using the default SignatureAndHashAlgorithm for now as per RFC 5246 Section-7.4.1.4.1
If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had sent
the value {sha1,rsa}.
(I'm still checking if the client do offer theses default values though)
Since I'm doing ECDHE_RSA I believe I should hash and sign the serverECDHparams as per RFC 4492 Section 5.4 (First post here so only 2 links sorry :) )
ServerKeyExchange.signed_params.sha_hash
SHA(ClientHello.random + ServerHello.random +
ServerKeyExchange.params);
struct {
select (KeyExchangeAlgorithm) {
case ec_diffie_hellman:
ServerECDHParams params;
Signature signed_params;
};
} ServerKeyExchange;
And I should do this as per RFC 2246 Section 7.4.3
select (SignatureAlgorithm) {
case rsa:
digitally-signed struct {
opaque md5_hash[16];
opaque sha_hash[20];
};
} Signature;
md5_hash
MD5(ClientHello.random + ServerHello.random + ServerParams);
sha_hash
SHA(ClientHello.random + ServerHello.random + ServerParams);
My Java code regarding signing the serverParams :
private byte[] getSignedParams(ChannelBuffer params)
throws NoSuchAlgorithmException, DigestException,
SignatureException, InvalidKeyException {
byte[] signedParams = null;
ChannelBuffer signAlg = ChannelBuffers.buffer(2);
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
switch (session.cipherSuite.sign) {
case rsa:
signAlg.writeByte(2); // 2 for SHA1
sha.update(clientRandom);
sha.update(serverRandom);
sha.update(params.toByteBuffer());
md5.update(clientRandom);
md5.update(serverRandom);
md5.update(params.toByteBuffer());
signedParams = concat(md5.digest(), sha.digest());
break;
}
signAlg.writeByte(session.cipherSuite.sign.value); // for RSA he byte is one
ChannelBuffer signLength = ChannelBuffers.buffer(2);
signLength.writeShort(signedParams.length);
return concat(signAlg.array(),concat(signLength.array(),signedParams));
}
So my question is basically : Am I wrong about all this ? and if so, what am I doing wrong ?
Thank you for your time ! :)
It's me again, I seem to have fixed my particular problem 2 things I noted :
Regarding my Java code, the MessageDigest class only does hashing no signing so I now use the Signature class instead.
It seems I only need to sign using SHA1 in TLS1.2 I don't need to do MD5 at all.
The second item is what I should have found in the RFC but didn't (maybe it is written somewhere, I don't know) I think this could be useful for people even if they're not doing Java ;)
How my code looks now :
private byte[] getSignedParams(ChannelBuffer params)
throws NoSuchAlgorithmException, DigestException,
SignatureException, InvalidKeyException {
byte[] signedParams = null;
Signature signature = Signature.getInstance(selectedSignAndHash.toString());
ChannelBuffer signAlg = ChannelBuffers.buffer(2);
signAlg.writeByte(selectedSignAndHash.hash.value);
signature.initSign(privateKey);
signature.update(clientRandom);
signature.update(serverRandom);
signature.update(params.toByteBuffer());
signedParams = signature.sign();
signAlg.writeByte(session.cipherSuite.sign.value);
ChannelBuffer signLength = ChannelBuffers.buffer(2);
signLength.writeShort(signedParams.length);
return concat(signAlg.array(), concat(signLength.array(), signedParams));
}
The code is different because in between I added a function to choose the SignatureAndHashAlgorithm to use from the list the client provides. But you could modify this to only respond using SHA1withRSA as this seems to be the default HashAndSignatureAlgorithm.