]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/ident/OpenSslDecryptor.java
Make CMS Login refresh more robust.
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / ident / OpenSslDecryptor.java
1 package org.argeo.ident;
2
3 import java.nio.charset.StandardCharsets;
4 import java.security.GeneralSecurityException;
5 import java.security.MessageDigest;
6 import java.util.Arrays;
7 import java.util.Base64;
8
9 import javax.crypto.BadPaddingException;
10 import javax.crypto.Cipher;
11 import javax.crypto.IllegalBlockSizeException;
12 import javax.crypto.spec.IvParameterSpec;
13 import javax.crypto.spec.SecretKeySpec;
14
15 /**
16 * Decrypts OpenSSL encrypted data.
17 *
18 * From
19 * https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
20 *
21 * See also
22 * https://stackoverflow.com/questions/54171959/badpadding-exception-when-trying-to-decrypt-aes-based-encrypted-text/54173509#54173509
23 * for new default message digest (not yet in CentOS 7 as of July 2019)
24 */
25 public class OpenSslDecryptor {
26 private static final int INDEX_KEY = 0;
27 private static final int INDEX_IV = 1;
28 private static final int ITERATIONS = 1;
29
30 private static final int SALT_OFFSET = 8;
31 private static final int SALT_SIZE = 8;
32 private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;
33
34 /** In bits. */
35 private final int keySize;
36
37 private Cipher cipher;
38 private MessageDigest messageDigest;
39
40 public OpenSslDecryptor() {
41 /*
42 * Changed to SHA-256 from OpenSSL v1.1.0 (see
43 * https://stackoverflow.com/questions/39637388/encryption-decryption-doesnt-
44 * work-well-between-two-different-openssl-versions)
45 */
46 this(128, "MD5");
47 }
48
49 public OpenSslDecryptor(int keySize, String messageDigest) {
50 this.keySize = keySize;
51 try {
52 this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
53 this.messageDigest = MessageDigest.getInstance(messageDigest);
54 } catch (GeneralSecurityException e) {
55 throw new IllegalArgumentException("Cannot initialise decryptor", e);
56 }
57 }
58
59 public String decryptAuthd(String dataBase64, String passphrase) {
60 try {
61 byte[] headerSaltAndCipherText = Base64.getDecoder().decode(dataBase64);
62
63 boolean withSalt = true;
64 byte[] salt = withSalt ? Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE)
65 : null;
66 byte[] encrypted = withSalt
67 ? Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length)
68 : headerSaltAndCipherText;
69
70 final byte[][] keyAndIV = EVP_BytesToKey(keySize / Byte.SIZE, cipher.getBlockSize(), messageDigest, salt,
71 passphrase.getBytes(StandardCharsets.US_ASCII), ITERATIONS);
72 SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
73 IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
74
75 cipher.init(Cipher.DECRYPT_MODE, key, iv);
76 byte[] decrypted = cipher.doFinal(encrypted);
77
78 String answer = new String(decrypted, StandardCharsets.US_ASCII);
79 return answer;
80 } catch (BadPaddingException e) {
81 throw new IllegalStateException("Bad password, algorithm, mode or padding;"
82 + " no salt, wrong number of iterations or corrupted ciphertext.", e);
83 } catch (IllegalBlockSizeException e) {
84 throw new IllegalStateException("Bad algorithm, mode or corrupted (resized) ciphertext.", e);
85 } catch (GeneralSecurityException e) {
86 throw new IllegalStateException(e);
87 }
88 }
89
90 private static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data,
91 int count) {
92 byte[][] both = new byte[2][];
93 byte[] key = new byte[key_len];
94 int key_ix = 0;
95 byte[] iv = new byte[iv_len];
96 int iv_ix = 0;
97 both[0] = key;
98 both[1] = iv;
99 byte[] md_buf = null;
100 int nkey = key_len;
101 int niv = iv_len;
102 int i = 0;
103 if (data == null) {
104 return both;
105 }
106 int addmd = 0;
107 for (;;) {
108 md.reset();
109 if (addmd++ > 0) {
110 md.update(md_buf);
111 }
112 md.update(data);
113 if (null != salt) {
114 md.update(salt, 0, 8);
115 }
116 md_buf = md.digest();
117 for (i = 1; i < count; i++) {
118 md.reset();
119 md.update(md_buf);
120 md_buf = md.digest();
121 }
122 i = 0;
123 if (nkey > 0) {
124 for (;;) {
125 if (nkey == 0)
126 break;
127 if (i == md_buf.length)
128 break;
129 key[key_ix++] = md_buf[i];
130 nkey--;
131 i++;
132 }
133 }
134 if (niv > 0 && i != md_buf.length) {
135 for (;;) {
136 if (niv == 0)
137 break;
138 if (i == md_buf.length)
139 break;
140 iv[iv_ix++] = md_buf[i];
141 niv--;
142 i++;
143 }
144 }
145 if (nkey == 0 && niv == 0) {
146 break;
147 }
148 }
149 for (i = 0; i < md_buf.length; i++) {
150 md_buf[i] = 0;
151 }
152 return both;
153 }
154
155 public static void main(String[] args) {
156 String dataBase64 = args[0];
157 String passphrase = args[1];
158 OpenSslDecryptor decryptor = new OpenSslDecryptor();
159 System.out.println(decryptor.decryptAuthd(dataBase64, passphrase));
160 }
161
162 }