I have the following code to extract Private Key
PEMParser parser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(decoded)));
Object object = parser.readObject();
PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder()
.build(props.getProperty(KeytoolFlags.KEYPASS.name()).toCharArray());
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
if (object instanceof PEMEncryptedKeyPair) {
KeyPair pair = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(provider));
return loadPublic ? pair.getPublic() : pair.getPrivate();
} else if (object instanceof PEMKeyPair) {
return loadPublic ? converter.getPublicKey(((PEMKeyPair) (object)).getPublicKeyInfo())
: converter.getPrivateKey(((PEMKeyPair) (object)).getPrivateKeyInfo());
} else {
InputDecryptorProvider p2 = new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(props.getProperty(KeytoolFlags.KEYPASS.name()).toCharArray());
return converter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(p2));
}
I would like to get the Public Key from converter when it's JceOpenSSLPKCS8DecryptorProviderBuilder. Is there any way?
Thanks,
The simplest way, although it feels rather ugly to me, is to convert the private key 'back' to one of OpenSSL's 'legacy' forms, which PEMParser is then able to turn into a PEMKeyPair with both halves, from which the public can be selected. Otherwise, the method must be tailored depending on the key algorithm aka type, but can be more efficient which I like better. Here are both options for your consideration:
public static void SO57043669PKCS8_Public_BC (String[] args) throws Exception {
Object p8e = new PEMParser (new FileReader (args[0])).readObject();
// for PKCS8-encrypted result is org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo
PrivateKeyInfo p8i = ((PKCS8EncryptedPrivateKeyInfo)p8e).decryptPrivateKeyInfo(
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(args[1].toCharArray()) );
// or get org.bouncycastle.asn1.pkcs.PrivateKeyInfo directly from PEMParser for PKCS8-clear
PublicKey pub = null;
if( args.length>=3 ){ // the simple way:
PrivateKey prv = new JcaPEMKeyConverter().getPrivateKey(p8i);
PemObject old = new JcaMiscPEMGenerator (prv,null).generate();
StringWriter w1 = new StringWriter();
PemWriter w2 = new PemWriter(w1);
w2.writeObject(old); w2.close();
Object pair = new PEMParser(new StringReader(w1.toString())).readObject();
pub = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)pair).getPublic();
}else{
ASN1ObjectIdentifier id = p8i.getPrivateKeyAlgorithm().getAlgorithm();
PKCS8EncodedKeySpec p8s = new PKCS8EncodedKeySpec (p8i.getEncoded());
if( id.equals(PKCSObjectIdentifiers.rsaEncryption) ){
// the standard PKCS1 private key format for RSA redundantly includes e
KeyFactory rfact = KeyFactory.getInstance("RSA");
RSAPrivateCrtKey rprv = (RSAPrivateCrtKey) rfact.generatePrivate(p8s);
// or JcaPEMKeyConverter.getPrivateKey does the same thing
pub = /*(RSAPublicKey)*/ rfact.generatePublic(
new RSAPublicKeySpec (rprv.getModulus(), rprv.getPublicExponent()));
}else if( id.equals(X9ObjectIdentifiers.id_dsa) ){
// the apparently ad-hoc format OpenSSL uses for DSA does not include y but it can be computed
KeyFactory dfact = KeyFactory.getInstance("DSA");
DSAPrivateKey dprv = (DSAPrivateKey) dfact.generatePrivate(p8s);
// or JcaPEMKeyConverter.getPrivateKey does the same thing
BigInteger p = dprv.getParams().getP(), q = dprv.getParams().getQ(), g = dprv.getParams().getG();
pub = /*(DSAPublicKey)*/ dfact.generatePublic (
new DSAPublicKeySpec(g.modPow(dprv.getX(),p), p, q, g) );
// warning: naive computation probably vulnerable to sidechannel attack if any
}else if( id.equals(X9ObjectIdentifiers.id_ecPublicKey) ){
// the SECG SEC1 format for EC private key _in PKCS8 by OpenSSL_
// includes []] BITSTR(Q) (but not [0] params which is already in the PKCS8 algid)
org.bouncycastle.asn1.sec.ECPrivateKey eprv = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(p8i.parsePrivateKey());
byte[] eenc = new SubjectPublicKeyInfo (p8i.getPrivateKeyAlgorithm(), eprv.getPublicKey().getOctets()).getEncoded();
KeyFactory efact = KeyFactory.getInstance("EC");
pub = /*(ECPublicKey)*/ KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(eenc));
//}else if maybe others ...
}else throw new Exception ("unknown private key OID " + id);
}
System.out.println (pub.getAlgorithm() + " " + pub.getClass().getName());
}
In addition to the other answer, here's a way to convert Ed25519 private keys to public keys.
Edit: As #tytk notes in the comments, given a BCEdDSAPrivateKey, you can just call getPublicKey(). I am not entirely sure if it's possible to obtain a private EdDSAKey that wouldn't be BCEdDSAPrivateKey, using BC or otherwise, but just in case I'm leaving an alternative codepath that works.
private val bouncyCastleProvider = BouncyCastleProvider()
private val pkcs8pemKeyConverter = JcaPEMKeyConverter().setProvider(bouncyCastleProvider)
fun makeKeyPair(keyReader: Reader, passphrase: CharArray): KeyPair {
var obj = PEMParser(keyReader).readObject()
...
if (obj is PrivateKeyInfo) {
val privateKey = pkcs8pemKeyConverter.getPrivateKey(obj)
when (privateKey) {
is BCEdDSAPrivateKey -> return KeyPair(privateKey.publicKey, privateKey)
is EdDSAKey -> return KeyPair(genEd25519publicKey(privateKey, obj), privateKey)
}
...
}
...
}
private fun genEd25519publicKey(privateKey: EdDSAKey, privateKeyInfo: PrivateKeyInfo): PublicKey {
val privateKeyRaw = ASN1OctetString.getInstance(privateKeyInfo.parsePrivateKey()).octets
val privateKeyParameters = Ed25519PrivateKeyParameters(privateKeyRaw)
val publicKeyParameters = privateKeyParameters.generatePublicKey()
val spi = SubjectPublicKeyInfo(privateKeyInfo.privateKeyAlgorithm, publicKeyParameters.encoded)
val factory = KeyFactory.getInstance(privateKey.algorithm, bouncyCastleProvider)
return factory.generatePublic(X509EncodedKeySpec(spi.encoded))
}
Related
How do we translate the following java into C# for .Net Framework 4.8?
private static String getBearerToken(String publicKeyBase64, String apiKey)
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA");
byte[] encodedPublicKey = Base64.decodeBase64(publicKeyBase64);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
PublicKey pk = keyFactory.generatePublic(publicKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] encryptedApiKey = Base64.encodeBase64(cipher.doFinal(apiKey.getBytes("UTF-8")));
return new String(encryptedApiKey, "UTF-8");
}
I have tried fiddling with the following (with BouncyCastle package):
public static string GetBearerToken(string publicKeyBase64, string apiKey)
{
var key = Convert.FromBase64String(publicKeyBase64);
var info = SubjectPublicKeyInfo.GetInstance(key);
var pk = PublicKeyFactory.CreateKey(info);
var x = ???(pk);
using (var rsa = new RSACryptoServiceProvider(???))
{
var parameters = new RSAParameters()
{
Modulus = x.???,
Exponent = x.???
};
rsa.ImportParameters(parameters);
var data = Encoding.UTF8.GetBytes(apiKey);
var encryptedBytes = rsa.Encrypt(data, true);
return Convert.ToBase64String(encryptedBytes);
}
}
I also tried this, which runs but does not produce the expected result in the test case:
public static string GetBearerToken(string publicKeyBase64, string apiKey)
{
var keyBytes = Convert.FromBase64String(publicKeyBase64);
var info = SubjectPublicKeyInfo.GetInstance(keyBytes);
var keyParameter = PublicKeyFactory.CreateKey(info);
var encryptEngine = new RsaEngine();
encryptEngine.Init(true, keyParameter);
var dataToEncrypt = Encoding.UTF8.GetBytes(apiKey);
var encryptedBytes = encryptEngine.ProcessBlock(dataToEncrypt, 0, dataToEncrypt.Length);
return Convert.ToBase64String(encryptedBytes);
}
And here is the unit test I'm trying to get passing:
[TestMethod]
public void Utils_GetBearerToken()
{
var publicKey = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAszE+xAKVB9HRarr6/uHYYAX/RdD6KGVIGlHv98QKDIH26ldYJQ7zOuo9qEscO0M1psSPe/67AWYLEXh13fbtcSKGP6WFjT9OY6uV5ykw9508x1sW8UQ4ZhTRNrlNsKizE/glkBfcF2lwDXJGQennwgickWz7VN+AP/1c4DnMDfcl8iVIDlsbudFoXQh5aLCYl+XOMt/vls5a479PLMkPcZPOgMTCYTCE6ReX3KD2aGQ62uiu2T4mK+7Z6yvKvhPRF2fTKI+zOFWly//IYlyB+sde42cIU/588msUmgr3G9FYyN2vKPVy/MhIZpiFyVc3vuAAJ/mzue5p/G329wzgcz0ztyluMNAGUL9A4ZiFcKOebT6y6IgIMBeEkTwyhsxRHMFXlQRgTAufaO5hiR/usBMkoazJ6XrGJB8UadjH2m2+kdJIieI4FbjzCiDWKmuM58rllNWdBZK0XVHNsxmBy7yhYw3aAIhFS0fNEuSmKTfFpJFMBzIQYbdTgI28rZPAxVEDdRaypUqBMCq4OstCxgGvR3Dy1eJDjlkuiWK9Y9RGKF8HOI5a4ruHyLheddZxsUihziPF9jKTknsTZtF99eKTIjhV7qfTzxXq+8GGoCEABIyu26LZuL8X12bFqtwLAcjfjoB7HlRHtPszv6PJ0482ofWmeH0BE8om7VrSGxsCAwEAAQ==";
var apiKey = "aaaab09uz9f3asdcjyk7els777ihmwv8";
var expectedToken = "rfNjFso4uJbzhwl8E9vizqmHEuD7XDmPqfsRx1L62UoTmURGGLAGgJSl9lCPbgy03Q7NwozFYD4r9BFQY5QpvErHximBDU8HE25urVahm0HnB8VyCIobs684XGSN4GjdequePDrG6xUAxxpvmhqZRlGt1tUjUBeBg6kYqp4EnKHsiaBtvd0THGLZbefpT6UaShASQWYNiEPwEon5wtUMaDwnyQEazDu1H2ieN3r8cCVM3hsak59J/1MP07FQjdFbxdCLfA0DuxgpeKpvLs7WrA767WJSB1QZy7hcP1igSGRfd7Zrp6E7gIukdpC0DApqPKa4XsNTo2AMpG4AwiET2WeKvHn539gbwREXf79kZlYdFDCgTc0Zs7OfDx5ZXMCBKHOS/H3tVFJqXTfEfIF5LOzrFU5pPE0HeNBV0Q2vm8qRwQX0RijnvMOGpdcmXb0qoph4oy8Mj+vjRfFRboMAafttDozBhRmWEmeBB3EjYASm1fToQp5ey6ltCiEt8rjL5PlexxB0u3u2LVJQcDzMVNiiq10t1xyw8qtc6BMOyrKVlIANWglRYOKr9saVBVvDFUcCfsghMjUTDeAwHom4A3cSDWmVlNF9Vs/WqCoUzjQCV0BFPDzeAUbQqt7h7OgFno/+D9n5j1eMro0aXbbHNx71u8YmgPJhdixzFhxM1Pw=";
var token = Utils.GetBearerToken(publicKey, apiKey);
Assert.AreEqual(expectedToken, token, "GetBearerToken");
}
I have figured out a solution from a very similar question: .NET equivalent of Java KeyFactory.getInstance "RSA"/"RSA/ECB/PKCS1Padding"
I also pasted the original java code into jdoodle (https://www.jdoodle.com/online-java-compiler-ide/) to see what it actually does - encrypts with randomization therefore the result is different every time and cannot be unit tested as originally presumed (contrary to the API documentation page, therefore always confirm the assumptions first!)
The following produces randomized results but so far seems to be accepted by the API (uses BouncyCastle package):
public static string GetBearerToken(string publicKeyBase64, string apiKey)
{
var keyBytes = Convert.FromBase64String(publicKeyBase64);
var keyParameter = PublicKeyFactory.CreateKey(keyBytes);
var rsaKeyParameters = (RsaKeyParameters)keyParameter;
var rsaParameters = new RSAParameters
{
Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParameters);
var dataToEncrypt = Encoding.UTF8.GetBytes(apiKey);
var encryptedBytes = rsa.Encrypt(dataToEncrypt, false);
return Convert.ToBase64String(encryptedBytes);
}
}
this project in github is a example of this usage https://github.com/RomuloSantanaFadami/fadamipay-autorizador-exemplo-rsa
if you have a problem with a public key formating, try use StringBuilder to make her.
I use like this
internal partial class Criptografia{
private string ChavePublica = String.Empty;
public Criptografia()
{
//if (String.IsNullOrEmpty(PertoBus.Program.PublicKeyQrCode))
//{
StringBuilder chavePublica = new StringBuilder();
chavePublica.AppendLine("-----BEGIN RSA PUBLIC KEY-----");
chavePublica.AppendLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
chavePublica.AppendLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
chavePublica.AppendLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=");
chavePublica.AppendLine("-----END RSA PUBLIC KEY-----");
ChavePublica = chavePublica.ToString();
//}
//else
// ChavePublica = PertoBus.Program.PublicKeyQrCode;
}
public string RsaCriptografarComChavePublica(string pTexto)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(pTexto);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(ChavePublica.ToString()))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
}
I have one Perl Script that sign the payload with private key(id_rsa), generated in linux machine with SSH-Keygen or OpenSSL. After that I am taking the hash or the signed value, decoding it in base 64 and sending to my scala code. Here I send two things, the public key(id_rsa.pub) and the encoded signature.
Now when I verify the signature it always give me false result.
I have tried the opposite, like singing in Scala and verifying with Perl. It returned me false.
I generated the keys in Scala and put them in Linux and done the signing and verifying part from Perl. It worked. It even worked when I imported both keys in scala and try to do the signing and verifying part both from scala.
But whenever I am mixing up these two like signing in Perl and verifying in Scala or signing in Scala and verifying in Perl, it is giving me a false result.
My question is is there any common format that I can use here. The code I am using is -
val fileOpened = Source.fromFile("file.hash") // taking the hashed value
val payload = fileOpened.getLines.mkString //file contents as string
val decodedString = Base64.getDecoder.decode(payload.getBytes) // Base64 Decoding of the hashed value
println("decodedString, the hash value was base64 encoded, so decoded it and took into bytes")
println(decodedString) //the hash value was base64 encoded, so decoded it and took into bytes
val fileOpened1 = Source.fromFile("file")
val ComareElement = fileOpened1.getLines.mkString
val decodedString1 = new String(ComareElement).getBytes
println("decodedString1, this is the main payload to compare, turned into bytes")
println(decodedString1) // this is the main payload to compare, turned into bytes
//val keyPairGen = KeyPairGenerator.getInstance("RSA") //Creating KeyPair generator object
//keyPairGen.initialize(2048, new SecureRandom) //Initializing the key pair generator
//val pair: KeyPair = keyPairGen.generateKeyPair
// save public key as id_rsa.pub
//val x509keySpec = new X509EncodedKeySpec(pair.getPublic.getEncoded)
//val publicKeyStream = new FileOutputStream("C:\\MyWorkSpace\\PayLoadSign\\src\\main\\scala\\id_rsa.pub")
//publicKeyStream.write(x509keySpec.getEncoded)
// save private key as id_rsa
//val pkcs8KeySpec = new PKCS8EncodedKeySpec(pair.getPrivate.getEncoded)
//val privateKeyStream = new FileOutputStream("id_rsa")
//privateKeyStream.write(pkcs8KeySpec.getEncoded)
val filePublicKey = new File("id_rsa.pub")
var inputStream = new FileInputStream("id_rsa.pub")
val encodedPublicKey: Array[Byte] = new Array[Byte](filePublicKey.length.toInt)
inputStream.read(encodedPublicKey)
inputStream.close()
val filePrivateKey = new File("id_rsa")
inputStream = new FileInputStream("id_rsa")
val encodedPrivateKey: Array[Byte] = new Array[Byte](filePrivateKey.length.toInt)
println("The key is now " +encodedPrivateKey)
inputStream.read(encodedPrivateKey)
inputStream.close()
val keyFactory: KeyFactory = KeyFactory.getInstance("RSA")
// public key
val publicKeySpec: X509EncodedKeySpec = new X509EncodedKeySpec(encodedPublicKey)
val publicKey: PublicKey = keyFactory.generatePublic(publicKeySpec)
// private key
val privateKeySpec: PKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey)
val privateKey: PrivateKey = keyFactory.generatePrivate(privateKeySpec)
new KeyPair(publicKey, privateKey)
// val pair = keyPairGen.generateKeyPair //Generate the pair of keys
//val privKey = pair.getPrivate //Getting the privatekey from the key pair
//val pubKey = pair.getPublic //Getting the PublicKey from the key pair
val privKey = privateKey //Getting the privatekey from the key pair
val pubKey = publicKey //Getting the PublicKey from the key pair
println("Getting the privateKey from the key pair " + privateKey)
println("Getting the publicKey from the key pair " + publicKey)
var writer = new PrintWriter(new File("C:\\MyWorkSpace\\PayLoadSign\\src\\main\\scala\\Private_Key"))
writer.write(privKey.toString)
writer.close()
writer = new PrintWriter(new File("C:\\MyWorkSpace\\PayLoadSign\\src\\main\\scala\\Public_Key"))
writer.write(pubKey.toString)
writer.close()
val sign = Signature.getInstance("SHA256withRSA") //Creating a Signature object
//sign.initSign(privKey)
//val bytes = payload.getBytes //Initializing the signature
val bytes = decodedString
println(bytes)
//sign.update(bytes) //Adding data to the signature
//val signature = sign.sign //Calculating the signature
//val signedPayload = new BASE64Encoder().encode(signature)
//writer = new PrintWriter(new File("file.hash"))
//writer.write(signedPayload)
//writer.close()
println(bytes)
sign.initVerify(pubKey) //Initializing the signature
sign.update(bytes)
//println(signature)
//val bool = sign.verify(new BASE64Decoder().decodeBuffer(signedPayload)) //Verifying the signature
//println(sign.verify(signature))
val bool = sign.verify(bytes)
println(bool)
if (bool) System.out.println("Signature verified")
else System.out.println("Signature failed")
}
I have found a solution. I achieved that by correcting the signature algorithm and its default values in Perl. Also you have to take care of the new line characters (\n). The modified code for verification should look like this in Scala -
object Verify_Test extends App {
var privateKeyString = new String(Files.readAllBytes(Paths.get("private_key.pem")), Charset.defaultCharset)
privateKeyString = privateKeyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "")
var publicKeyString = new String(Files.readAllBytes(Paths.get("public_key.pem")), Charset.defaultCharset)
publicKeyString = publicKeyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "")
val keyFactory = KeyFactory.getInstance("RSA")
val encodedPrivateKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString))
val privateKey = keyFactory.generatePrivate(encodedPrivateKeySpec)
val encodedPublicKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString))
val publicKey = keyFactory.generatePublic(encodedPublicKeySpec)
var encodedSignatureVale = new String(Files.readAllBytes(Paths.get("hashedSignature")), Charset.defaultCharset)
encodedSignatureVale = encodedSignatureVale.replaceAll("\\n", "")
println(encodedSignatureVale)
var actualValue = new String(Files.readAllBytes(Paths.get("original data")), Charset.defaultCharset)
//actualValue = actualValue.replaceAll("\\n", "")
println(actualValue)
val signature = Signature.getInstance("SHA256withRSA")
//signature.initSign(privateKey)
//signature.update("Hello, World\n".getBytes("UTF-8"))
//val signatureValue = signature.sign
//val encodedSignatureVale = Base64.encodeBase64String(signatureValue)
//println(Base64.encodeBase64String(signatureValue))
signature.initVerify(publicKey)
// signature.update("Hello, World\n".getBytes("UTF-8"))
signature.update(actualValue.getBytes("UTF-8"))
val bool = signature.verify(Base64.decodeBase64(encodedSignatureVale))
println(bool)
if (bool) println("Signature verified")
else println("Signature failed")}
Remember I have taken both private and public key here. But we can use only public for verification.
The Perl code for signing will be -
use File::Slurp qw(read_file);
use File::Slurp qw(write_file);
use MIME::Base64 qw(encode_base64);
require Crypt::PK::RSA;
my $datatosign = read_file( 'payload.txt');
my $privatekey = Crypt::PK::RSA->new('private_key.pem');
my $signature = $privatekey->rsa_sign_message($datatosign, 'SHA256', 'v1.5');
my $hash = encode_base64($signature, '');
write_file('payload.hash', $hash);
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 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
I am using SoftHSM and am able to generate and store keys in a token. In order to use SunPKCS Interfaces many methods require the session handle and i am at a loss on how to retrieve them.
Currently i am using SoftHSM and PKCS11 as follows. THe code within the comments are what i tried to work with the SUNPKCS11 interface signatures.
Any example code on how to wrap and unwrap keys will also be much appreciated. I am attempting to backup keys using PKCS11 from one token to another and if my understanding is right, the approach must be via wrapping....
Sitaraman
public static void main(String[] args) {
// TODO code application logic hereString configName = "softhsm.cfg";
try {
// Set up the Sun PKCS 11 provider
String configName = "/etc/softhsm.cfg";
Provider p = new SunPKCS11(configName);
String configName1 = "/etc/softhsm1.cfg";
Provider p1 = new SunPKCS11(configName1);
if (-1 == Security.addProvider(p)) {
throw new RuntimeException("could not add security provider");
}
PKCS11 p11 = PKCS11.getInstance("/usr/local/lib/softhsm/libsofthsm.so", "C_GetFunctionList", null, false);
/* p11.C_GetSessionInfo(0);
CK_INFO cki = p11.C_GetInfo();
long[] slots = p11.C_GetSlotList(true);
String label = new String(p11.C_GetTokenInfo(slots[0]).label);
Object obj = new Object();
long sessionhandle = p11.C_OpenSession(slots[0], 1, null, null);
CK_MECHANISM ckm = new CK_MECHANISM();
ckm.mechanism = PKCS11Constants.CKM_RSA_PKCS;
CK_ATTRIBUTE[] cka = new CK_ATTRIBUTE[1];
CK_ATTRIBUTE[] cka1 = new CK_ATTRIBUTE[1];
long[] keypair =p11.C_GenerateKeyPair(slots[1], ckm, cka, cka1);
*/
//System.out.println("No. of slots" + slots.length + "label" + label);
// Load the key store
char[] pin = "vit12345".toCharArray();
char[] pin1 = "User12345".toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS11", p);
KeyStore ks1 = KeyStore.getInstance("PKCS11", p1);
ks.load(null, pin);
ks1.load(null, pin1);
Entry e;
KeyStore.PrivateKeyEntry e1;
// Generate the key
SecureRandom sr = new SecureRandom();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", p);
keyGen.initialize(1024, sr);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey pk = keyPair.getPrivate();
// Java API requires a certificate chain
X509Certificate[] chain = generateV3Certificate(keyPair);
ks.setKeyEntry("ALIAS-GOES-HERE", pk, "1234".toCharArray(), chain);
//ks1.setKeyEntry("ALIAS-GOES-HERE1", pk, "1234".toCharArray(), chain);
ks.store(null);
//ks1.store(null);
Key k = ks.getKey("ALIAS-GOES-HERE", "1234".toCharArray());
System.out.println("key string is " + k.toString());
PrivateKey pk1 = (PrivateKey) k;
System.out.println("OK");
} catch (Exception ex) {
ex.printStackTrace();
}
}
You can use approach from SunPKCS11 wrapper like you do in commented block in your sample code.
You can obtain a session handle by the following sample code:
CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS();
PKCS11 p11 = PKCS11.getInstance("D:\\cryptoki.dll", "C_GetFunctionList", initArgs, false);
long hSession = p11.C_OpenSession(0, CKF_SERIAL_SESSION| CKF_RW_SESSION, null, null);
char [] pin = {'1', '2', '3', '4', '5', '6', '7', '8'};
p11.C_Login(hSession, CKU_USER, pin);
// do work
p11.C_Logout(hSession);
p11.C_CloseSession(hSession);
For example to generate AES key and obtain a handle to generated key use the following (as an example):
CK_ATTRIBUTE[] aesKeyObject = new CK_ATTRIBUTE[13];
try
{
aesKeyObject[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
aesKeyObject[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_AES);
aesKeyObject[2] = new CK_ATTRIBUTE(CKA_VALUE_LEN, 32);
aesKeyObject[3] = new CK_ATTRIBUTE(CKA_TOKEN, true);
aesKeyObject[4] = new CK_ATTRIBUTE(CKA_LABEL, label);
aesKeyObject[5] = new CK_ATTRIBUTE(CKA_PRIVATE, true);
aesKeyObject[6] = new CK_ATTRIBUTE(CKA_EXTRACTABLE, false);
aesKeyObject[7] = new CK_ATTRIBUTE(CKA_WRAP, true);
aesKeyObject[8] = new CK_ATTRIBUTE(CKA_UNWRAP, true);
aesKeyObject[9] = new CK_ATTRIBUTE(CKA_ENCRYPT, true);
aesKeyObject[10] = new CK_ATTRIBUTE(CKA_DECRYPT, true);
aesKeyObject[11] = new CK_ATTRIBUTE(CKA_TRUSTED, true);
aesKeyObject[12] = new CK_ATTRIBUTE(CKA_ID, 1550);
CK_MECHANISM mech = new CK_MECHANISM(CKM_AES_KEY_GEN);
long newAESKeyHandle = p11.C_GenerateKey(hSession, mech, aesKeyObject);
}catch(Exception e)
{
}
Then you can work with this handle to use newly generated key.
Also the PKCS11 wrapper has methods for wrapping and unwrapping keys which you can use to backup:
public native byte[] C_WrapKey(long hSession,
CK_MECHANISM pMechanism,
long hWrappingKey,
long hKey) throws PKCS11Exception
public native long C_UnwrapKey(long hSession,
CK_MECHANISM pMechanism,
long hUnwrappingKey,
byte[] pWrappedKey,
CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception