ECC public and private keys setters - cryptography

I need to test ECC verification with given public and private keys. I found methods for random keys generation but there are no setters for specific public/private keys. How to set public/private keys as byte array?
byte[] privateKeyBytes = new byte[]{(byte)0x24, (byte)0xF4, (byte)0x36, (byte)0x16, (byte)0xD0, (byte)0x96, (byte)0x12, (byte)0x63, (byte)0x90, (byte)0x2E, (byte)0x51, (byte)0xF6, (byte)0x87, (byte)0x55, (byte)0xAB, (byte)0xCB, (byte)0x5D, (byte)0xAC, (byte)0x56, (byte)0x1A, (byte)0xA5, (byte)0xFA, (byte)0x55, (byte)0xDB};
byte[] publicKeyBytes = new byte[]{(byte)0x71, (byte)0x0B, (byte)0xCD, (byte)0xF8, (byte)0xEE, (byte)0x7F, (byte)0x36, (byte)0x32, (byte)0xF4, (byte)0x3E, (byte)0x8B, (byte)0x20, (byte)0x54, (byte)0xF7, (byte)0x84, (byte)0x26, (byte)0x4E, (byte)0x96, (byte)0xD9, (byte)0xBA, (byte)0x0F, (byte)0x82, (byte)0x84, (byte)0x2D, (byte)0xC1, (byte)0x31, (byte)0xE0, (byte)0xBF, (byte)0x9F, (byte)0x60, (byte)0x5F, (byte)0xAE, (byte)0x3A, (byte)0xA1, (byte)0x43, (byte)0x50, (byte)0x88, (byte)0x87, (byte)0xFE, (byte)0x49, (byte)0x6C, (byte)0x1F, (byte)0xF6, (byte)0x82, (byte)0x73, (byte)0xD8, (byte)0x77, (byte)0x8F};
KeyPair pair = g.generateKeyPair();
PublicKey pubKey = pair.getPublic();
PrivateKey prikey = pair.getPrivate();

Public and Private Keys can't be set, because they should be generated.
According to this, you won't be able to set it.
Normally you are able to encode a message and put the public key into the encoding method. Mabye you have an "ImportParameters"-Function like in C#, where you can import a "Key" into the chosen algorithm like RSA.
Edit:
According to THIS answer, you can import like this.
I would suggest you generate the keys, store them using a serialisation as JSON or something, so you can import, deserialise and import them in the method again

Used for encoded keys:
private static PrivateKey generatePrivateKey(KeyFactory factory, byte[] content){
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
return factory.generatePrivate(privKeySpec);
}
private static PublicKey generatePublicKey(KeyFactory factory, byte[] content) {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
return factory.generatePublic(pubKeySpec);
}
For unencoded:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp192r1");
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(1, privateKeyBytes), spec);
ECNamedCurveSpec params = new ECNamedCurveSpec("secp192r1", spec.getCurve(), spec.getG(), spec.getN());
java.security.spec.ECPoint w = new java.security.spec.ECPoint(new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 0, 24)), new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 24, 48)));
PublicKey publicKey = factory.generatePublic(new java.security.spec.ECPublicKeySpec(w, params));

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==

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/

SalesForce marketing System not getting decrypted in Using java Crypto

I need some help in understanding, how to decrypt SalesforceMarketingCloud encrypted String into java.
I should admit that I have few things missing in my questions but this is what ever I have been provided so far
Algorithm – AES256, 256 Bit
Initilization vector – 16 bytes
Salt – 8 Bytes
Passphrase – “presharedpassphrase123”
As per my understanding the Input String has been encrypted using
EncryptSymmetric(#Clear, "AES", #null, #Passphrase, #null, #Salt, #null, #IV)
But when I try to decrypt it at Java side, it fails for various reason.
My best shot has been getting a garbage String as output so far when I used combination like below
NoPadding, CBS, and hashing my passphrase(which is 22 char long) to 32 bit using SHA-256
Can anyone please help, any Java code sample which you successfully used in past.
private String decryptWithKey(String myKey, byte[] strToDecrypt) throws Exception
{
MessageDigest sha = null;
try {
key = myKey.getBytes(CHAR_SCHEME);
sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 32);
secretKey = new SecretKeySpec(key, ALGO);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
byte[] ivByte = new byte[cipher.getBlockSize()];
ivByte = hexStringToByteArray("0716A494177F29F102AF33AFD0253BA1");;
System.out.println(new String(ivByte));
// IvParameterSpec ivParamsSpec = new IvParameterSpec(IV);
IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParamsSpec);
setDecryptedString(new String(cipher.doFinal(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: "+e.toString());
throw e;
}
return decryptedString;
}

Java byte array to ECCPrivateKey - InvalidKeySpecException: encoded key spec not recognised

When I try to make ECC private key from byte array, I get exception mentioned below. I have public/private keys and out signed output from C library micro-ecc/uECC.h. C used secp192r1 curve. I am trying to verify data with C generated keys in Java. How to convert byte array to private/public key?
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] kb = new byte[]{(byte)0x24, (byte)0xF4, (byte)0x36, (byte)0x16, (byte)0xD0, (byte)0x96, (byte)0x12, (byte)0x63, (byte)0x90, (byte)0x2E, (byte)0x51, (byte)0xF6, (byte)0x87, (byte)0x55, (byte)0xAB, (byte)0xCB, (byte)0x5D, (byte)0xAC, (byte)0x56, (byte)0x1A, (byte)0xA5, (byte)0xFA, (byte)0x55, (byte)0xDB};
X509EncodedKeySpec ks = new X509EncodedKeySpec(kb);
KeyFactory kf = java.security.KeyFactory.getInstance("ECDH", "BC");
org.bouncycastle.jce.interfaces.ECPrivateKey remotePublicKey = (org.bouncycastle.jce.interfaces.ECPrivateKey)kf.generatePublic(ks);
java.security.spec.InvalidKeySpecException: encoded key spec not recognised
at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
Also I have tried to use
KeyFactory.getInstance("ECDH", "BC");
but it throws the same exception above.
KeyFactory.getInstance("EC");
throws
java.security.InvalidKeyException: invalid key format
or
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=116, too big.
X509EncodedKeySpec(key) or PKCS8EncodedKeySpec(key) constructors take private/public keys in encoded format. Unencoded key bytes can be converted this way:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp192r1");
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(1, privateKeyBytes), spec);
ECNamedCurveSpec params = new ECNamedCurveSpec("secp192r1", spec.getCurve(), spec.getG(), spec.getN());
java.security.spec.ECPoint w = new java.security.spec.ECPoint(new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 0, 24)), new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 24, 48)));
PublicKey publicKey = factory.generatePublic(new java.security.spec.ECPublicKeySpec(w, params));

Why PBE generates same key with different salt and iteration count?

I am trying to test PBE encryption/decryption. I found that PBE generates same key with different salt and iteration count. Of course, the password used is same.
As what I understand, same password and different salt/iteration should get different keys.
Below is my test code:
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class PBETest
{
public static void main(String[] args)
throws Exception
{
String algo = "PBEWithSHA1andDESede";
System.out.println("====== " + algo + " ======");
char[] password = "password".toCharArray();
SecureRandom rand = new SecureRandom();
byte[] salt = new byte[32];
rand.nextBytes(salt);
int iterationCount = rand.nextInt(2048);
//encryption key
PBEKeySpec encPBESpec = new PBEKeySpec(password, salt, iterationCount);
SecretKeyFactory encKeyFact = SecretKeyFactory.getInstance(algo);
Key encKey = encKeyFact.generateSecret(encPBESpec);
System.out.println("encryptioin iteration: " + iterationCount);
//decryption key
rand.nextBytes(salt);
iterationCount = rand.nextInt(2048);
PBEKeySpec decPBESpec = new PBEKeySpec(password, salt, iterationCount);
SecretKeyFactory decKeyFact = SecretKeyFactory.getInstance(algo);
Key decKey = decKeyFact.generateSecret(decPBESpec);
System.out.println("decryptioin iteration: " + iterationCount);
System.out.println("encryption key is same as decryption key? " + encKey.equals(decKey));
}
}
I am expecting the final output is a false.
Did I do anything wrong?
You got spectacularly lucky, and your random salts and iteration counts just happened to match. Go directly to Las Vegas. Now. ;)
I googled for PBEWithSHA1andDESede and tracked down this example: http://cryptofreek.org/2010/06/04/encrypting-and-decrypting-files-with-java wherein he specifies the key alone with new PBEKeySpec(password) and creates a separate PBEParameterSpec using the salt and iteration count which is then passed to Cipher.init().
So, no, you did nothing wrong, you just stopped before the salt and count got stuffed into the cipher.
If you use PBKDF2WithHmacSHA1 instead of PBEWithSHA1andDESede your assumption works as it supports salt. You just need to add a the keyLength parameter to PBEKeySpec:
String algo = "PBKDF2WithHmacSHA1";
...
PBEKeySpec decPBESpec = new PBEKeySpec( password, salt, iterationCount, 128 );
I have run a test and the result is: false.
However, note that for encryption and decryption to work properly you need to use the same salt and iteration count when generating the key.