How to DER encode an ECDH Public Key in BouncyCastle Java - java

So I know how to encode/decode a public key in the BouncyCastle C# library into a byte array:
Encode:
PublicKey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(p1.Public).GetDerEncoded();
Decode:
ECPublicKeyParameters pubKey = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(OtherPublicKey);
I can't seem to figure out how to do this in the java version of the BouncyCastle Library since there doesn't seem to be a SubjectPublicKeyInfoFactory object in the Java version of the library. There does, however, seem to be a PublicKeyFactory class in Java so it looks like I can just use the same code but I don't know how to DER encode the public key in the java library. Can anyone help?? Thanks!
-----EDIT---------------------------------------------------------
Ok, so here is what I have so far in C#:
Create the ECDH instance:
public static ECDHBasicAgreement CreateECDHInstance(out byte[] PublicKey)
{
IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("ECDH");
FpCurve curve = new FpCurve(
new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
ECDomainParameters ecSpec = new ECDomainParameters(
curve,
curve.DecodePoint(Hex.Decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307"), // n
BigInteger.One); // h
g.Init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
AsymmetricCipherKeyPair aKeyPair = g.GenerateKeyPair();
ECDHBasicAgreement aKeyAgreeBasic = new ECDHBasicAgreement();
aKeyAgreeBasic.Init(aKeyPair.Private);
PublicKey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(aKeyPair.Public).GetDerEncoded();
return aKeyAgreeBasic;
}
This creates and returns a ECDHBasicAgreement object perfectly and outputs a public key in der encoded byte array form. Here is what I have in java:
public void testECDH() throws Exception
{
AsymmetricCipherKeyPairGenerator g = new ECKeyPairGenerator();
Fp curve = new Fp(
new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
ECDomainParameters ecSpec = new ECDomainParameters(
curve,
curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307"), // n
BigInteger.ONE); // h
g.init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
AsymmetricCipherKeyPair aKeyPair = g.generateKeyPair();
ECDHBasicAgreement aKeyAgreeBasic = new ECDHBasicAgreement();
aKeyAgreeBasic.init(aKeyPair.getPrivate());
// The part that doesn't work
//byte[] publickey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(aKeyPair.getPublic()).GetDerEncoded();
}
In java, there doesn't seem to be a SubjectPublicKeyInfoFactory class or the equivalent that can take the aKeyPair.getPublic() and be able to generate a DER encoded byte array. Can anyone please help!??!!? I'm about at my wits end! Thanks!!!!
----------EDIT 2 -------------------------------------------------------------------------
Ok, so here's where I'm at now:
public void test2() throws Exception
{
ECKeyPairGenerator g = new ECKeyPairGenerator();
Fp curve = new Fp(
new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
ECDomainParameters ecP = new ECDomainParameters(
curve,
curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307"), // n
BigInteger.ONE); // h
g.init(new ECKeyGenerationParameters(ecP, new SecureRandom()));
// Generate key pair
AsymmetricCipherKeyPair aKeys = g.generateKeyPair();
JCEECPublicKey jpub = new JCEECPublicKey("EC", (ECPublicKeyParameters)aKeys.getPublic());
JCEECPrivateKey jpriv = new JCEECPrivateKey("EC", (ECPrivateKeyParameters)aKeys.getPrivate());
KeyPair aKeyPair = new KeyPair(jpub, jpriv);
ECDHBasicAgreement aKeyAgree = new ECDHBasicAgreement();
aKeyAgree.init(aKeys.getPrivate());
byte[] encoded = aKeyPair.getPublic().getEncoded();
// The part that breaks now (Exception DERNull)
ECPublicKeyParameters decoded = decodeECPublicKeyParameters(encoded);
}
public static ECPublicKeyParameters decodeECPublicKeyParameters(byte[] pkByte) throws IOException {
return (ECPublicKeyParameters) PublicKeyFactory.createKey(pkByte);
}
So I've been able to get the public/private keys into JCEEC Key objects and have been able to encode them. When I attempt to decode them I get a DERNull exception. I ran some other tests and generated keys using the regular native java KeyPairGenerator and was able to encode/decode the keys so I know that this method does work. I think there is just something missing when I convert the AsymmetricCipherKeys into the JCEEC Keys. I did notice there is another parameter in the JCEECPublicKey construct, the third parameter of ECKeySpec. Only trouble is, I'm not exactly sure how to get an ECKeySpec out of the code I have so far (or if thats even the problem to begin with). Any other suggestions? Thanks!!!

Have you tried using the Bouncycastle SubjectPublicKeyInfo class? Something like:
byte [] derEncoded;
//...
SubjectPublicKeyInfo pkInfo = new SubjectPublicKeyInfo((ASN1Sequence)ASN1Object.fromByteArray(derEncoded))
EDIT:
There is an easy if somewhat unsatisfying way. You can use the JCEPublicKey class and it has a getEncoded() method that produces (I think) the correct answer.
EDIT 2:
I'm learning as I go :) It turns out you must identify the elliptic curve in the algorithm parameters, which makes sense. Here is a slight change that does that.
g.init(new ECKeyGenerationParameters(ecP, new SecureRandom()));
// Generate key pair
AsymmetricCipherKeyPair aKeys = g.generateKeyPair();
ECParameterSpec ecSpec = new ECParameterSpec(ecP.getCurve(), ecP.getG(), ecP.getN());
JCEECPublicKey jpub = new JCEECPublicKey("EC",
(ECPublicKeyParameters) aKeys.getPublic(), ecSpec);
JCEECPrivateKey jpriv = new JCEECPrivateKey("EC",
(ECPrivateKeyParameters) aKeys.getPrivate(), jpub, ecSpec);

Related

ECDSA signature generation KeyPair Java to C# - JcaPEMKeyConverter()

I have been converting over some code from a Java Android app to C# using Xamarin and I have come across a problem when trying to generate a signature using a certain snippet of BouncyCastle code.
Is there a replacement function in C# for the line of code
"pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);" ??
This is the Java code:
// Generating the signature
Signature signature = Signature.getInstance("SHA256withECDSA");
Reader rdr = new StringReader("privatekeygoeshere");
Object parsed = new PEMParser(rdr).readObject();
KeyPair pair;
pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);
PrivateKey signingKey = pair.getPrivate();
signature.initSign(signingKey);
signature.update(nonceData1);
signature.update(nonceData2);
signature.update(collectorID);
signature.update(publicKeyCompressed);
byte[] signedData = signature.sign();
I have found another way to read the private key and create a KeyPair. However, the private key is stored as a AsymmetricCipherKeyPair which I cannot add into the signature.InitSign() function as this requires an IPrivateKey.
The Different ways that I have tried to create a signature do not allow me to update other byte array data to the signature generation like the Java code, this doesn't work for me so I am really stuck.
I am also open to any ideas of signature generation.
Example of this here:
AsymmetricKeyParameter signingKey;
AsymmetricCipherKeyPair keyPair = null;
using (var textReader = new System.IO.StringReader("privatekeygoeshere"))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
I managed to come up with a solution for my problem using a string reader and looping through each array using the Update() command. This works well for me however, if any one can find a better way of doing this... Please comment below.
AsymmetricKeyParameter signingKey;
using (var textReader = new System.IO.StringReader(LONG_TERM_PRIVATE_KEY))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
var signer = SignerUtilities.GetSigner("SHA256withECDSA");
signer.Init(true, signingKey);
foreach (byte b in terminalNonce)
{
signer.Update(b);
}
foreach (byte b in mobileDeviceNonce)
{
signer.Update(b);
}
foreach (byte b in COLLECTOR_ID)
{
signer.Update(b);
}
foreach (byte b in terminalEphemeralPublicKeyCompressed)
{
signer.Update(b);
}
var signed = signer.GenerateSignature();

How to generate PublicKey for PrivateKey in X25519?

I'm working with X25519-keys based encryption at the moment.
My question is, basically, how to derive PublicKey from existing X25519 PrivateKey?
I have found the code in the XDHKeyPairGenerator:
BigInteger publicKey = ops.computePublic(privateKey.clone());
But this package is platform-specific, thus not accessible. And I can't find a method to do it through publicly-accessible interfaces.
So far I've discovered only one way to do it through JDK-provided interfaces (without using any additional libraries like Bouncy Castle or Google Tink):
public class StaticSecureRandom extends SecureRandom {
private final byte[] privateKey;
public StaticSecureRandom(byte[] privateKey) {
this.privateKey = privateKey.clone();
}
#Override
public void nextBytes(byte[] bytes) {
System.arraycopy(privateKey, 0, bytes, 0, privateKey.length);
}
}
public PublicKey generatePublicKeyFromPrivate(PrivateKey privateKey) throws GeneralSecurityException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(X25519);
keyPairGenerator.initialize(new NamedParameterSpec(X25519), new StaticSecureRandom(getScalar(privateKey)));
return keyPairGenerator.generateKeyPair().getPublic();
}
It's not a very elegant solution, but it works without any third-party libraries and I couldn't find any other way.
You must scalar multiply the private key (which is just a big number) by the 25519 curve generator point.
Here is some code in python to illustrate:
from tinyec import registry
import secrets
curve = registry.get_curve('curve25519')
def compress_point(point):
return hex(point.x) + hex(point.y % 2)[2:]
privKey = secrets.randbelow(curve.field.n)
pubKey = privKey * curve.g //the key step for you...
print("private key:", hex(privKey))
print("public key:", compress_point(pubKey))
If you let me know the Java lib I will try and help further.
BouncyCastle has Ed25519KeyPairGenerator, X25519KeyPairGenerator, PrivateKeyInfoFactory, and SubjectPublicKeyInfoFactory that can assist with making the keys. Here's an example in C#. Substitute ECKeyPairGenerator with X25519KeyPairGenerator. This example uses a standard keys and a NIST curve since I couldn't get Curve25519 working with the X25519 generated keys because the oid isn't supported in the current implementation.
public static async Task Bouncy()
{
var originalSecret = "X25519 example";
var message = Encoding.UTF8.GetBytes(originalSecret);
// Generate signing keys
var gen = new Ed25519KeyPairGenerator();
gen.Init(new Ed25519KeyGenerationParameters(new SecureRandom()));
var kp = gen.GenerateKeyPair();
// Sign data with private key
var signer = new Ed25519Signer();
signer.Init(true, kp.Private);
signer.BlockUpdate(message, 0, message.Length);
var sig = signer.GenerateSignature();
// Verify signature with public key
var verifier = new Ed25519Signer();
verifier.Init(false, kp.Public);
verifier.BlockUpdate(message, 0, message.Length);
var sigresult = verifier.VerifySignature(sig);
// Generate encryption keys
var genX = new ECKeyPairGenerator();
genX.Init(new KeyGenerationParameters(new SecureRandom(), 521));
var p1 = genX.GenerateKeyPair();
var p1_private = ECPrivateKeyStructure.GetInstance(PrivateKeyInfoFactory.CreatePrivateKeyInfo(p1.Private));
var p1_x25519_priv = new X25519PrivateKeyParameters(p1_private.GetDerEncoded(), 0);
var p2 = genX.GenerateKeyPair();
var p2_public = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(p2.Public);
var p2_x25519_pub = new X25519PublicKeyParameters(p2_public.GetDerEncoded(), 0);
// Generate secret from keys
var secret = new byte[32];
p1_x25519_priv.GenerateSecret(p2_x25519_pub, secret, 0);
// Setup ECIES (Elliptical Curve Integrated Encryption Scheme)
var gcm = new GcmBlockCipher(new AesEngine());
var ies = new IesEngine(new ECDHBasicAgreement(), new Kdf2BytesGenerator(new Sha512Digest()),
new HMac(new Sha512Digest()), new PaddedBufferedBlockCipher(gcm.GetUnderlyingCipher()));
// 256bit MAC, 256 key
var p = new IesWithCipherParameters(secret, new byte[1], 256, 256);
// Encrypt secret
ies.Init(true, p1.Private, p2.Public, p);
var encrypted = ies.ProcessBlock(message, 0, message.Length);
// Decrypt secret
ies.Init(false, p2.Private, p1.Public, p);
var decrypted = ies.ProcessBlock(encrypted, 0, encrypted.Length);
var decrypted_string = Encoding.UTF8.GetString(decrypted);
}

How to Create Digital Signature and Verify it in C# using PKCS 7 and SHA algorithm

I am trying to digitally sign xml document and verify the signature with the original xml file with public key and signed document. I have a java code for reference. I need to convert java code to C# where I have java code like this:
certList = new ArrayList<X509Certificate>();
certList.add(signerCert);
certStore = new JcaCertStore(certList);
signedDataGenerator = new CMSSignedDataGenerator();
ContentSigner sha2Signer = new JcaContentSignerBuilder("SHA512with" + privateKey.getAlgorithm()).build(privateKey);
ignedDataGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).setDirectSignature(true).build(sha2Signer, signerCert));
signedDataGenerator.addCertificates(certStore);
CMSSignedData sigData = signedDataGenerator.generate(new CMSProcessableFile(inputXmlFile), false);
signedBytes = sigData.getEncoded();
I have converted java code to C# like this:
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("oppose no valid application was found");
}
// Hash the data
SHA512Managed sha1 = new SHA512Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
I am trying to convert it since two days, It is generating sign byte array but not been able to verify. While verifying it is throwing bad hash\r\n error I shall be highly grateful for any assistance. I know I am somewhere wrong in converting the java code to C#. I am able to verify the code but not been able to sign the document
I have generated Signature using System.Security.Cryptography.Pkcs library like this
public static byte[] Sign(byte[] data, X509Certificate2 certificate)
{
if (data == null)
throw new ArgumentNullException("data");
if (certificate == null)
throw new ArgumentNullException("certificate");
// setup the data to sign
ContentInfo content = new ContentInfo(data);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
// create the signature
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
and verify the signature like this
private static bool VerifySignatures(FileInfo contentFile, Stream signedDataStream)
{
CmsProcessable signedContent = null;
CmsSignedData cmsSignedData = null;
Org.BouncyCastle.X509.Store.IX509Store store = null;
ICollection signers = null;
bool verifiedStatus = false;
try
{
//Org.BouncyCastle.Security.addProvider(new BouncyCastleProvider());
signedContent = new CmsProcessableFile(contentFile);
cmsSignedData = new CmsSignedData(signedContent, signedDataStream);
store = cmsSignedData.GetCertificates("Collection");//.getCertificates();
IX509Store certStore = cmsSignedData.GetCertificates("Collection");
signers = cmsSignedData.GetSignerInfos().GetSigners();
foreach (var item in signers)
{
SignerInformation signer = (SignerInformation)item;
var certCollection = certStore.GetMatches(signer.SignerID);
IEnumerator iter = certCollection.GetEnumerator();
iter.MoveNext();
var cert = (Org.BouncyCastle.X509.X509Certificate)iter.Current;
verifiedStatus = signer.Verify(cert.GetPublicKey());
}
}
catch (Exception e)
{
throw e;
}
return verifiedStatus;
}
It is working for me

C# equivalent of Java's KDFCounterBytesGenerator (from bouncycastle)

I want to port this code to C#
byte[] result = new byte[length];
byte[] data = ...;
byte[] key = ...;
HMac hMac = new HMac(new SHA256Digest());
KDFCounterBytesGenerator g = new KDFCounterBytesGenerator(hMac);
g.init(new KDFCounterParameters(key, data, 32));
g.generateBytes(result , 0, result .length);
But I can't find any equivalent classes in C# bouncycastle library.
Any advice?
Edit:
I ported the KDFCounterBytesGenerator from the Java source and it worked fine..
BouncyCastle has an equivalent library for C# that should contain most if not all of the same features as it's Java version with some minor syntax differences:
You should be able to install it via NuGet and then reference it accordingly :
// Using statements (for BouncyCastle)
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
// Define your byte[]s
byte[] result = new byte[1];
byte[] data = new byte[2];
byte[] key = new byte[3];
// Build your HMac, Generator and generate your bytes
HMac hMac = new HMac(new Sha256Digest());
Kdf1BytesGenerator g = new Kdf1BytesGenerator(hMac.GetUnderlyingDigest());
g.Init(new Org.BouncyCastle.Crypto.Parameters.KdfParameters(key, data));
g.GenerateBytes(result, 0, result.Length);

Get public key from private in Java

I remember do this long time ago with OpenSSL, but I want to know if it's possible and how, I've never used Cryptography on java.
The assumption is that we are talking about RSA private and Public keys. Then, if you are working from a PEM format file, then first you need to read the private key from the file into a PrivateKey object:
public PrivateKey readPemRsaPrivateKey(String pemFilename) throws
java.io.IOException,
java.security.NoSuchAlgorithmException,
java.security.spec.InvalidKeySpecException
{
String pemString = File2String(pemFilename);
pemString = pemString.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
pemString = pemString.replace("-----END RSA PRIVATE KEY-----", "");
byte[] decoded = Base64.decodeBase64(pemString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
where File2String is something like:
private static String File2String(String fileName) throws
java.io.FileNotFoundException, java.io.IOException
{
File file = new File(fileName);
char[] buffer = null;
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
buffer = new char[(int)file.length()];
int i = 0;
int c = bufferedReader.read();
while (c != -1) {
buffer[i++] = (char)c;
c = bufferedReader.read();
}
return new String(buffer);
}
Now you can generate the corresponding PublicKey with code like this:
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec;
...
PrivateKey myPrivateKey = readPemRsaPrivateKey(myPrivateKeyPemFileName);
RSAPrivateCrtKey privk = (RSAPrivateCrtKey)myPrivateKey;
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey myPublicKey = keyFactory.generatePublic(publicKeySpec);
Credits: How to get a RSA PublicKey by giving a PrivateKey?
Please make sure that Eli Rosencruft answer is basically correct, but the order of the modulus and the public exponent are incorrect! This is the correct statement:
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
You cannot generate either key directly from the other. It is mathematically impossible. If you had a key blob that contained both the public and private keys, you could extract either one of them with relative ease.
EDIT, 2017: Many years and a much better understanding of crypto later, and it's now clear to me that this answer isn't really correct.
To quote Wikipedia:
The public key consists of the modulus n and the public (or encryption) exponent e. The private key consists of the modulus n and the private (or decryption) exponent d, which must be kept secret. p, q, and λ(n) must also be kept secret because they can be used to calculate d.
The public modulus n can be computed as p × q. The only thing missing from a raw private key is e, but this value is usually selected as 65537, and if not you can still compute e from d and λ(n).
However, many private key storage formats actually contain the public modulus n alongside the other components, so you can just do a direct extraction of the values.
EDIT, 2018: Still getting downvotes for this, and rightly so! I'm leaving this answer up so people can see why I was originally wrong, and to remind myself not to be wrong in future.

Categories

Resources