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();
Related
please help me to solve the following:
I have a code on Java
java code bellow:
StringBuilder fullText;
KeyStore p12 = KeyStore.getInstance("PKCS12");
p12.load(new FileInputStream("FileName.p12"), "1234".toCharArray());
Key key = (Key) p12.getKey("1", "1234".toCharArray());
//signing
Signature signer = Signature.getInstance("SHA1withRSA");
signer.initSign((PrivateKey) key);
signer.update(fullText.toString().getBytes());
b`yte[] digitalSignature = signer.sign();
String base64sign = new String(Base64.getEncoder().encode(digitalSignature));
I tried to reproduce it on .Net platform.
I create a code on .NET 3.5 platform. The code on X++ below:
public static boolean Encrypt(str sXmlDoc)
{
boolean bSuccess = false;
System.Security.Cryptography.X509Certificates.X509Certificate2 p12;
System.Security.Cryptography.AsymmetricAlgorithm key;
str sBase64Cert;
str sBase64Xml;
str sBase64Sign;
str sTmp;
System.Byte[] byteArray;
System.Security.Cryptography.Xml.Signature signer;
System.Exception ex;
str sKeyPublic;
System.Byte[] keyPublic;
System.Int32 myInt32;
int myInt;
System.Byte[] byteTmp, byteTmp2;
System.Text.ASCIIEncoding txtEncoder;
System.Security.Cryptography.Xml.KeyInfo keyInfo;
System.Security.Cryptography.Xml.SignedXml signedXml;
System.Xml.XmlDocument xmlDocument;
System.Xml.XmlElement xmlElement;
System.Security.Cryptography.Xml.SignedInfo signedInfo;
System.Security.Cryptography.Xml.Reference reference;
System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform env;
System.Security.Cryptography.Xml.RSAKeyValue rsaKeyValue;
System.Security.Cryptography.RSA rsaKey;
try
{
p12 = new System.Security.Cryptography.X509Certificates.X509Certificate2("fileName.p12", "pass");
if (p12)
{
//Signature
//TEST
if (p12.get_HasPrivateKey())
{
key = p12.get_PrivateKey();
rsaKey = p12.get_PrivateKey();
xmlDocument = new System.Xml.XmlDocument();
xmlDocument.set_PreserveWhitespace(true); //Allow white spaces
xmlDocument.LoadXml(sXmlDoc);
signedXml = new System.Security.Cryptography.Xml.SignedXml(xmlDocument);
signedXml.set_SigningKey(key);
keyInfo = new System.Security.Cryptography.Xml.KeyInfo();
rsaKeyValue = new System.Security.Cryptography.Xml.RSAKeyValue(rsaKey);
keyInfo.AddClause(rsaKeyValue);
signedXml.set_KeyInfo(keyInfo);
// Create a reference to be signed.
//System.Security.Cryptography.Xml.Reference reference;
reference = new System.Security.Cryptography.Xml.Reference();
reference.set_Uri("");
// Add an enveloped transformation to the reference.
env = new System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
signedXml.set_KeyInfo(keyInfo);
signedXml.ComputeSignature();
xmlElement = signedXml.GetXml();
signer = new System.Security.Cryptography.Xml.Signature();
signer = signedXml.get_Signature();
signedInfo = new System.Security.Cryptography.Xml.SignedInfo();
signedInfo = signer.get_SignedInfo();
byteTmp = signer.get_SignatureValue();
sTmp = System.Convert::ToBase64String(byteTmp);
sBase64Sign = "<signature>"+sTmp+"</signature>";
info(sBase64Sign);
}
}
}
catch (Exception::CLRError)
{
ex = ClrInterop::getLastException();
if (ex != null)
{
ex = ex.get_InnerException();
if (ex != null)
{
error(ex.ToString());
}
}
}
return bSuccess;
}
But the result differs than I got on java. I opened a p12 key. I signed an XML sting and got the signature for this XML, but got the wrong string. What did I do wrong?
The Java code doesn't say anything about XML, so I don't know if you learned anything about porting code, but if you do use XML in C# then it's going to fail.
In short, you need to use the RSA functions directly. Starting with RSA.Create() might make a lot of sense in most languages. However, .NET is rather certificate / key based (you perform operations on the key rather than using the key, for better or worse, and private keys are considered part of the certificate that they belong to). So using a constructor to read PKCS#12 is probably a better starting point.
That's all for this little tutorial. I don't believe for a second that you thought that your code would be a correct port, so start over. Happy programming.
EDIT: Oh, one last hint: SHA1withRSA is RSA using PKCS#1 v1.5 padding for signature generation, using SHA-1 as underlying hash function (which of course means it is SHATTERED and more).
The best way to sign an XML on .NET is by using a Bouncy Castle library. I hope anybody in one day will solve it on .NET framework, without using an external library, but the solution has found via using BounsyCastle.
This code on C# used BouncyCastle.
string item = string.Empty;
Pkcs12Store p12 = new Pkcs12Store();
p12.Load(_p12, _p12_psw.ToCharArray());
string alias = "";
foreach (string al in p12.Aliases)
{
if (p12.IsKeyEntry(al) && p12.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
}
//signature
var data = Encoding.UTF8.GetBytes(xmlDoc);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(true,p12.GetKey(alias).Key);
signer.BlockUpdate(data, 0, data.Length);
var sign = signer.GenerateSignature();
string base64Sign = Convert.ToBase64String(sign);
item = "<signature>"+base64Sign+"</signature>", base64Sign);
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);
}
I'm trying to implement digital signature in php as in java sample code below:
Signature rsaSig = Signature.getInstance("MD5withRSA");
RSAPrivateKey clientPrivateKey = readPrivateKeyFromFile(fileName);
rsaSig.initSign(clientPrivateKey);
String source = msg;
byte temp[] = source.getBytes();
rsaSig.update(temp);
byte sig[] = rsaSig.sign();
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(sig);
My php code :
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // in xml format
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
But looks like some thing is missing. We should get same signature as java code returns.Can anybody guide me in this?
By default phpseclib uses sha1 as the hash. You probably need to do $rsa->setHash('md5').
I would like to use id_rsa and id_rsa.pub to create a challenge-response login system for an application in Java. Towards this purpose, I want to be able to construct PublicKey and PrivateKey from id_rsa and id_rsa.pub.
The direct approach would be to parse these in the way I would normally parse a text file, and then manually construct the appropriate java.security data structures for signing and verifying in the client and server.
Is there a standard library shortcut that will ingest these files directly?
The closest and easiest way I know, would be to use The Legion of the Bouncy Castle Java API with something like this -
// Just for the public / private key files...
private static String readFileAsString(
String filePath) throws java.io.IOException {
StringBuilder sb = new StringBuilder(100);
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(
filePath));
int fileIn;
while ((fileIn = reader.read()) != -1) {
sb.append((char) fileIn);
}
} finally {
reader.close();
}
return sb.toString();
}
// Add the SecurityProvider and a Base64 Decoder (Once)
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
BASE64Decoder b64 = new BASE64Decoder();
// For the publicKey
String publicKeyString = readFileAsString(publicKeyFileName);
AsymmetricKeyParameter publicKey =
(AsymmetricKeyParameter) PublicKeyFactory.createKey(b64.decodeBuffer(publicKeyString));
// For the privateKey
String privateKeyString = readFileAsString(privateKeyFilename);
AsymmetricKeyParameter privateKey =
(AsymmetricKeyParameter) PrivateKeyFactory.createKey(b64.decodeBuffer(privateKeyString));
yes, I just wrote it: openssh-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);