I have inherited a self-made certification solution based on Java and Microsoft SQL server. We are moving forward to a Venafi solution. The old certificate has to be moved from the old solution to a Venafi pki solution. The designer of the old solution is not here anymore, but I have the decryption part in Java, including the decryption key. I have absolutely no experience in Java, and very limited cryptographic experience in Powershell.
The Java code to decrypt is this:
SecretKeySpec key = new SecretKeySpec(Base64.decode(encryptionkey.getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(encrypted);
The password is something like: gTsLrRTFR5Q0cvQZFRuZPw== (Not the actual password) and the certificates in encrypted format is basic hex data like 0x5F4E23E1 ... all in 1024 sizes.
How would I decrypt these certificates in powershell?
I'm not the specialist for powershell so I can just give you some hints to run the decryption part, for tasks like "get all files in folder with ending *.enc"
I'm leaving it to you to find a solution.
Assuming you are having a key in the format
MTIzNDU2Nzg5MDEyMzQ1Ng==
then it is a Base64-encode key. If all of your 30.000 certificates were encrypted with the same key then simply get the hex encoded value of the key
using an online service like https://base64.guru/converter/decode/hex.
Just enter the string above and press convert Base64 to Hex and you receive the key as follows:
31323334353637383930313233343536
Now count the chars - here we have 32 chars that mean it is a 16 byte (128 bit) long key used for AES cryptography. It's not a joke to count them because
the length of the key is important for the decryption task.
Having a certificate-file that was encrypted with a 32-char long hex-key ("cert32.enc") you use openssl (can be used in powershell as well as in many
other scripting languages) with this command (it is important to use the -K-option with a capital K and the hexstring enclosed with quotation marks):
openssl enc -aes-128-ecb -d -in cert32.enc -out cert.pem -K "31323334353637383930313233343536"
This will decode you the original certificate to the file "cert.pem" as follows (it is a sample certificate !):
-----BEGIN CERTIFICATE-----
MIIEETCCAvmgAwIBAgIUP2GufsPxg8R2n6L161b6wauxnGYwDQYJKoZIhvcNAQEL
BQAwgZcxCzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcM
BGNpdHkxEDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNV
BAMMFmphdmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFj
cnlwdG9AZ214LmRlMB4XDTIwMDcxMTA4MDMxNloXDTIxMDcxMTA4MDMxNlowgZcx
CzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcMBGNpdHkx
EDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNVBAMMFmph
dmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFjcnlwdG9A
Z214LmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1fayJg6TO7D
NASipOooJu9aYxLfw5/jokbaggOkL7wPQ2+eWk3tby72jMFniWySm6YIkiTbewYo
8WKl94zWTT8xx5pg/eOh28BTQLsi0/s9RF37z+eJA1TjA6TuBesNevm3V310H93Y
BLTA2Mjp/99W/smBhefaeYEkLh6TrZAAi2JtUHrs0FwNREoXrRIfq9monpUpY7lr
YRSy70nEaEyctw6khxeTRVRR97ZdqogLl2oEur9k5NKD8XHJ6A7MYz+asLMYNcnA
0jV02wR+b6etEr1tAtnswdxQ5T6tLrAnoen5v/fXSDnz93L7oRmTPsQJhK55TrrM
G+RWvV8aoQIDAQABo1MwUTAdBgNVHQ4EFgQULdU7CowdOtePac360y4n9aEbP2ow
HwYDVR0jBBgwFoAULdU7CowdOtePac360y4n9aEbP2owDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAcP6EBsscMFAg0mMTLnd7+7VJzLuodPBAxiMS
zCYtUNS0KPBBR6OrGbHTbbvYd8/VGORpaWORfm0MDLP2kIxLKCYn4l7Wwoou7Idc
+Z+mohQKPwjtHnMZX6HyiCpmDF+qNR7dpOKpIsMahm9zVD8rfySFzr5oDSa7zFSr
MyJKmnz5I+gkUjJKvjYpKPjv7yuENhCbj4roNYK7ztN/vU6yJnFmzOaomP4MxhlE
GDoucjmy+qdGWF/i3Kh8n7zXBxRoBJZSkGqPE2N0PLIJlAFxb9c2QjPu5rmtZiDO
HiFj2Xk7jayMNw3JNVcayHjAcdHkp/u/BgTeqJWZRC+GsMz7ag==
-----END CERTIFICATE-----
Now we are working with the other key length (64 chars long). Having a base64 that looks like
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
you will get the hex-string with 64 characters:
3132333435363738393031323334353637383930313233343536373839303132
Now we are running the openssl-commandline with a similar algorithm (and another filename/key):
openssl enc -aes-256-ecb -d -in cert64.enc -out cert.pem -K "3132333435363738393031323334353637383930313233343536373839303132"
Voila, it's decoding to the same cert.pem as above.
All files are available via my GitHub repo for easy testing:
https://github.com/java-crypto/Stackoverflow/tree/master/Decoding_from_Java_to_Powershell
Related
The file is encrypted using the following command:
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678
The file is decrypted using the following command:
openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678
After printing the salt and key in java I get:
Key=b796fbb416732ce13d39dbb60c0fb234a8f6d70e49df1c7e62e55e81d33a6bff774254ac99268856bf3afe0b95defdad
and in cmd I get :
salt=2D7C7E1C84BD6693
key=B796FBB416732CE13D39DBB60C0FB234A8F6D70E49DF1C7E62E55E81D33A6BFF
iv =774254AC99268856BF3AFE0B95DEFDAD
after running :
openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -pbkdf2 -k 1234567812345678 -p
I am using the following code but the encrypted file is printing :
public static void main(String args[]) throws InvalidKeySpecException,
NoSuchAlgorithmException,
IllegalBlockSizeException,
InvalidKeyException,
BadPaddingException,
InvalidAlgorithmParameterException,
NoSuchPaddingException,
IOException {
String password = "1234567812345678";
String algorithm = "AES/CBC/PKCS5Padding";
IvParameterSpec ivParameterSpec = AESUtil.generateIv();
Resource resource = new ClassPathResource("file_enc.txt");
File inputFile = resource.getFile();
byte[] salt = new byte[8], data = new byte[1024], tmp;
int keylen = 32, ivlen = 16, cnt;
try( InputStream is = new FileInputStream(inputFile) ){
if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
|| is.read(salt) != 8 ) throw new Exception("salt fail");
byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(password.toCharArray(), salt, 10000, (keylen+ivlen)*8)
).getEncoded();
System.out.println("Key "+ byteArrayToHex(keyandIV));
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"),
new IvParameterSpec(keyandIV,keylen,ivlen));
while( (cnt = is.read(data)) > 0 ){
if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
}
tmp = ciph.doFinal(); System.out.write(tmp);
}
}
Your getKeyFromPassword creates a SecretkeyFactory for PBKDF2 (with HmacSHA256) but doesn't use it; instead you use the password as the key, which is wrong -- unless you actually wanted to do openssl enc with a key (-K uppercase and hex which should be 64 hexits/32 bytes for AES-256) and not a password (-k lowercase and any characters or any length). (Below IIRC 18, String.getBytes() gives you a JVM-dependent encoding which for an arbitrary string like a real password can vary on different systems or environments, which will cause cryptography using it to fail, but for the example string you show all realistic encodings give the same result.)
But even if you did use this factory it wouldn't be correct because openssl enc by default does not use PBKDF2, it uses a function called EVP_BytesToKey which is based on but different from PBKDF1. In OpenSSL 1.1.1 and 3.0 only, you can optionally specify -pbkdf2 (and/or -iter N) in enc, and if you don't it gives a warning message about 'deprecated key derivation' which you should have noticed, so I assume you're either using obsolete OpenSSL or trying to prevent us accurately understanding your situation and thus making it unlikely to get a useful answer.
Also, with either kind of key derivation (old EVP_BytesToKey or optional new PBKDF2) openssl enc by default uses salt, which is randomly generated and written in the file when encrypting; when decrypting you must read the salt from the file and use it. Specifically, skip or discard the first 8 bytes (which are the fixed characters Salted__) and take the next 8 bytes as salt, then the remainder of the file as ciphertext
Depending on what you want (or maybe your users/customers/etc want) to do there are three possibilities:
encrypt with openssl enc -aes-256-cbc -k ... with the default derivation (as now) and code the Java to read the salt from the file as above, implement EVP_BytesToKey using the password and that salt, and use its output for both the key and IV in the Java Cipher (for aes-256-cbc generate 48 bytes and use the first 32 bytes as key and the last 16 bytes as IV). EVP_BytesToKey uses a hash which defaults to SHA256 for OpenSSL 1.1.0 up and MD5 for lower versions, so you need to know which version did the encryption for this to work, or else you can specify the hash on the enc command with -md $hash. There have been hundreds of Qs about this going back over a decade; search for EVP_BytesToKey to find some of them.
encrypt with openssl enc -aes-256-cbc -pbkdf2 -k ... and code the Java to read the salt from the file as above and use the keyfactory you created to generate 48 bytes of 'key' material, which you must actually split into key and IV as above in the Java Cipher.
encrypt with openssl enc -aes-256-cbc -K 64hexits -iv 32hexits and code the Java to use the corresponding binary key and IV values.
In command i didn't specify neither random IV nor PKCS5Padding
When you use either old or new key derivation in openssl enc it derives the IV rather than specifying it separately; only if you use explicit key (-K uppercase) do you also specify -iv. openssl enc always defaults to the padding variously called pkcs5, pkcs7, or pkcs5/7, except when no padding is needed (stream ciphers like RC4 or ChaCha or stream modes like CTR, OFB, CFB).
Okay, you seem to be reading only about half of what I said. Most fundamentally, you still have openssl enc without -pbkdf2 but are trying to decrypt in Java with PBKDF2, which is flat wrong. In addition you are reading the salt but then converting it to hex, which is wrong, the salt from the file is the correct salt, and you are generating a completely bogus random IV, not deriving it as I said.
To be concrete, if you (or I) encrypt a file with -pbkdf2 like
openssl enc -aes-cbc-256 -pbkdf2 -k 1234567812345678
which will only work on OpenSSL 1.1.1 or 3.0 (i.e. since 2018), the following (minimalistic) Java code correctly decrypts it:
static void SO73456313OpensslEnc2_Java (String[] args) throws Exception {
// file pw: decrypt openssl(1.1.1+) enc -aes-256-cbc -pbkdf2 -k $pw
byte[] salt = new byte[8], data = new byte[1024], tmp;
int keylen = 32, ivlen = 16, cnt;
try( InputStream is = new FileInputStream(args[0]) ){
if( is.read(salt) != 8 || !Arrays.equals(salt, "Salted__".getBytes() )
|| is.read(salt) != 8 ) throw new Exception("salt fail");
byte[] keyandIV = SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(args[1].toCharArray(), salt, 10000, (keylen+ivlen)*8)
).getEncoded();
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyandIV,0,keylen,"AES"),
new IvParameterSpec(keyandIV,keylen,ivlen));
while( (cnt = is.read(data)) > 0 ){
if( (tmp = ciph.update(data, 0, cnt)) != null ) System.out.write(tmp);
}
tmp = ciph.doFinal(); System.out.write(tmp);
}
}
Note in PBEKeySpec I used itercount=10000 which is the enc default. You can use a higher number like 65536, which may be desirable for security (but that part is offtopic here), if you specify it when encrypting like:
openssl enc -aes-256-cbc -pbkdf2 -iter 65536 -k ...
OTOH if you use the command you posted, which you must on OpenSSL 1.1.0 or lower, then you cannot decrypt using PBKDF2 at all.
For that case instead see
How to decrypt file in Java encrypted with openssl command using AES?
How to decode a string encoded with openssl aes-128-cbc using java?
Java equivalent of an OpenSSL AES CBC encryption
Java AES Decryption with keyFile using BouncyCastle SSL
and CryptoJS AES encryption and Java AES decryption (cryptojs is sometimes compatible with OpenSSL, including the case in that Q).
And remember, as noted in at least some of those earlier Qs, the command you posted uses EVP_BytesToKey with SHA256 in 1.1.0 up but MD5 in 1.0.2 and lower, so you need to know which OpenSSL was or will be used.
I have gone through many similar threads but no luck!!
I want to generate public and private keys using a PEM file. Following is the code I am using for the same:
String pemFileNme = "C:\\Users\\amitmm\\Desktop\\clean\\key.pem";
File pubKeyFile = new File(pemFileNme);
File privKeyFile = new File(pemFileNme);
// read public key DER file
DataInputStream dis = new DataInputStream(new
FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
// read private key DER file
dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode public key
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey)
keyFactory.generatePublic(pubSpec);
// decode private key
PKCS8EncodedKeySpec privSpec = new
PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey)
keyFactory.generatePrivate(privSpec);
Exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: invalid key format
at
sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at main.java.me.txedo.security.Main2.f1(Main2.java:47)
at main.java.me.txedo.security.Main2.main(Main2.java:20)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
PEM File Content:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwnEEodFEf86+Ae+wYyI//u1kekIWnA3RfzbAwWD77uG7D9Ci
9vVNbPO4XT2hKL03/q7d7KTgrA1sjBltfaOzVfA56x1S/0cYVk4xI440dpLo0F+m
RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI/W
eqsD6TOQs4bI+3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx/IVzwyV7qjWiR4U
C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec/u+0m8top8cqQF+gpBn9iLXpbtahA3
pqyvLuNXRH9yn8mlEneBrjjl6U0H3W/AV7/dGwIBAwKCAQEAgaCtwTYtqonUAUp1
l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP/x8+ncNAcrOdsrue
U8J3jqAmnL43VNoQOYl2F7Qi+bdF4D/ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG
TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt
C0vPyuy9THlFWtkOdHItNK+vOTcpuHn29rFUJI/D3R+SQjcdqj3aaqljOtdeBxgd
yDl2/Z4rUyetgzcZMfNTt/NRT0hOJ6R6/2S7gFCTtxMHBh3vVCH+pLLnQyJvcPQu
AsORSwKBgQDhOPr1x/8BioqaasoXvO9NsGktCgPDjbC4d3jR8n6lCa42X/eIahaD
xi1VGWyQhdO7aMXiDmzOtox7xHcMRh+a5ySIs9gTsHkMB2hqwIUNg25INRkQ3Vr3
eWnoTBGsfJqC1TEME3ocKwmyz57ZAe4yyR/ZRdDX5DUt9qCCFeA8uQKBgQDdAzbq
7BlJkbTYfdlIRNJEJAO3wWqQTx8X0ttCMMwDluOT9l+RR/KuUxl85ph+kwJci6E/
ixfeMTW1NcsMY/lB6mTP0oooalU1MP7gpPSu+24zhLXnUHZotbNbv9nk6w/1WWhz
FBt5w2DG4kQPFK6LSySqcVuzIGQyvWD5PbpGcwKBgQCWJfyj2qoBBwcRnIa6ffTe
dZtzXAKCXnXQT6XhTFRuBnQkP/pa8WRX2XOOEPMLA+J88IPsCZ3fJF2n2E9dhBUR
722wd+VidaYIBPBHKwNeV57azhC16OdPpkaa3WEdqGcB43YIDPwSx1vMimnmAUl3
ML/mLos6mCNz+cBWuUAoewKBgQCTV3nx8ruGYSM6/pDa2IwtbVfP1kcK32oP4eeB
dd1Xue0NTupg2qHJjLuombr/DKw9smt/sg/pdiPOI9yy7VDWnEM1NwbFnDjOIKnr
GKMfUkl3rc6aNaRFzneSf+aYnLVOO5r3Yrz715XZ7C1fYx8Hh23G9j0iFZgh05X7
fnwu9wKBgHyC0X26KZQ0ukan5jDSiz4dapUp2d3F+vnRzZa2AOsmo995gsXLdfsJ
n0o4Z3LsQJUDRI3tQ4dXe/5jS4oFrOdxALOAw6YmvEv/3oHwsCYPDhqLNfIJ9I6m
Dt3yG61pUJiCArhPaYG17NQoCxF6Xi6GUajRsECbr8DdyGMAu5eE
-----END RSA PRIVATE KEY-----
I have tried removing file header and footer manually. I tried code from bouncycastle, no luck, same error.
Python code which works with this file:
def t2e_enc(plaintext, pk_pem_file = './2017-12-04T062008Z.pem'):
'''
Function for encryption of Track2 credit card data.
This function uses private key to derivate public part used for encryption
'''
with open(pk_pem_file, 'rb') as pk:
private_key = serialization.load_pem_private_key(pk.read(),
password=None, backend=default_backend())
public_key = serialization.load_pem_public_key(
private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo),
backend=default_backend()
)
ciphertext = public_key.encrypt(
plaintext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
b64ciphertext=base64.b64encode(ciphertext)
return b64ciphertext
I am doing this for the first time, so bear with me if there is any silly mistake.
Partial dupe Load RSA public key from file
So, you 'wrote' (I assume, copied) code that clearly says you need two files, in DER form, containing PKCS8 and 'X509' encodings. (What Java calls X.509 here is really the SubjectPublicKeyInfo component of X.509.) You give it one file, in PEM form not DER, containing a PKCS1 encoding not PKCS8 or X509 -- and you're surprised it doesn't work? Python works because it calls OpenSSL, and OpenSSL supports over a dozen encodings and formats for privatekeys, including this one; Java supports only one (outside of keystores) which isn't this one. (Bare) publickeys are a little better; internally libcrypto supports multiple forms, but in practice only two of them are used, and one of them matches Java -- although many publickeys are distributed, stored, and used in the form of X.509 certificates, which provides several more forms to worry about.
There are approximately 7 solutions to your situation:
the simplest is to use OpenSSL commandline to convert your one file to the two files Java wants:
# (corrected! pkey is inconsistent!)
openssl pkcs8 -topk8 -nocrypt -in input.pem -outform der -out private.der
openssl pkey -in input.pem -pubout -outform der -out public.der
# or for very old versions (should not be needed now)
openssl rsa -in input.pem -pubout -outform der -out public.der
Those files can now be read by the code you posted (except with the filenames separated). Note this conversion doesn't have to be done on the same system; if necessary you can do it elsewhere and copy the files, if you use a method that works for binary files i.e. NOT cut&paste.
if you really want only one file, but it can be converted, create the private.der file as above and read it with only the privatekey-related parts of your code, then do:
RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
you could convert the files to PKCS8 and 'X509' PEM by omitting -outform der from the above conversions, then read those files and manually 'de-PEM' by removing the header and trailer lines and converting the base64 to binary (removing or skipping the linebreaks); this results in binary PKCS8 and X509 encodings you can run through your existing code. This is as much work on the openssl side and more work on the Java side so there is no apparent advantage, except that PEM files are valid text and can be cut&pasted if necessary.
combining these, you could convert to PKCS8 PEM only, read that per bullet 3 (de-PEM then the privatekey parts of your code), then extract publickey from privatekey per bullet 2
one way to use the format you have (unconverted) in plain Java is to de-PEM per bullet 3 giving you a PKCS1 encoding, then manually construct the PKCS8 encoding, then proceed as before to run the PKCS8 through the KeyFactory and extract publickey per bullet 2. See my answer at Java: Convert DKIM private key from RSA to DER for JavaMail for a really ugly way to do this (including one de-PEM method). There is a better way if you use BouncyCastle (which has a class for this ASN.1 type), but if you use BouncyCastle it's better not to use this method at all, see below.
another way to use the unconverted format in plain Java is to de-PEM per bullet 3, then parse the ASN.1 structure of PKCS1 and construct an RSAPrivateCrtKeySpec which you can run through your KeyFactory instead of a PKCS8 encoding then extract publickey per bullet 2. This is even more complicated, although I think I have seen it somewhere; will add if I find it. Again BouncyCastle can improve this method, but doesn't need to, see below.
finally, if you have BouncyCastle it's dead easy. You don't say what you tried with 'no luck', but the following BouncyCastle code is all you need and does work:
try( Reader r = new FileReader(filename) ){
KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject());
}
Note this gives you a KeyPair with both privatekey and publickey objects from the one file.
I am provided two files encrypted_key.enc and encrypted_data.csv.enc. I need to use my private key to decrypt the encrypted_key.enc to get a symmetric key and then use that symmetric key to decrypt the encrypted_data.csv.enc file.
On the terminal, the following commands get the job done:
openssl rsautl -decrypt -ssl -inkey my_private_key -in encrypted_key.enc -out key
openssl aes-256-cbc -d -in encrypted_data.csv.enc -out secret.txt -pass file:key
My goal is to perform the java equivalent of the two commands. I was able to successfully decrypt the first file and retrieve the symmetric key.
Now I'm unable to use that symmetric key to decrypt the csv file. My issue arises in the decipher.init(Cipher.DECRYPT_MODE, keySpec); I receive the following stacktrace
Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default parameters
I'm unclear on what exactly I'm missing from the decryption process. I've tried changing the cipher provider but that didn't help. Other posts have posted solutions using an IVParameterSpec but my decryption case doesn't seem to need it or I'm confused on where to put it.
File file = new File("my_private_key");
PrivateKey pk = getPrivateKey(file);
// Decrypt secret key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pk);
File skFile = new File("encrypted_key.enc");
FileInputStream fileInputStream = new FileInputStream(skFile);
byte[] decodedBytes = IOUtils.toByteArray(fileInputStream);
byte[] original = cipher.doFinal(decodedBytes);
String decodedOriginal = new String(Base64.encodeBase64(original));
System.out.println(decodedOriginal);
// Use the secret key for decrypting file
File csvFile =
new File(
"encrypted_data.csv.enc");
FileInputStream csvIS = new FileInputStream(csvFile);
Cipher decipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(original, "AES");
decipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] csvOriginal = decipher.doFinal(IOUtils.toByteArray(csvIS));
String csvContents = new String(csvOriginal);
System.out.println(csvContents);
Before Java 1.8 (I think, somewhere around there) you are limited by the Java Unlimited Strength Policy for key sizes above 128-bits. This is the most likely cause of the exception you are getting.
Unfortunately this won't fix your code. openssl with the pass flag uses an insecure KDF named EVP_BytesToKey(). Java doesn't natively support this KDF. You don't want to use it anyway since it is insecure. Update the upstream code to use a better KDF like PBKDF2. There is native support for this in Java.
Further, you're using CBC mode in openssl and ECB mode in Java. And you aren't specifying an IV in openssl. I get the impression you didn't write the Java code yourself. You might benefit from taking the time to learn and research what is actually happening in your code and in the commands you are executing and you might be better equipped to solve the problem.
I got a string represents PEM certificate:
-----BEGIN CERTIFICATE-----
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
-----END CERTIFICATE-----
I assigned the above string to String variable String myCertStr.
What is the proper way to convert myCertStr to DER encoded byte[]?
(I am using Java 7, and I am not interested to use 3rd party library for this, I am seeking for a JDK7 way of doing it.)
IMPORTANT
As #dave_thompson_085 has pointed out in the comments, SunJCE CertificateFactory is indeed capable of parsing PEM files.
So you can just use that to get the Certificate object
as detailed at How to load public certificate from pem file..? (which is an earlier answer by #dave on the same topic, so please upvote it, instead of this one, if you find this useful !), and then access its encoded (DER) form.
However, if your PEM file is a raw "RSA PUBLIC KEY" (like the one that was attached to this question), or some other entity which SunJCE implementation can not parse directly, you can still parse and decode it manually, as detailed below.
Technically what you have here is not a certificate, but just a public key.
You can decode it to DER bytes as simple as that:
byte[] derBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----", "").replaceAll("\n", "")
);
Note, that what you will get will be a raw RSA (PKCS#1) key:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
You can use the same technique to decode X.509 certificates or private keys.
E.g. the code to decode the X.509 certificate:
byte[] certificateBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) CERTIFICATE-----", "").replaceAll("\n", "").getBytes("UTF-8")
);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)(certificateFactory.generateCertificate(
new ByteArrayInputStream(certificateBytes)
)
);
UPDATE
The code above uses Java 8 Base64 decoder.
As question has been updated asking for a Java 7 solution,
here is a link to an excellent thread, discussing various options available: Base64 Java encode and decode a string.
E.g. java.xml.bind method described there does not require any extra libraries on Java 7 (which seems to match what OP wants)
Using Java I have created RSA keypairs. Using Java I can use these keys to sign and verify some text. I can also "export" these keys in PEM format and load them into a Python test script. Once in the Python script, I can use these keys to sign and verify some text using M2Crypto.
I have not yet been able to verify in Python the signature I created in Java.
Right now I am just trying to get cross-platform signing and verifying to work.
Here is Java snippet:
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(key.getPrivate());
sig.update("This is a message.".getBytes("UTF8"));
byte[] signatureBytes = sig.sign();
return Base64.encodeBytes(signatureBytes, Base64.DO_BREAK_LINES);
Which generates:
PIp4eLhA941xmpdqu7j60731R9oWSNWcHvwoVADKxABGoUE02eDS0qZ4yQD2vYBdRDXXxHV4UjtW
YQwv9nsOzCBWeDQ0vv6W0dLVfTBuk79On7AALuwnTFr8s0y5ZN5RINvPPR60mwONav26ZbPj4ub3
NZqUS/zkqyO8Z8D2zUjk0pqAhWDGbFBaWPQJBPOY9iRt8GlsAUkGfYGeIx9DNU8aiJmQ3NnUHbs4
5NEr3xydbNJjwK96kkNJ9vyKZRfnNd4eW2UllPiwJSRQgefCQfh79ZuiYeQEuk3HMh7Si4iYl7uU
rWCgYFl4fGV1X/k+BSHR4ZZFWGQ3IPfafYHyNw==
And here is the public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg+N7xQHVHU3VHMZ9VGFdUf6ud3rAL8YS
sfWv2zFMnKAjh6XacwDGX6jJR/0/pyDriRGw/uodBDSFvgn9XEM/srMYtbJ1KQ9R9ChDifixF3Hx
yzbzPRvFoEfZnS/63+d1r0wnafz7xx0eDEYE3TgRKTMikwOsuIOFWa7GhA4BvP7ad79bI5ORJdLu
9Je+k+4dbt0xk2t7YopxYmyU+4zhZMxuthJTr69rXgqhgsCRdK6kFIGm3YuJ1WQcci8OVwH+3o7F
XzJHpSTxH57m6PX5lXaywIDCbUauTpBV3w+0vTeGI/2o+U40qhLBkpZT9GSVKxgXl5a0XxrkwTGn
61XZqQIDAQAB
-----END PUBLIC KEY-----
Then in Python the key is loaded and the signature is attempted to be verified:
from M2Crypto import RSA, EVP
pub_key = RSA.load_pub_key('public_key.pem')
verify_evp = EVP.PKey()
verify_evp.assign_rsa(pub_key)
verify_evp.verify_init()
verify_evp.verify_update("This is a message.")
if verify_evp.verify_final(sig_string.decode('base64')) == 1:
print "Good"
else:
print "Bad"
And this does not verify. I suspect it is some leading or trailing characters or encoding weirdness that I don't understand.
I have no particular attachement to M2Crypto and have played a bit with the gdata.tlslite modules as well.
Please note that the keys are working, and that the text and signature above verify in Java, and the keys (public and private) can be used within Python to sign and verify text. The problem is somewhere in how Java-generated signature and/or the message text is getting into the Python code.
What am I doing wrong?
M2Crypto.EVP defaults to 'sha1' (SHA1) and you're using MD5WithRSA. I think you should switch SHA1WithRSA (MD5 is a very weak algorithm).