I am trying to achieve ECIES encryption, for which below code is working.
X9ECParameters ecP = CustomNamedCurves.getByName("curve25519");
ECParameterSpec ecSpec = EC5Util.convertToSpec(ecP);
BigInteger d = new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990");
ECPrivateKeySpec priKeySpec = new ECPrivateKeySpec(
d, // d
ecSpec);
ECPoint Q = new FixedPointCombMultiplier().multiply(params.getG(), d.multiply(BigInteger.valueOf(-1)));
Q = Q.normalize();
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(
new ECPoint(Q.getAffineXCoord().toBigInteger(), Q.getAffineYCoord().toBigInteger()), // Q
ecSpec);
KeyFactory factTrial = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
BCECPrivateKey sKey = (BCECPrivateKey) factTrial.generatePrivate(priKeySpec);
PublicKey vKey = factTrial.generatePublic(pubKeySpec);
Cipher c = Cipher.getInstance("ECIESwithAES-CBC",BouncyCastleProvider.PROVIDER_NAME);
byte[] encodeBytes = c.doFinal(data.getBytes());
String encrypt = Base64.getEncoder().encodeToString(encodeBytes);
Cipher c2 = Cipher.getInstance("ECIESwithAES-CBC",BouncyCastleProvider.PROVIDER_NAME);
c2.init(Cipher.DECRYPT_MODE,sKey, c.getParameters());
byte[] decodeBytes = c2.doFinal(encodeBytes);
String deCrypt = new String(decodeBytes,"UTF-8");
Issue is the private key element 'd'. If I try to replace it with output of scrypt hash, private key fails to be converted in PrivateKey instance.
I have gone through net resources https://github.com/bcgit/bc-java/issues/251, https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation, https://crypto.stackexchange.com/questions/72134/raw-curve25519-public-key-points.
Above resources suggest the way Bouncy Castle for Curve25519 interprets private key is different from the ways some internet resource suggest. In post https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation there is mention as follows.
According to the curve25519 paper a x25519 public key can be represented in 32 bytes.
The x25519 library I'm using (bouncycastle) however gives me a 33 byte representation according to this standard.
I am very new to ECC, these resources are confusing me, the difference between lengths, the style of encoding big vs. little.
I have tried libSodium 'crypto_box_easy' and 'crypto_box_open_easy'
via its Java binding and it works all fine. The 32 byte scrypt output
is used by 'crypto_box_seed_keypair' to generate key pair which is
used for encryption process.
As I see some maths is involved here which I lack at present or I am failing to see the conversion.
I have to go this route Scrypt output -> key pair -> use for encryption
Using directly KeyGenerator from BC is working, but that utilises SecureRandom, but I need the output of Scrypt to behave as private key.
Questions:
I'll really appreciate someone helps me understand the difference between libSodium and Bouncy Castle approach. libSodium mentions it uses X25519. When I try to create X25519 key from 32 bytes, but BC Cipher(ECIESwithAES-CBC) then complaints it is not a EC Point, from this resource 'https://github.com/bcgit/bc-java/issues/251' it seems there are differences in that too (Curve25519 vs X25519).
The private key 'd', how to interpret it. I have seen these random values in Bouncy Castle documentation and test cases, is this simply a number in the prescribed range for valid keys? This number is treated (little vs. big endian) before creating BigInteger instance. I mean the raw value of 'd' in the my code example was converted from some other number?
The struggle between understanding different mechanism of Curve25519 and BC API itself, I am really confused.
Some pointers to further my research would be of great help.
Related
Use case:
I have a use case wherein client generates private and public key , sends the base 64 encoded public key to the server.
On server side I will encrypt a message using this public key and send the encrypted message to client , which the client decrypts using its private key.The algorithm agreed upon is 'RSA'.
The problem is on server side I am seeing that certain keys are working using X509EncodedKeySpec as key spec
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
While some keys throw exception (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence) using X509EncodedKeySpec but work using RSAPublicKeySpec:
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
So, what I came to understand is that client and server need to agree whether to use:
PKCS #1 or X.509 for encoding the key . My question is which one is better for my use case? Any guidelines when to use which format?
There's very little difference. The key format Java calls X.509, more exactly known as the ASN.1 structure SubjectPublicKeyInfo (or SPKI) defined in X.509 or equivalently and more conveniently in RFC5280 sec 4.1, is a quite simple way to handle a large and flexible set of algorithms: it consists of a substructure AlgorithmIdentifier which identifies the algorithm and its parameters if applicable, then an opaque BIT STRING which contains the actual key information (encoded) in a format depending on (the algorithm identified by) the AlgorithmIdentifier.
For RSA, the algorithm-dependent part is the ASN.1 structure RSAPublicKey defined in PKCS1 or more conveniently RFC8017 appendix A.1.1 and its earlier versions, and duplicated in RFC3279 sec 2.3.1. Thus for RSA the X.509 (SPKI) format contains the PKCS1 format, and since RSA doesn't have parameters (or at least key-related parameters), the only real difference is that the X.509 format explicitly specifies that the key is RSA -- which in your application you already know.
You have already discovered that vanilla (Oracle-was-Sun-now-OpenJDK) Java crypto, aka JCA Java Cryptographic Architecture, directly supports only the X.509 (SPKI) format, which is a minor advantage. However if you use BouncyCastle it is much easier to convert back and forth than the code in your Q; you simply use the org.bouncycastle.asn1.x509.SubjectPublicKeyInfo class to add or discard the AlgorithmIdentifier:
// test data source
KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spkienc));
// extract PKCS1 part of original SPKI
byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));
// rebuild SPKI from the PKCS1
AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spki2enc));
See my answer to the similar golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() and especially the links to:
Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
Generating RSA keys in PKCS#1 format in Java
Problem transmiting a RSA public key, javaME , bouncy castle
If you don't have BouncyCastle, it's a little harder; you need to write a partial ASN.1 parser or generator. Full ASN.1 processing is rather complicated, but for this case you need only a small subset that isn't too bad. (Yeah, that's faint praise.) I may add this later if I have more time.
A much bigger potential issue is that your key is not authenticated. The hard part of public key distribution, much harder than tiny format details, is making sure that only the legitimate key is distributed. If an attacker can substitute their publickey for the correct one, then the victim encrypts the supposedly secret data in a way the attacker can easily read, and all your fancy cryptography code is completely worthless.
This is why most actual systems don't distribute bare publickeys, but instead certificates that allow verifying the key is the correct key. There are a few certificate schemes, but the most widespread by far is X.509 and its Internet profile PKIX -- in fact the RFCs I referenced above, 5280 and 3279, are part of PKIX. SSL-now-TLS uses X.509. Code-signing uses X.509. S/MIME email uses X.509. (PGP/GPG uses a different kind of certificates, not X.509, but still certificates.) And (vanilla) Java directly supports X.509 certificates just as well or even better than it does "X.509" (SPKI) publickeys.
I am new to verification and certificates etc ..
I am facing an issue , that I need to sign a message on c# then verify the signature on java , the issue I ma facing that I am unable to load the public key on java on a (PublicKey) object using the Base64 string generated on c# , I used the following code to generate the private and public key on c# side
CspParameters cspParams = new CspParameters { ProviderType = 1 };
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
System.Diagnostics.Debug.WriteLine("pri:" + privateKey);
Console.WriteLine("Key added to container: \n {0}", rsaProvider.ToXmlString(true));
then I used the following code to create a public key on Java side :
X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
KeyFactory xx = KeyFactory .getInstance("RSA");
PublicKey ssx= xx.generatePublic(specc);
note that I copied the base64 public key string from the c# console .
When I try to run the code on java side the I get the following exception :
java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
I need to find a way to generate private and public key on c# (and generate a .cer file for the public key) to load it on java side , or find a way to load the base64 public key string into a (Publickey) object on java side . please help !
Option 1: Same data, different format.
The easiest way to transmit a public RSA key from .NET is to check that the public exponent value is { 01 00 01 } and then send the modulus value. On the receiver side you accept the modulus and assert the public exponent.
RSAParameters keyParams = rsa.ExportParameters(false);
if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
throw new InvalidOperationException();
Send(keyParams.Modulus);
Then Creating RSA keys from known parameters in Java says you can straightforwardly recover it on the Java side.
Option 2: Same format, different parser.
The next option you have is to keep using the CSP blob, but writing a parser in Java. The data is the result of calling CryptExportKey with PUBLICKEYBLOB, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.
In summary:
A header (which you could decide to skip, or just test it for equal to the fixed value(s) that you expect):
A byte, value 0x06 (PUBLICKEYBLOB)
A byte, value 0x02 (blob v2)
A short, value 0x0000 (reserved)
An integer (stored as little-endian) identifying the key as RSA (0x0000A400 or 0x00002400)
An integer (stored as little-endian) identifying the next segment as an RSA public key (a bit redundant, but technically a different structure now): 0x31415352
After all that comes the relevant data:
The bit-length of the modulus stored as a little-endian unsigned integer. For your 1024-bit example this will be 1024, aka 0x00000400, aka { 00 04 00 00 }(LE).
The public exponent, stored as a little-endian unsigned integer. This is almost always 0x00010001 (aka { 01 00 01 00 }), but since it's there you should respect it.
The next bitLen/8 bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array".
Option 3: Build a certificate
.NET Framework doesn't have this capability built-in (as of the current version, 4.7). You can P/Invoke to CertCreateSelfSignCertificate, but that would involve quite a lot of change (since RSACryptoServiceProvider won't let you get at the key handle, so you'll have to P/Invoke all of that, too).
You could "bit bang" out the DER-encoded certificate yourself. While fun, this is hard to get right, and probably not a viable path.
If you can move to .NET Core, the ability to create certificates has been added to .NET Core 2.0 via the CertificateRequest class. For a very simple certificate from your key:
var certReq = new CertificateRequest(
"CN=SubjectCN",
rsaProvider,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// add any extensions you want. I'm not adding any because I said "simple".
DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;
I have known public key and encrypted data. I want to decrypt it with public key. My code is look like:-
String s = "176byteofhexstring";
BigInteger Modulus = new BigInteger(s, 16);
String y = "03";
BigInteger Exponent = new BigInteger(y, 16);
RSAPublicKeySpec receiverPublicKeySpec = new RSAPublicKeySpec(Modulus, Exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey receiverPublicKey = (RSAPublicKey)
keyFactory.generatePublic(receiverPublicKeySpec);
Cipher rsaCipher = Cipher.getInstance("RSA/NONE/NoPadding","BC");
rsaCipher.init(Cipher.ENCRYPT_MODE, receiverPublicKey);
byte[] z = { 176 byte of cipher data };
byte[] m = rsaCipher.doFinal(z);
When I am run this code, getting error like:java.security.NoSuchProviderException: No such provider: BC.
Could anybody tell me how to avoid this error.
Add somewhere in the beginning of your code:
Security.addProvider(new BouncyCastleProvider());
This will register BouncyCastle provider to the JCA.
Another option is to use provider directly:
private static final Provider BC_PROVIDER = new BouncyCastleProvider();
...
Cipher rsaCipher = Cipher.getInstance("RSA/NONE/NoPadding", BC_PROVIDER);
Just use Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");. You don't need the Bouncy Castle provider to do textbook RSA. ECB here is a bit of a misnomer that is required for the provider of the standard JRE from Oracle; it's functionality the same as specifying NONE.
Note that using textbook RSA is completely insecure.
Completely missed it initially, but decryption with a public key is not the same thing as signature verification. Use the Signature class instead.
Here Want to share My doings for others.
Step1 - I was missing .Jar related to BouncyCastle (BC) , here the site help me to download the .jar file - http://www.itcsolutions.eu/2011/08/22/how-to-use-bouncy-castle-cryptographic-api-in-netbeans-or-eclipse-for-java-jse-projects/
step 2 - I download the jar from http://www.bouncycastle.org/latest_releases.html with name - bcprov-jdk15on-152.jar
step 3 - Add this jar to project, Properties -> Library -> Add Jar/folder
step 4 - add
import org.bouncycastle.jce.provider.BouncyCastleProvider;
step 5 - add line to your code
Security.addProvider(new BouncyCastleProvider());
and it solve my purpose...
Cipher.getInstance also accepts just the transformation - the provider is optional. When you don't specify the provider, it will use the default provider as specified in your java.security file.
I came across this while experiencing the same problem (only with the Signature.getInstance), and the answers already provided were very helpful in helping me realize this.
I'm working on some interoperable code for encrypting/decrypting strings between Java and node.js and have managed to get node.js to decrypt what Java has encrypted with this being the final part to successful decryption: the secret key.
To derive a secret key in Java, we write:
private static Key deriveSecretKey(String secretKeyAlgorithm, String secretKey, String salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), char2byte(salt), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), secretKeyAlgorithm);
return secret;
}
Notice the key length passed to PBEKeySpec() is 128 here. In node.js, however, I get an "Invalid key length" if I try to use 128 and actually have to use 16 here instead:
crypto.pbkdf2(key_value, salt_value, 65536, 16, function(err, key) {
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
// decipher.setAutoPadding(false);
var decoded = decipher.update(ciphertext, 'binary', 'utf8');
decoded += decipher.final('utf8');
console.log('Result: ' + decoded);
});
Console output:
Result: Super secret stuff -- right here.
Curious as to why the difference when specifying key lengths between these two functions. Thanks!
Normally, key sizes are defined in bits. However, most cryptographic libraries don't handle bit sizes that cannot be divided by 8 particularly well - the output is almost always in octets (8-bit bytes). So it is up to the designer of the API if the user has to specify the size in bits, or in the number of octets in the octet string (byte array).
The only way to really know why bits or bytes are being chosen is to ask the person who designed the library. In my own code, I do try to keep to (ad-hoc) standards - so bits for key sizes. If it's unclear from the context which is which, it is probably best to use names such as blockSizeBits or blockSizeBytes. Documentation may be of help too of course, but using specific identifiers is best in my opinion.
I recently asked a question about Oracle Encryption. Along the way to finding a solution for myself I decided to move the encryption (well, obfuscation) to the application side for certain tasks.
My problem is that the database is already encrypting data a certain way and I need Java code to duplicate that functionality, so that text encrypted by one system can be decrypted by the other and vice versa.
I want the encryption to be compatible with what the DB was already doing but couldn't find the documentation that describes exactly what Oracle is doing. How do I replicate this in Java?
dbms_obfuscation_toolkit.DESEncrypt(
input_string => v_string,
key_string => key_string,
encrypted_string => encrypted_string );
RETURN UTL_RAW.CAST_TO_RAW(encrypted_string);
No matter what I try, it seems as if the Java DES encryption is different than Oracle's.
I found this works:
KeySpec ks = new DESKeySpec(new byte[] {'s','e','c','r','e','t','!','!'});
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey sk = skf.generateSecret(ks);
Cipher c = Cipher.getInstance("DES/CBC/NoPadding");
IvParameterSpec ips = new IvParameterSpec(new byte[] {0,0,0,0,0,0,0,0});
c.init(Cipher.ENCRYPT, sk, ips);
// or
c.init(Cipher.DECRYPT, sk, ips);
The missing piece was the Initialization Vector (ips) which must be 8 zeros. When you use null in Java you get something different.
Using Java in the database would have been another approach that would (should!) have guarenteed that the code (and hence results) would be identical.