Nodejs Crypto ECDH PublicKey Hex as X.509 - java

I am using the prime256v1 curves for generating key pairs and sign using nodejs with the default crypto module.
Using crypto
let crypto = require('crypto');
let e = crypto.createECDH('prime256v1');
e.generateKeys();
privateKey = e.getPrivateKey();
privateKeyHex = privateKey.toString('hex');
publicKey = e.getPublicKey();
publicKeyHex = publicKey.toString('hex');
I obtain a publickey which looks like the hex string below:
'049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286'
Usign jsrsasign
let jsrsa = require('jsrsasign');
let KEYUTIL = jsrsa.KEYUTIL;
let kp = KEYUTIL.generateKeypair("EC", "prime256v1");
let pkHex = kp.pubKeyObj.pubKeyHex
which returns
'04f36e41189420db05dd8a73e3cb310b0c55809190bdedd89bf19769ac8df3cd06c1380f646e9e65e31c24affff79e43516b37e0186c3753cfdfd29894c2becc84'
Converting the PublicKey Hex to PublicKey object in Java
I want to use these publicKeys and convert it into a PublicKey object in java. Using the EC KeyFactory, I convert the hex to a byte[] and try to construct the PublicKey object in java which expects a X.509 format encoding.
public PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(publicKeySpec);
return pub;
}
To convert the hex string to a byte[] I use the following:
public byte[] hexStringToByteArray(String hexString) {
byte[] bytes = new byte[hexString.length() / 2];
for(int i = 0; i < hexString.length(); i += 2) {
String sub = hexString.substring(i, i + 2);
Integer intVal = Integer.parseInt(sub, 16);
bytes[i / 2] = intVal.byteValue();
String hex = "".format("0x%x", bytes[i / 2]);
}
return bytes;
}
Trying to do the same using the test case as follows results in an InvalidKeySpecException:
#Test
public void pkConversionTest() throws NoSuchAlgorithmException, InvalidKeySpecException {
ECDSA.setDebug(true);
byte[] pk = hexStringToByteArray("049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286");
PublicKey pub = ECDSA.getPublicKey(pk);
System.out.println(pub);
}
returns
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=26, too big.
I am however able to generate a KeyPair using java and use the publicKey hex obtained with nodejs to perform signature verify. A sample publickey hex generated from java looks as follows:
3059301306072a8648ce3d020106082a8648ce3d0301070342000425a321d5a1a74e6c04a6e3cab030401f3dbc04d5242f9bc629175c3d3988799175eb80cd96d7e76ea924630a8d86b93c54dec7cb965b58de31705eb3343846a1
How do I format the publicKey generated by nodejs in an X.509 format to be used on the java's side?
Edit:
3059301306072a8648ce3d020106082a8648ce3d030107034200 seems to be a common prefix for the publicKey hexes generated using java. By Prefixing this to the hex values of the PublicKey obtained using nodejs since the length is smaller seems to solve the problem. But can someone explain why?
Thank you.

But can someone explain why?
Java encodes public keys in "X.509" format or more exactly the SubjectPublicKeyInfo structure (SPKI) defined by X.509/PKIX; see rfc5280, rfc3279, and for ECC specifically rfc5480. That's why the data you pass to the key factory is in a class named X509EncodedKeySpec. This ASN.1 structure contains an AlgorithmIdentifier which identifies the algorithm used and its parameters (which for ECC is the curve/group used, in your case an OID identifying prime256 aka P-256 aka secp256r1) plus a BIT STRING type containing the actual encoded publickey value (which for ECC is the point in X9.62 format, which has several variants; here you are using uncompressed; according to the doc nodejs.crypto also supports compressed).
Your "prefix" is the DER encoding of the ASN.1 outer SEQUENCE, AlgorithmIdentifier, and tag length and padcount which begin the BIT STRING to contain the publickey point.
Basically dupe:
* How can I get a PublicKey object from EC public key bytes?
* Loading raw 64-byte long ECDSA public key in Java (Maarten's answer is effectively what you did)
* How can I generate a valid ECDSA EC key pair? (disclosure: mine)
FYI: Effectively the same issue also occurs for RSA and there are more Qs on that.
And there are also similar issues with privatekeys in generic PKCS8 format compared to algorithm-specific formats, but since publickeys are usually exchanged with other systems and/or programs while privatekeys usually aren't interoperability of privatekey encoding is less often a concern.

Related

create java PrivateKey and PublicKey from a String of file

Good day,
There is another third party that need my web application to send them some data in encrypt format. Thus they send me some guide to do so, however, I am not familiar with it, I am trying to google around but looks like I am google wrong way.
The guide is something as follow:
Run openssl command to generate a privatekey:
openssl ecparam -name prime256v1 -genkey -out myprivate.pem
After run this command, I output a priv.pem file, and I saw inside got some key end with '==', which is as follow:
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILefWfeuZOgnbDlxpwo3uQ2xQXfhXHUPTS+vKzvVZdCToAoGCCqGSM49
AwEHoUQDQgAE4MeQspGRJ1qdpweBfiaT5P84alZdga1f7mSpa5HqXTH58u0ZWJUQ
J7ToU/bUOPITh4FX07AV6wrgFCmwtUenDQ==
-----END EC PRIVATE KEY-----
Second one is run openssl command to generate the public key, and then send them:
openssl ec -in myprivate.pem -pubout -out mypublic.pem
Convert the private key to pkcs8 format:
openssl pkcs8 -topk8 -nocrypt -in myprivate.pem -out mypkcs8.pem
The third party will give me a public key in string format, then ask me to generate a secret key, and provide me some java code as follow:
first is to generate secret key and second one is encrypt:
public static SecretKey generateSharedSecret(PrivateKey privateKey,
PublicKey publicKey) {
try {
KeyAgreement keyAgreement = KeyAgreement.getInstance( "ECDH" );
keyAgreement.init( privateKey );
keyAgreement.doPhase( publicKey, true );
SecretKeySpec key = new SecretKeySpec(
keyAgreement.generateSecret( ), "AES" );
return key;
} catch ( Exception e ) {
// TODO Auto-generated catch block
e.printStackTrace( );
return null;
}
}
public static String encryptString(SecretKey key, String plainText) {
try {
String myIv = "Testing # IV!";
byte[] iv = myIv.getBytes( "UTF-8" );
IvParameterSpec ivSpec = new IvParameterSpec( iv );
Cipher cipher = Cipher.getInstance( "AES / CBC / PKCS5Padding" );
byte[] plainTextBytes = plainText.getBytes( "UTF-8" );
byte[] cipherText;
cipher.init( Cipher.ENCRYPT_MODE, key, ivSpec );
cipherText = new byte[cipher.getOutputSize( plainTextBytes.length )];
int encryptLength = cipher.update( plainTextBytes, 0,
plainTextBytes.length, cipherText, 0 );
encryptLength += cipher.doFinal( cipherText, encryptLength );
return bytesToHex( cipherText );
} catch ( Exception e ) {
e.printStackTrace( );
return null;
}
}
and also the bytes to hex string method:
public static String bytesToHex(byte[] byteArray) {
StringBuffer hexStringBuffer = new StringBuffer( );
for ( int i = 0; i < byteArray.length; i++ ) {
hexStringBuffer.append( String.format( "%02X", byteArray[ i ] ) );
}
return hexStringBuffer.toString( );
}
I have self gen a private key and also a public key by using openssl command, but the 4th step telling me that they will give me a public key as well, thus I am not understand, which public key should I use.
And also, how can I convert a String into java PrivateKey and PublicKey object?
* add on *
I try to convert the der file to java PublicKey object, it looks work. Before this, I convert the pem to der using openssl command:
openssl pkey -pubin -in ecpubkey.pem -outform der -out ecpubkey.der
Here is the java code:
File f = new File("/home/my/Desktop/key/ecpubkey.der");
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
KeyFactory fact = KeyFactory.getInstance("EC");
PublicKey theirpub = fact.generatePublic(new X509EncodedKeySpec(keyBytes));
However, I am hitting java.security.spec.InvalidKeySpecException: java.io.IOException: insufficient data when I try to convert der file to java PrivateKey object, the following is what I did:
openssl ecparam -name prime256v1 -genkey -out priv.pem
openssl pkcs8 -topk8 -nocrypt -in priv.pem -outform der -out priv.der
And the following is my java code:
File f2 = new File("/home/my/Desktop/key/priv.der");
FileInputStream fis2 = new FileInputStream(f2);
DataInputStream dis2 = new DataInputStream(fis2);
byte[] keyBytes2 = new byte[(int) f.length()];
dis2.readFully(keyBytes2);
dis2.close();
KeyFactory fact2 = KeyFactory.getInstance("EC");
PrivateKey pKey = fact2.generatePrivate( new PKCS8EncodedKeySpec(keyBytes2) ); // this line hit insufficient data
Diffie-Hellman is well-explained in wikipedia -- and probably some of the hundreds of Qs here, and crypto.SX and security.SX, about it, but I can't easily find which. In brief:
you generate a keypair, keep your privatekey, and provide your publickey to the other party
the other party does the same thing (or its reflection): generate a keypair, keep their privatekey, and provide their publickey to you
you use your privatekey and their publickey to compute the 'agreement' value
they similarly use their privatekey and your publickey to compute the same 'agreement' value. This is also called a shared secret, because you and the other party know it, but anyone eavesdropping on your traffic does not.
The 'provide' in that synopsis omits a lot of very important details. It is vital that when you provide your publickey to the other party they actually get your publickey and not a value altered or replaced by an adversary, and similarly when they provide their publickey to you it is vital you get the real one and not a modified or fake one. This is where actual DH systems mostly break down, and the fact you mention none of the protections or complications needed here suggests your scheme will be insecure and easily broken -- if used for anything worth stealing.
Note you should NEVER disclose or 'send' your privatekey to anyone, and they should similarly not disclose theirs. That's the main basis for public-key (or 'asymmetric') cryptography to be of any value or use at all.
There are numerous ways that keys can be represented, but only some are relevant to you.
Public keys are often represented either in
the ASN.1 structure SubjectPublicKeyInfo defined in X.509 and more conveniently in PKIX, primarily in rfc5280 #4.1 and #4.1.2.7 and rfc3279 2.3, encoded in DER, which has the limitation that many of the bytes used in this encoding are not valid characters and cannot be correctly displayed or otherwise manipulated and sometimes not transmitted or even stored; or
that same ASN.1 DER structure 'wrapped' in 'PEM' format, which converts the troublesome binary data to all displayable characters in an easily manipulable form. PEM format was originally created for a secure-email scheme call Privacy Enhanced Mail which has fallen by the wayside, replaced by other schemes and technologies, but the format it defined is still used. The publickey PEM format was recently re-standardized by rfc7468 #13 (which as you see referenced rfc5280).
OpenSSL supports both of these, but the commandline utility which you are using mostly defaults to PEM -- and since you need to convey your key to 'them', and they need to convey their key to you, PEM may well be the most reliable and/or convenient way of doing so. (Although other formats are possible, if you and they agree -- and if they require something else you'll have to agree for this scheme to work at all.)
Java directly supports only DER, thus assuming you receive their publickey in SPKI PEM, to use it in Java you need to convert it to DER. You can either do this in OpenSSL
openssl pkey -pubin -in theirpub.pem -outform der -out theirpub.der
and then read the DER into a Java crypto KeyFactory:
byte[] theirpubder = Files.readAllBytes(Paths.get(whatever));
KeyFactory fact = KeyFactory.getInstance("EC");
PublicKey theirpub = fact.generatePublic(new X509EncodedKeySpec(theirpubder));
// can downcast to ECPublicKey if you want to be more specific
Alternatively you can have Java convert the PEM which isn't too hard; there are several variations but I like:
String theirpubpem = new String(Files.readAllBytes(Paths.get(whatever)));
// IN GENERAL letting new String(byte[]) default the charset is dangerous, but PEM is OK
byte[] theirpubder = Base64.getMIMEDecoder().decode(theirpubpem.replaceAll("-----[^\\n]*\\n","") );
// continue as for DER
For private keys
there are significantly more representations, but only one (or two-ish) that Java shares with OpenSSL. Since you only need to store the private key locally and not 'send' it, PEM may not be needed; if so you can just add -outform der to your pkcs8 -topk8 -nocrypt command, adjusting the name appropriately, and read the result directly in a Java KeyFactory in the same fashion as above except with PKCS8EncodedKeySpec and generatePrivate and [EC]PrivateKey. If you do want to store it in (PKCS8-clear) PEM, you can also combine the above.
Using the DH agreement value directly as a symmetric cipher (e.g. AES) key is nonstandard and generally not considered good practice, although for ECDH with prime256v1 (aka secp256r1 or P-256) it is technically possible. AFAIK all good standards use a key-derivation step (aka Key Derivation Function or KDF) in between. Since you haven't shown us their 'guide' I can't say if this is correct -- for at least small values of correct.
To be sure you know, using CBC with a fixed IV more than once for the same key (which in this case is the same DH result) is insecure. I assume 'Testing' means you plan to replace it with something better.
Also FYI you don't need to use the full complication of the Cipher.init,update,doFinal API. When the data is small enough to fit in memory, as here, you can just do:
cipher.init(ENCRYPT_MODE, key, parms);
byte[] encrypted = cipher.doFinal (plainbytes);
// or since you want to hexify it
... bytesToHex (cipher.doFinal (plainbytes)) ...
Finally because Java byte is signed, your bytesToHex will output almost exactly half of all bytes with FFFFFF prefixed. This is very unusual, and phenomenally ugly, but again I don't know if it is 'correct' for you.
Base on dave_thompson_085 explanation and code, I manage to create my java PublicKey and Privatekey with following:
public static PublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
String publicKeyPEM = getKey(filename);
return getPublicKeyFromString(publicKeyPEM);
}
private static String getKey(String filename) throws IOException {
// Read key from file
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new FileReader(filename));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line + "\n";
}
br.close();
return strKeyPEM;
}
public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
BASE64Decoder b = new BASE64Decoder();
byte[] encoded = b.decodeBuffer(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
return pubKey;
}
and this is for private key
public static PrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
String privateKeyPEM = getKey(filename);
return getPrivateKeyFromString(privateKeyPEM);
}
public static PrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
String privateKeyPEM = key;
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
BASE64Decoder b = new BASE64Decoder();
byte[] encoded = b.decodeBuffer(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
PrivateKey privKey = (PrivateKey) kf.generatePrivate(keySpec);
return privKey;
}
Many thanks to #dave_thompson_085 explanation.

Implementing SHA1withRSA in Java

I'm trying to port a C++ program to Java, but not having much luck. The algorithm being used is RSASSA-PKCS1-v1_5. I know Java behaves differently from many other languages when it comes to cryptographic stuff.
This is known to be working:
bssl::UniquePtr<RSA> rsa(RSA_new());
rsa->n = BN_bin2bn(server_key, sizeof(server_key), nullptr);
rsa->e = BN_new();
BN_set_word(rsa->e, 65537);
std::uint8_t gs_hash[20];
SHA1(gs.data(), gs.size(), gs_hash);
if (1 != RSA_verify(NID_sha1, gs_hash, sizeof(gs_hash), gs_sig.data(), gs_sig.size(), rsa.get())) {
// failed
}
My current implementation:
Security.addProvider(new BouncyCastleProvider());
byte[] serverKey = new byte[] {...};
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(new RSAPublicKeySpec(new BigInteger(serverKey), BigInteger.valueOf(65537)));
MessageDigest digest = MessageDigest.getInstance("SHA1");
byte[] gs_hash = digest.digest(gs);
Signature sig = Signature.getInstance("SHA1withRSA/PSS", "BC");
sig.initVerify(publicKey);
sig.update(gs_hash);
if (sig.verify(gs_sig)) System.out.println("ALL GOOD");
else System.out.println("FAIL");
I've also tried using SHA1withRSA for the signature. gs and gs_sig are known and respectively 96 and 256 bytes long.
Test values:
serverKey: ace0460bffc230aff46bfec3bfbf863da191c6cc336c93a14fb3b01612acac6af180e7f614d9429dbe2e346643e362d2327a1a0d923baedd1402b18155056104d52c96a44c1ecc024ad4b20c001f17edc22fc43521c8f0cbaed2add72b0f9db3c5321a2afe59f35a0dac68f1fa621efb2c8d0cb7392d9247e3d7351a6dbd24c2ae255b88ffab73298a0bcccd0c58673189e8bd3480784a5fc96b899d956bfc86d74f33a6781796c9c32d0d32a5abcd0527e2f710a39613c42f99c027bfed049c3c275804b6b219f9c12f02e94863eca1b642a09d4825f8b39dd0e86af9484da1c2ba863042ea9db3086c190e48b39d66eb0006a25aeea11b13873cd719e655bd
gs: 4f2d7c6e76ccb6400ae1ff560d55a8084d98563ae03ac109d899fde735f6490935383cd1a97aa1fbff12e646f837194e9c6e57e1c5f956fcfde446a387c6be9a35c3225475f86df5a2c9b94626a2f90da3673af9861e33e8851a9a0ae20b9809
gs_signature: 35d1e685382f6d75bc5991f8ca1d252d70851c7bf66aa332bc6fc37bdb95da40c77c3cb452e6a70feda2bd6f63036d4b7a6d3f205789cf23a5777bcbd917a803ec2f3fb47fb6bdb911dd7d40dbe8425ad0d1905f1edb1bbc037ef4e259fa9c5dacdf9d58db8a18baf593d4e1c8055f51acffdaeeb10b4cee79f8b2421cfc28bdc9513859f76b101c965427334927207b575b9c29c581dfcc6fa4f135a1b04cfda4363e589f0510407faec4041b142b0ebbb9acd26dc2ed54fb28c38f560e05f8048c08c3574a4865f903192636987021eb72598ee822544026756ca294a150eb6642e65a860bac31fbba5ca0d800d030a52b515a7b8768def7de8e9f5dccdebe
gs_hash: 41aed19785bc5f4ffaa7eb30a29b1a39d52a225a
There are two things wrong:
You don't need to hash yourself when using "SHA1withRSA" (the signature will do the hashing for you, just feed it the data) and
if you add "/PSS" then you're using a different scheme than PKCS#1 v1.5.
BigInteger(byte[]) will create a negative value if used with the modulus. You need to use BigInteger(1, byte[]) to make it a positive value. However, both Bouncy as the JCE should throw an exception on negative exponents, so I don't see how you make your code run anyway.

Translating C# RSACryptoServiceProvider code to Java

I need to encrypt String for project related purpose and was given the below code for the same by vendor.
public static string EncryptString(string StringToEncrypt)
{
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
string xmlString = "<RSAKeyValue><Modulus>qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=</Modulus><Exponent>AQAB</Exponent><P>20PwC7nSsfrfA9pzwSOnRYdbhOYivFSuERxvXHvNjCll5XdmFYYp1d2evXcXbyj3E1k8azce1avQ9njH85NMNQ==</P><Q>x0G0lWcQ13NDhEcWbA7R2W5LPUmRqcjQXo8qFIaHk7LZ7ps9fAk/kOxaCR6hvfczgut1xSpXv6rnQ5IGvxaHYw==</Q><DP>lyybF2qSEvYVxvFZt8MeM/jkJ5gIQPLdZJzHRutwx39PastMjfCHbZW0OYsflBuZZjSzTHSfhNBGbXjO22gmNQ==</DP><DQ>NJVLYa4MTL83Tx4vdZ7HlFi99FOI5ESBcKLZWQdTmg+14XkIVcZfBxDIheWWi3pEFsWqk7ij5Ynlc/iCXUVFvw==</DQ><InverseQ>X5Aw9YSQLSfTSXEykTt7QZe6SUA0QwGph3mUae6A2SaSTmIZTcmSUsJwhL7PLNZKbMKSWXfWoemj0EVUpZbZ3Q==</InverseQ><D>jQL4lEUYCGNMUK6GEezIRgiB5vfFg8ql3DjsOcXxnOmBcEeD913kcYnLSBWEUFW55Xp0xW/RXOOHURgnNnRF3Ty5UR73jPN3/8QgMSxV8OXFo3+QvX+KHNHzf2cjKQDVObJTKxHsHKy+L2qjfULA4e+1cSDNn5zIln2ov51Ou3E=</D></RSAKeyValue>";
provider.FromXmlString(xmlString);
return Convert.ToBase64String(provider.Encrypt(Encoding.ASCII.GetBytes(StringToEncrypt), false));
}
However I need to modify or translate it to JAVA. I have wrote the below method for the same purpose.
public static String EncryptString(String strToBeEncrypted) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException
{
String modulusString = "qqoWhMwGrrEBRr92VYud3j+iIEm7652Fs20HvNckH3tRDJIL465TLy7Cil8VYxJre69zwny1aUAPYItybg5pSbSORmP+hMp6Jhs+mg3qRPvHfNIl23zynb4kAi4Mx/yEkGwsa6L946lZKY8f9UjDkLJY7yXevMML1LT+h/a0a38=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(modulusString);
byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = strToBeEncrypted.getBytes("US-ASCII");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.encodeBase64String(cipherData);
return encryptedStringBase64;
}
But the sample results do not match.
String is "4111111111111111" and encrypted result should be:
PfU31ai9dSwWX4Im19TlikfO9JetkJbUE+btuvpBuNHTnnfrt4XdM4PmGA19z8rF+lPUC/kcOEXciUSxFrAPyuRJHifIDqWFbbJvPhatbf269BXUiAW31UBX3X5bBOqNWjh4LDitYY0BtarlTU4xzOFyb7vLpLJe9aHGWhzs6q0=
But the result from Java code is
Cxp5AIzTHEkrU6YWwYo5yYvpED2qg9IC/0ct+tRgDZi9fJb8LAk+E1l9ljEt7MFQ2KB/exo4NYwijnBKYPeLStXyfVO1Bj6S76zMeKygAlCtDukq1UhJaJKaCXY94wi9Kel09VTmj+VByIYvAGUFqZGaK1CyLnd8QXMcdcWi3sA=
Every encryption algorithm needs to be randomized in order to provide semantic security. Otherwise, an attacker might notice that you've sent the same message again, just by observing ciphertexts. In symmetric ciphers, this property is achieved by a random IV. In RSA, this is achieved by a randomized padding (PKCS#1 v1.5 type 2 and PKCS#1 v2.x OAEP are randomized).
You can check whether the padding is randomized by running the encryption again with the same key and plaintext, and comparing the ciphertexts to previous ciphertexts. If the ciphertexts change in either C# or Java between executions, then you will not be able to tell whether the encryption is compatible, just by looking at the ciphertexts.
The proper way to check this, would be to encrypt something in one language and then decrypt in the other. For full compatibility, you should also try it the other way around.
Looking at your code, both seem equivalent, because false is passed as the second parameter into RSACryptoServiceProvider#Encrypt to use PKCS#1 v1.5 padding, and Cipher.getInstance("RSA/ECB/PKCS1PADDING") requests the same padding. The input/output encodings also seem equivalent. So, yes this code will be equivalent.
PKCS#1 v1.5 padding should not be used nowadays, because it is vulnerable against a Bleichenbacher attack (reference). You should use OAEP for encryption and PSS for signing, which are considered secure. C# and Java both support OAEP, but there may be differences in the default hash functions that are used (hash and MGF1).

How Do i compress or encode the Elliptic curve public key and put it over the network?

I am developing Distributed digital signature that signs a document and send it through network to the Application Server.I am using socket programming in java to do it. I think the public key should be encoded or compressed i.e the x and y values are somehow represented as a single binary data and saved in a public registry or network.But i don't know how to do it in java.
// I have class like this
public class CryptoSystem{
EllipticCurve ec = new EllipticCurve(new P192());
//-------------------
//--------------------
public ECKeyPair generatekeyPair()
{
return ECKeyPair(ec);
}
}
// i don't think i have problem in the above
CryptoSystem crypto = new CryptoSystem();
ECKeyPair keyPair = crypto.generateKeyPair();
BigInteger prvKey = keyPair.getPrivateKey();
ECPoint pubKey = keyPair.getPublicKey();
// recommend me here to compress and send it the public key to a shared network.
I want to know how to encode the public key and domain parameters, so that the verifier of the signature will decode it to use it.because when you send them over the network to the verifier u gonna have to encode the as a single byte array.i am no using Bouncy Castle Provider. The whole implementation of ECDSA algorithm is my project
Elliptic curve points are almost always encoded using the encoding specified in X9.62.
It is optional to use point compression. It is trivial to encode using point compression, but decoding a compressed point needs a bit more work, so unless you really need to save the extra bytes, I would not bother. Let me know if you need it, and I will add the details. You can recognize X9.62 encoded points with point compression by the first byte, which will be 0x02 or 0x03.
Encoding without point compression is really simple: start with a 0x04 (to indicate no compression). Then follow with first the x coordinate, then the y coordinate, both zero-padded on the left up to the size in bytes of the field:
int qLength = (q.bitLength()+7)/8;
byte[] xArr = toUnsignedByteArray(x);
byte[] yArr = toUnsignedByteArray(y);
byte[] res = new byte[1+2*qLength];
res[0] = 0x04;
System.arraycopy(xArr, 0, res, qLength - xArr.length, xArr.length);
System.arraycopy(yArr, 0, res, 2* qLength - yArr.length, nLength);
Decoding this is of course trivial.
I'm pretty sure that the BC implementation uses X9.63 encoding, so these would be rather standardized encodings. You will need to add the Bouncy Castle provider to your JRE (Security.addProvider(new BouncyCastleProvider()), see the bouncy documentation.
public static void showECKeyEncodings() {
try {
KeyPairGenerator kp = KeyPairGenerator.getInstance("ECDSA");
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable
.getParameterSpec("prime192v1");
kp.initialize(ecSpec);
KeyPair keyPair = kp.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
byte[] encodedPrivKey = privKey.getEncoded();
System.out.println(toHex(encodedPrivKey));
PublicKey pubKey = keyPair.getPublic();
byte[] encodedPubKey = pubKey.getEncoded();
System.out.println(toHex(encodedPubKey));
KeyFactory kf = KeyFactory.getInstance("ECDSA");
PublicKey pubKey2 = kf.generatePublic(new X509EncodedKeySpec(encodedPubKey));
if (Arrays.equals(pubKey2.getEncoded(), encodedPubKey)) {
System.out.println("That worked for the public key");
}
PrivateKey privKey2 = kf.generatePrivate(new PKCS8EncodedKeySpec(encodedPrivKey));
if (Arrays.equals(privKey2.getEncoded(), encodedPrivKey)) {
System.out.println("That worked for the private key");
}
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}

Decrypt Rijndael 256 (from PhP) encoded text in java with little information

I have some data from a external party which is encrypted according to them in: 'Rijndeal 256 with the private key'
Alongside these records there are a public and private key certificate which look like RSA certificates.
From what i've learned so far it seems the common way to use encryption with certifcates is to generate a 'secret key' or some kind in initialization vector and use this to encrypted text. So i'm thinking this is probably what they have done (the data was encrypted by a PHP application)
I'm trying to decrypt this text with javax.crypto.Cipher but i think i problably need more information on the specific encryption, but i dont really know what information to ask for, and think its likely the 'default options' will probably work. (Communication with the supplying party is difficult and slow).
i'm currently Using the following code to get the private key:
InputStreamReader ir = new InputStreamReader(the_inputstream_for_the_private_key_record);
Security.addProvider(new BouncyCastleProvider());
pemr = new PEMReader(ir);
Object o = pemr.readObject();
keyPair kp = (KeyPair) o;
return kp.getPrivate();
This seems to work as i get a instantiated PrivateKey object without errors the toString looks like:
RSA Private CRT Key
modulus: c98faa50ba69<trimmed>
public exponent: 10001
private exponent: bb889fbe5cb2a6763f...<trimmed>
primeP: eb73e85dc636f5751b...<trimmed>
primeQ: db269bd603a2b81fc9...<trimmed>
primeExponentP: 85b9f111c190595cc8...<trimmed>
primeExponentQ: a66d59a75bb77530de...<trimmed>
crtCoefficient: 79415b078c4c229746...<trimmed>
For each record i also have a entry like the following:
{
"decryptedLength":128389,
"symKeyLength":32,
"symKey":"SImE8VnSZaAu1Ve...<trimmed (this is always 685 chars long) >...ayaJcnpSeOqAGM7q="
}
Basically this is where i'm a bit stuck.
My guess would be that that 'symkey' value is encrypted with RSA which in turn when decrypted would yield the secretKey for the AES part, but if i try:
Cipher rsaCipher = Cipher.getInstance("RSA");
rsaCipher.init(Cipher.DECRYPT_MODE, key);
byte[] b = rsaCipher.doFinal('symkey'.getbytes());
this gets me "javax.crypto.IllegalBlockSizeException: Data must not be longer than 512 bytes", which seems logical since this string is 685characters long
I'm probably missing something very obvious here...
Any suggestions are appreciated.
Just guessing, but I think the value
"symKey":"SImE8VnSZaAu1Ve...<trimmed (this is always 685 chars long) >...ayaJcnpSeOqAGM7q="
is the base64 encoded output from RSA encryption using a 4096-bit public key. You need to first base64 decode the value into a byte[] array, then decrypt it with the private key, the result of which will be a 256-bit key. Note that "Rijndael 256" is ambiguous, since Rijndael supports both a 256 bit blocksize and also a 256 bit keysize.
with GregS's answer i finaly got this to work.
(adding an answer in case someone else needs to decrypt similar php encoded stuff).
The first part was to decrypt de symmetricKey ("symkey") from the metaData string
This was as Greg notes a Base64 encoded, RSA encrypted key which was decoded like so:
Cipher rsaCipher = Cipher.getInstance("RSA");
rsaCipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedRijndaelKey = Base64.decodeBase64(base64EncodedSymetricKey); //from the metaData
byte[] rijndaelKeyBytes = rsaCipher.doFinal(encryptedRijndaelKey);
This Rijndael key was then used to decrypt de actual encrypted data like so:
RijndaelEngine rijndaelEngine = new RijndaelEngine(256); // *1 *2
KeyParameter keyParam = new KeyParameter(rijndaelKeyBytes)
rijndaelEngine.init(false, keyParam); //false == decrypt
PaddedBufferedBlockCipher bbc = new PaddedBufferedBlockCipher(rijndaelEngine, new ZeroBytePadding()); // *3
byte[] decryptedBytes = new byte[decryptedLenght]; //from the storageOptions string
int processed = bbc.processBytes(inputBytes, 0, inputBytes.length, decryptedBytes, 0);
bbc.doFinal(decryptedBytes, processed);
*1 because the Sun JCA only supports common AES which has a 128bits keysize i had to use a different provider (BouncyCastle).
*2 apparently the blocksize was also 256 bits (trail & error)
*3 apparently there was no padding used, thus the ZeroPadding for padding (again trail & error).
The symKey value is Base64 encoded and must be decoded before you can do the private key decryption on it. Also, the symmetric encryption sounds like it is AES-256. (AES is based on the Rijndael cipher).

Categories