OpenSSL command equivalent using JAVA - java

So I have a very basic openssl command that was provided to me openssl smime -encrypt -binary -aes-256-cbc -in $inPath -out $encryptedPath -outform DER $pubCert, this command also works correctly and outputs an encrypted file. I need to use the equivalent of this command in a java application, preferably without invoking process and using openssl itself (only because I feel like that is probably bad practice).
I have researched quite a lot and there does not seem to be any equivalent out there that I can find.. I have tried several things and most of them do not seem to work. The weird thing is... I am able to get a simple "Hello World" string to encrypt using the code I wrote (although I don't believe it was encrypting it correctly because I had the cipher set to "RSA" not "AES") but when the byte array was coming from a file, it silently failed and just wrote 0 bytes. Right now this is what my code looks like.
Cipher aes = Cipher.getInstance("RSA");
CertificateFactory certF = CertificateFactory.getInstance("X.509");
File public_cert = new File( getClass().getClassLoader().getResource("public.crt").getFile());
FileInputStream certIS = new FileInputStream(public_cert);
X509Certificate cert = (X509Certificate) certF.generateCertificate(certIS);
certIS.close();
aes.init(Cipher.ENCRYPT_MODE, cert);
File tarGz = new File("C:\\volatile\\generic.tar.gz");
FileInputStream fis = new FileInputStream(tarGz);
byte[] tarGzBytes = FileUtils.readFileToByteArray(tarGz);
tarGzBytes = "Hello World".getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream("Hello World".getBytes());
File encFile = new File("C:\\volatile\\generic.tar.gz.enc");
FileOutputStream enc = new FileOutputStream(encFile);
CipherOutputStream cos = new CipherOutputStream(enc, aes);
cos.write(tarGzBytes);
//IOUtils.copy(fis, cos);
//IOUtils.copy(bais, cos);
cos.flush();
cos.close();
So this works, and encrypts a little file with Hello World encrypted in it. I don't believe this is AES-256-CBC though, and it does not work when I use the FileUtils.readFileToByteArray(tarGz), although the resulting byte array in a debugger is correctly sized at about 94MB. Which seems really odd to me, that it works with "Hello World".toByteArray() and not FileUtils.readAllBytes(tarGz). Also as a side note, the ByteArrayInputStream using IOUtils.copy works, whereas the FileInputStream version writes 0 bytes as well.
Also, when I set the cipher mode to AES/CBC/PKCS5Padding (because I found something online suggesting to set it to that and it looks more like what I want) I get the following error message:
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
at javax.crypto.Cipher.chooseProvider(Cipher.java:892)
at javax.crypto.Cipher.init(Cipher.java:1724)
~~~~
If anyone has any suggestions, or if I need to provide more information please let me know. I am fairly stuck right now and I am at this point debating writing a script to simply run the openssl command and run that script from java...
Conclusion
After reading through #dave-thompson-085's answer I realized that there was a really good reason why I could not find what I was wanting to do. So therefore I decided to go ahead and just call the openssl process from java using a process builder. I was able to recreate the openssl command from above as a Process in java, start it and run it with the following code:
File cert = new File(getClass().getClassLoader().getResource("public.crt").getFile());
ProcessBuilder openSslBuilder = new ProcessBuilder("openssl", "smime", "-encrypt", "-binary",
"-aes-256-cbc", "-in", "C:\\volatile\\generic.tar.gz", "-out",
"C:\\volatile\\generic.tar.gz.enc", "-outform", "DER", cert.getPath());
Process openssl = openSslBuilder.start();
openssl.waitFor();
System.out.println(openssl.exitValue());
openssl.destroy();
Hopefully this helps someone else who is looking to attempt this as well and maybe save someone a bunch of time!

First, to be clear: the openssl smime command actually handles both S/MIME and CMS (aka PKCS7) formats; these are related but different standards that basically use different file formats for essentially the same cryptographic operations. With -outform DER you are actually doing CMS/PKCS7.
Second and more fundamental: CMS/PKCS7, and S/MIME, and most other common cryptographic schemes like PGP, actually does hybrid encryption. Your data is not actually encrypted with RSA; instead your data is encrypted with a symmetric algorithm (here AES-256-CBC, since you selected that) using a randomly generated key called the DEK (data encryption key) and the DEK is encrypted with RSA using the recipient's publickey (obtained from their certificate), and both of those results plus a good deal of metadata is arranged into a fairly complicated data structure. The recipient can parse the message to extract these pieces, then use RSA with their privatekey to decrypt the DEK, then AES-decrypt the data with the DEK. Note you always use RSA keys for RSA, and AES keys for AES; symmetric keys are pretty much all just bits and only vary in size, but public-key cryptographic keys including RSA (also DH, DSA, ECC and more) are much more complicated and cannot be intermixed.
Trying to encrypt data directly with RSA as you did, in addition to being wrong, won't work in general because RSA can only encrypt limited amounts of data, depending on the key size used, typically about 100-200 bytes. Symmetric encryption also has some limits, but they are generally much larger; AES-CBC is good for about 250,000,000,000,000,000 bytes.
If you want to implement this yourself, you need to read the standard for CMS particularly the section on EnvelopedData using KeyTransRecipientInfo (for RSA), combined with the rules for ASN.1 BER/DER encoding. This is not a simple job, although it can be done if you want to put the effort in.
If you can use a third-party library in Java, the 'bcpkix' jar from https://www.bouncycastle.org has routines that support CMS, among several other things. This is usually easy if you are writing a program to run yourself, or in your department. If this is to be delivered to outside users or customers who may not like having to manage a dependency, maybe not.
That said, running another program to do something isn't necessarily bad practice in my book, and can be done directly from java (no script). Unless you (need to) do it very often, such as 100 times a second.

Related

RSA decryption according to the private key reports an error

My code using RSA on the front end:
const rsa = new JSEncrypt();
rsa.setPublicKey(k);
const resultText = rsa.encrypt("violet");
console.log(resultText);
My code using RSA in the backend:
byte[] inputByte = org.apache.commons.codec.binary.Base64.decodeBase64(str.getBytes("UTF-8"));
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new
PKCS8EncodedKeySpec(decoded));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,priKey);
String outStr=new String(cipher.doFinal(inputByte));
return outStr;
PublicKey like this:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA13gYdCmOjR9yqQD7ldzG
ZXabSon6SiLceCK6vRXf4NMbF+EQke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVT
iaGIhWDlPu3mecri8rYtmOSfd8GCE0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8W
Hzdas96CLiESbjSAruRasQXP2OLqEA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFa
lxYltFadVpz3+kBplvpzuj82t4fc3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y
9uN0G4gxhcDFJ4L41uXOln5CPapk7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nH
GQIDAQAB
-----END PUBLIC KEY-----
PrivateKey like this:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA13gYdCmOjR9yqQD7ldzGZXabSon6SiLceCK6vRXf4NMbF+EQ
ke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVTiaGIhWDlPu3mecri8rYtmOSfd8GC
E0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8WHzdas96CLiESbjSAruRasQXP2OLq
EA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFalxYltFadVpz3+kBplvpzuj82t4fc
3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y9uN0G4gxhcDFJ4L41uXOln5CPapk
7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nHGQIDAQABAoIBAAyqFmXde294BblB
QYhRbafRDNeYvIlW+zZkdC1g98OzJMiGhf7NvhWcSFud3CWFrMeNcyXSe+s+iRgy
Y/SmPP6969RLGa5VNVK7RhOV+aAe7/COAyM3NNmGDehlJIaz8FXbqggWcKaUWIMn
K+WuHdK/4ijoTyZ+8yJfG6Er8tisryLQ9io9+op9g/ZWzaUKgu934/cDxUt70bfm
x+ZEPi6YfkJ1uOpXnnadDyw2RUDcvCM3fK3KF5fqM7SJAXY9b1pmLr+Ccn1qkT9G
I+QHidEsGfJciX5AoHnlIMLPMVIPKBbq4GwC/Ngb41LprNJWlPR38N2ySjky/Jyt
159XWHECgYEA9lx2KfFmyLyVjnkIF3JI50mSZAw4YPBBqB27UInacvxXbjfVURht
xK60GB9OkDbFdeNh89x86Gfwvm5bTq4W8YSH4Obd5Fg8XjTuaicTi03CSfF5SdJn
JLLOUmlqP75gkbEPNUoOfqhqq6IbyJVB3egyL90cd2/wCdJOVLEUly8CgYEA3+Y4
lNdl0xedkDNkWsCyyA4iPSUzVxsuch9hW/VGBwzga8rtiNllpKifVvctQOEu/KUe
vVQRF78ojZaMGT33l6TivnUL54z9Lo9uWghoG8TqMfnG34pFPe3R+zvGP87Hrozw
1EUhiMT198SlB/YHrgGGGlJbG+rlm5GIx3lEdDcCgYA4RSw0LlA0v2ZFkX14pYDj
WxmVwnjKI3ZLqObU4XfE1cA+i4AssrC3wNOfwt7V77ywTYxc/9qD1uHVDS3LzdWt
uoCyrOi3tDOtrNdb5asAIXWkIAR9CRLH/hNEHZHIF3rFLDT2DgE7iso6g59m9DiE
L/nulsleunGQPLnpfDzgvwKBgDRV5Q3tl3CTSZJGYQPRnTikDR7LzkdjJCUq7qAH
IhpNyTuJEKL3ZgnqHGzAlERhHpGRqzDIMMKjPUBzW0YfNPuuYA3y4Bh83UV/42SK
KIOtMK0D3JeuA2dparbWDw4lMIm9iiGkEyWcHH6Q6Z6FxN/InWcTrxZEfu0xRI6T
6wtbAoGAfl5dW9LNoaNfQbgIq+PJKZm9f1frza55mFTJgo3ravdb3GmzWVHN8xRf
nLKyKyNLqbkT35IGc39NkALJLxT5RibkAZLiUiwqdMF63sgODbA9AGTmhe+JHS+V
hBmFnCyp6UiN9E4ZAWcZQILa0rRMftMFngAJ3El0ZP+HziRnNzs=
-----END RSA PRIVATE KEY-----
Bu-t, when i do the java code decryption, it reported such an error:
java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.
How can i solve this problem ?
1. You are decoding wrong. PEM format has a dash-BEGIN line identifying the type of data, a block of base64 encoding the data, and a dash-END line. The BEGIN and END lines are part of the format, but they do not contain base64-encoded data; only the lines in between contain the base64-encoded data. You are apparently passing the whole thing, including the BEGIN and END lines, to commons.codec.Base64, which results in decoding a bunch of garbage before and after the actual data. That garbage isn't valid ASN.1 DER, so when Java tries to parse it as DER it fails.
2. Plus your data is not a PKCS8-clear privatekey. The PEM type 'RSA PRIVATE KEY' is an OpenSSL-defined format that contains a 'traditional' or 'legacy' format, namely the PKCS1 representation of the private key. This is not PKCS8, which is the only key format Java supports natively; that's why the spec class is named PKCS8EncodedKeySpec, because it is a key spec encoded as PKCS8 and more specifically PKCS8-clear. If you fix the above problem by removing the BEGIN and END lines before base64-decoding, Java can parse the result as DER, but not as a PKCS8-clear key; you get a different exception about 'algid parse error, not a sequence'. To fix this there are 5 approaches:
change whatever process you use to initially generate the keypair so it generates PKCS8, not OpenSSL-legacy PKCS1. Especially since you need anyway to replace the keypair you compromised by publishing it, as 207421 said. You give no clue what that process is or was, so I can't give any details.
convert your generated privatekey, or a copy, to PKCS8-clear. This is not programming or development and offtopic, but if you have or get OpenSSL (on the same or any accessible and secure system), you can do
openssl pkey -in oldfile -out newfile # 1.0.0 up only, but older is now rare
# or
openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile # even ancient versions
Once you have a PKCS8-clear file, just remove the BEGIN and END lines and base64-decode what is left, and pass that to KeyFactory as PKCS8EncodedKeySpec as you already do.
use https://www.bouncycastle.org . The 'bcpkix' jar has (Java) code to read a large range of OpenSSL-supported PEM formats, including the RSA-PKCS1 private key format you have. There are lots of existing Qs about this; just search for PEMParser and JcaPEMKeyConverter.
convert it yourself. Decode the body of the file you have, after removing the BEGIN and END lines, to get the PKCS1 key, then build the PKCS8 format for that key, and then pass it to KeyFactory as PKCS8EncodedKeySpec. See answers by Noa Resare and Jean-Alexis Aufauvre on Getting RSA private key from PEM BASE64 Encoded private key file or mine in Java: Convert DKIM private key from RSA to DER for JavaMail .
do it entirely yourself. Decode the file you have without BEGIN/END to get PCKS1, parse that as DER following e.g. RFC8447, and build RSAPrivateCrtKeySpec. Some other As on the Q I linked just above do this. However, this requires either: using undocumented internal sun.* classes, which used to work in Java (hence the existing answers) but which 'modular' Java versions (9 up) since 2017 have steadily made more difficult or impossible; using BouncyCastle which has documented (and good) support for ASN.1 -- but then it's easier to use bcpkix for the whole job as above; or writing your own ASN.1 parsing, which is a good deal of work.
PS: encrypting text with RSA is usually a bad design; it's not suited for that. But that's not really a programming issue and doesn't belong here.

Encypting with php openssl and decrypting with Java

I am trying to encrypt a string with php open_ssl and then decrypt it with Java. I thought I kind of understood what was going on, but apparently not.
At first I was unable to get the algorithms to match up. From what I can gather, openssl_private_encrypt() is using RSA and although the documentation is about PKCS1_PADDING, from what I read it seems that it was changed to use PKCS5/7 to become more secure. And I cannot get any Java cipher with RSA/NONE/PKCS5 or PKCS7.
But I thought I was having success by using NoPadding, and filling the block myself. This is with an existing 512 bit key that I converted from DER to PEM with openssl. I had a test string of
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
I was able to read in the private key in php and encrypt the text with
$fp=fopen("/folder/private_key.pem","r");
$privkey_res=fread($fp,1024);
$privkey = openssl_pkey_get_private($privkey_res);
$padding = OPENSSL_NO_PADDING;
openssl_private_encrypt($texttocrypt, $encryptedtext, $privkey, $padding);
file_put_contents("/folder/encrypted.txt", $encryptedtext );
Then back in Java I then was able to correctly decrypt that string using
Cipher cipherb = Cipher.getInstance("RSA/NONE/NoPadding");
cipherb.init(Cipher.DECRYPT_MODE, publicKey);
decrypted = cipherb.doFinal(text.getBytes());
So I thought I could get things working to be useful. However, then I changed the test string slightly, like the last 'f' to 'g'
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeg
And then I got complete garbage on the decryption. Although no error. And if I just changed the first character, it still decrypted correctly.
At this point I am not even sure what it is that I don't understand. But is there a way to do my original goal? Encrypt with php open_ssl and decrypt with Java.
Thanks

perl CBC DES equivalent in java

We are migrating some code from perl to java/scala and we hit a roadblock.
We're trying to figure out how to do this in Java/scala:
use Crypt::CBC;
$aesKey = "some key"
$cipher = new Crypt::CBC($aesKey, "DES");
$encrypted = $cipher->encrypt("hello world");
print $encrypted // prints: Salted__�,%�8XL�/1�&�n;����쀍c
$decrypted = $cipher->decrypt($encrypted);
print $decrypted // prints: hello world
I tried a few things in scala but didn't really get it right, for example something like this:
val secretKey = new SecretKeySpec("some key".getBytes("UTF-8"), "DES")
val encipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
encipher.init(Cipher.ENCRYPT_MODE, secretKey)
val encrypted = encipher.doFinal(bytes)
println("BYTES:" + bytes)
println("ENCRYPTED!!!!!!: " + encrypted)
println(toString(encrypted))
Any help or direction in Java/scala would very much be appreciated
Assuming that Crypt module is the one I find at https://metacpan.org/pod/Crypt::CBC it is documented as by default doing (the same as) openssl, apparently meaning commandline 'enc' (openssl library has MANY other options). That is not encryption
with the specified key (and IV) directly, but instead 'password-based' encryption (PBE) with a key and IV derived from the specified 'key' (really passphrase) plus (transmitted) salt, using a twist on the original (now unrecommended) PKCS#5 v1.5 algorithm, retronymed PBKDF1. See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html (or the man page on a Unix system with openssl installed) and rfc2898 (or the original RSA Labs PKCS documents now somewhere at EMC).
You say you cannot change the perl sender. I hope the users/owners/whoever realize that original DES,
retronymed single-DES for clarity, has been practically brute-forceable for well over a decade, and
PBE-1DES may be even weaker; the openssl twist doesn't iterate as PKCS#5 (both KDF1 and KDF2) should.
Java (with the Suncle providers) does implement PBEWithMD5AndDES, which initted with PBEParameterSpec (salt, 1)
does successfully decrypt data from 'openssl enc -des-cbc', and thus I expect also your perl sender (not tested).
FWIW if you could change to triple-DES, Java implements PBEWithMD5AndTripleDES using an apparently nonstandard
extension of PBKDF1 (beyond hash size) that is quite unlike openssl's nonstandard extension, and thus incompatible if the perl module is in fact following openssl.
You would have to do the key-derivation yourself and then direct 3DES-CBC-pad, which isn't very hard.
Also note encrypted data from any modern computer algorithm is binary. "Printing" it as if it were text
in perl, or Java or nearly anything else, is likely to cause data corruption if you try to use it again.
If you are only looking to see 'is there any output at all, and is it visibly not the plaintext' you're okay.

Capicom and SHA1 - Help translating a java code to Delphi

I have a java application that signs a string using a certificate. It works encrypting the string it with SHA1. I am trying to translate the code to Delphi 2010, but I have no idea how to get it working the same way the java app does (using sha1). So far, I have found this:
Delphi 7 access Windows X509 Certificate Store
It does work, but it does not use sha1 and I get different results when I run the java app.
Java code
char[] pass = (char[]) null;
PrivateKey key = (PrivateKey) getKeyStore().getKey(alias, pass);
Certificate[] chain = getKeyStore().getCertificateChain(alias);
CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC");
X509Certificate cert = (X509Certificate) chain[0];
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);
gen.addCertificatesAndCRLs(certsAndCRLs);
CMSProcessable data = new CMSProcessableByteArray(conteudoParaAssinar);
CMSSignedData signed = gen.generate(data, true, "SunMSCAPI");
byte[] envHex = signed.getEncoded();
CertInfo certInfo = new CertInfo();
certInfo.Hash = new BigInteger(envHex).toString(16);
return certInfo;
Delphi Code
var
lSigner: TSigner;
lSignedData: TSignedData;
fs: TFileStream;
qt: integer;
ch: PChar;
msg : WideString;
content : string;
cert: TCertificate;
begin
cert := Self.GetCert;
content := 'test';
lSigner := TSigner.Create(self);
lSigner.Certificate := cert.DefaultInterface;
lSignedData := TSignedData.Create(self);
lSignedData.content := content;
msg := lSignedData.Sign(lSigner.DefaultInterface, false, CAPICOM_ENCODE_BASE64);
lSignedData.Free;
lSigner.Free;
EDIT
Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex? Is this the right order and the same thing the java code does? I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.
We use DCPCrypt in some delphi apps that interface with our Java Tomcat App and are able to get SHA-256 compatible hashes. I suspect SHA1 is also easy.
Here's an example
function Sha256FileStreamHash(fs : TFileStream): String;
var
Hash: TDCP_sha256;
Digest: array[0..31] of byte; // RipeMD-160 produces a 160bit digest (20bytes)
i: integer;
s: string;
begin
if fs <> nil then
begin
fs.Seek(0, soFromBeginning);
Hash:= TDCP_sha256.Create(nil); // create the hash
try
Hash.Init; // initialize it
Hash.UpdateStream(fs,fs.Size); // hash the stream contents
Hash.Final(Digest); // produce the digest
s:= '';
for i:= 0 to 31 do
s:= s + IntToHex(Digest[i],2);
Result:= s; // display the digest
finally
Hash.Free;
end;
end;
end;
First, what makes you think you're not using SHA-1 ? I'm asking because CAPICOM's sign function only works with SHA-1 signature.
Second, how do you know that you're getting a different result ? Have you tried to validate the answer ? If yes, using what ?
Third, there is something that you MUST know about CAPICOM: the "content" property is a widestring. This has various implication, including the fact that all content will be padded to 16-bits. If your input data is of different size, you'll get a different result.
Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex?
No. You get an interface to an instance of a ICertificate object (or, more likely, ICertificate2) and you just use that directly. If you have the B64 encoded version of the certificate, you can create a new ICertificate instance and then call the ICertificate.Import method. The hash of the certificate itself is only used by the signing authority to sign that specific cert.
The hash algorythm is actually used during the data signature process: the library reads the data, creates a hash of that data (using SHA-1 in case of CAPICOM) and then digitally sign that hash value. This reduction is necessary because signing the whole data block would be far too slow and because, that way, you only have to carry the hash if you're using a hardware crypto system.
Is this the right order and the same thing the java code does?
Yes and no. The Java code does all the necessary steps in explicit details, something you don't have (and actually cannot) do with CAPICOM. It should result in compatible result, though.
It also has an additional step not related to the signature itself: I'm not sure what it does because it seems to create a dummy certificate information data and store the SHA-1 hash value of the signed CMS message and return the resulting instance. I suppose that it's a way the Java dev has found to pass the hash value back to the caller.
I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.
The HashedData class is used to (surprise) hash data. It has the same limitation as Signeddata i.e. it only works on widestrings so compatibility with other frameworks is dodgy at best.
Final note: Windows offers access to much more comprehensive cryptographic functions through the CAPI group of functions. CAPICOM is only an interface to that library that is used (mostly) in script language (JavaScript on web pages, VB, etc). You should do yourself a favor and try using it instead of CAPICOM because there is a good chance you'll encounter something that you simply cannot do properly using CAPICOM. At that stage, you will have to rewrite part for all of your application using CAPI (or another library). So save time now and skip CAPICOM if you don't have a requirement to use it.

Java encryption alternitive to hardcoded key

I am new to encryption.
I have looked at the javax.crypto documentation and got encryption of a file to work using this code ...
File saveFile = new File("Settings.set");
saveFile.delete();
FileOutputStream fout = new FileOutputStream(saveFile);
//Encrypt the settings
//Generate a key
byte key[] = "My Encryption Key98".getBytes();
DESKeySpec desKeySpec = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey skey = keyFactory.generateSecret(desKeySpec);
//Prepare the encrypter
Cipher ecipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, skey);
// Seal (encrypt) the object
SealedObject so = new SealedObject(this, ecipher);
ObjectOutputStream o = new ObjectOutputStream(fout);
o.writeObject(so);
o.close();
However if you were a clever hacker ( or maybe even amateur since I figured this out), all you would have to do is open the class file that contains this code, and the encryption key (My Encryption Key98) is plainly visible.
How do you encrypt the encryption key? ...LOL... Can you?
Thanks for your help!
If the attacker has access to both the software and the file, it could decrypt it. There are some ways to solve this:
Use asymetric keys. Encrypt the file with the public key, and it can only be decrypted with a private key. This assumes that the software does not need to decrypt the file.
Use Diffie-Hellman exchange. If you want to send an encrypted piece of data over the network, both parties can establish a key without an attacker knowing about it.
If the program needs to both encrypt and decrypt the data, there is nothing you can do. The attacker can simply run the program and look at the decrypted information.
An attacker can always do everything the program can do and usually quite a bit more. The only way to get things secure is the use information not under control of the program. Request the user to enter a password or put information in a store under control of the operating system. The later will not help if an attacker has physical access or maybe even a lot of rights unless special hardware like a Trusted Platform Module (TPM) is involved.
Well if the program can decrypt the data without additional input from the user, you can't really avoid someone else from accessing the file if he has access to the program.
If you are targeting Windows only, you might want to take a look at the Data Protection API (DPAPI). It essentially does the same thing, but the passphrase used for encryption is protected by the operating system on a user (or machine) scope. Simply put: you need the user login (or a program that runs on the given user account) to access the key (or for machine scope the login for any user on the machine).
I don't know how to access the API from Java, but Google brings up some wrapper libraries.
Don't hardcode the key. Assuming you don't have a user on hand to enter the passphrase, configure your code to pull the encryption key from a plain file, then rely on operating system security to keep the file safe. Provide a way to migrate to a new key when the system administrator deems it necessary.
I do not believe that this is possible without having the user entering the key on encryption and decryption.
You could employ some technique to make it harder to view the key without the full source code, but it would not be secure.
If your program can encrypt / decrypt a file on its own, then everything you need to perform the decryption is already built into the program, so a determined troublemaker could decrypt files you encrypted.
If possible, ask the user for a 'password,' and use what they give you as the encryption / decryption key.
Is it important that the user not be able to see his own encryption key? Or merely important that by discovering his won key, the user should not thereby know everyone else's key?
You could prompt the user for a personal key and either store it externally or prompt the user each time you need it. That way each user's key would be his own, and would not be usable to decrypt documents stored by other users on other machines.
the most secure method is not use any encryption, just put your user.properties to your home directory, with following code:
String userhome = System.getProperty("user.home");
String username = system.getProperty("user.name");
String hostname = java.net.InetAddress.getLocalHost().getHostName();
if (hostname.equals("webserver") && username.equals("root")){
ResourceBundle user = ResourceBundle.getBundle(userhome/ "user.properties");
}

Categories