java - जावा 256-बिट एईएस पासवर्ड-आधारित एन्क्रिप्शन
encryption cryptography (6)
स्प्रिंग सिक्योरिटी क्रिप्टो मॉड्यूल का उपयोग करने पर विचार करें
स्प्रिंग सिक्योरिटी क्रिप्टो मॉड्यूल सममित एन्क्रिप्शन, कुंजी पीढ़ी, और पासवर्ड एन्कोडिंग के लिए समर्थन प्रदान करता है। कोड कोर मॉड्यूल के हिस्से के रूप में वितरित किया जाता है लेकिन किसी भी अन्य वसंत सुरक्षा (या वसंत) कोड पर कोई निर्भरता नहीं है।
यह एन्क्रिप्शन के लिए एक सरल अमूर्तता प्रदान करता है और यह आवश्यक है कि यहां क्या आवश्यक है,
पीकेसीएस # 5 के पीबीकेडीएफ 2 (पासवर्ड-आधारित कुंजी व्युत्पन्न समारोह # 2) का उपयोग करके "मानक" एन्क्रिप्शन विधि 256-बिट एईएस है। इस विधि को जावा 6 की आवश्यकता है। SecretKey उत्पन्न करने के लिए उपयोग किया जाने वाला पासवर्ड सुरक्षित स्थान पर रखा जाना चाहिए और साझा नहीं किया जाना चाहिए। आपके एन्क्रिप्टेड डेटा से समझौता होने की स्थिति में कुंजी के खिलाफ शब्दकोश हमलों को रोकने के लिए नमक का उपयोग किया जाता है। एक 16-बाइट यादृच्छिक प्रारंभिक वेक्टर भी लागू किया जाता है ताकि प्रत्येक एन्क्रिप्टेड संदेश अद्वितीय हो।
इंटर्नल्स पर एक नजर एरिक्सन के जवाब के समान संरचना दिखाती है।
जैसा कि इस प्रश्न में उल्लेख किया गया है, इसके लिए जावा क्रिप्टोग्राफी एक्सटेंशन (जेसीई) असीमित शक्ति क्षेत्राधिकार नीति की आवश्यकता है (अन्यथा आपको InvalidKeyException: Illegal Key Size
सामना करना InvalidKeyException: Illegal Key Size
)। यह जावा 6 , जावा 7 और जावा 8 के लिए डाउनलोड करने योग्य है।
उदाहरण उपयोग
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;
public class CryptoExample {
public static void main(String[] args) {
final String password = "I AM SHERLOCKED";
final String salt = KeyGenerators.string().generateKey();
TextEncryptor encryptor = Encryptors.text(password, salt);
System.out.println("Salt: \"" + salt + "\"");
String textToEncrypt = "*royal secrets*";
System.out.println("Original text: \"" + textToEncrypt + "\"");
String encryptedText = encryptor.encrypt(textToEncrypt);
System.out.println("Encrypted text: \"" + encryptedText + "\"");
// Could reuse encryptor but wanted to show reconstructing TextEncryptor
TextEncryptor decryptor = Encryptors.text(password, salt);
String decryptedText = decryptor.decrypt(encryptedText);
System.out.println("Decrypted text: \"" + decryptedText + "\"");
if(textToEncrypt.equals(decryptedText)) {
System.out.println("Success: decrypted text matches");
} else {
System.out.println("Failed: decrypted text does not match");
}
}
}
और नमूना उत्पादन,
Salt: "feacbc02a3a697b0" Original text: "*royal secrets*" Encrypted text: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" Decrypted text: "*royal secrets*" Success: decrypted text matches
मुझे 256 बिट एईएस एन्क्रिप्शन को लागू करने की आवश्यकता है, लेकिन 256 बिट कुंजी उत्पन्न करने के लिए मुझे ऑनलाइन मिले सभी उदाहरण "KeyGenerator" का उपयोग करते हैं, लेकिन मैं अपनी खुद की पासकी का उपयोग करना चाहता हूं। मैं अपनी खुद की कुंजी कैसे बना सकता हूं? मैंने 256 बिट्स पर इसे पैडिंग करने का प्रयास किया है, लेकिन फिर मुझे यह कहते हुए एक त्रुटि मिल रही है कि कुंजी बहुत लंबी है। मेरे पास असीमित क्षेत्राधिकार पैच स्थापित है, इसलिए समस्या नहीं है :)
अर्थात। KeyGenerator इस तरह दिखता है ...
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
संपादित करें
मैं वास्तव में पासवर्ड को 256 बाइट्स तक पैडिंग कर रहा था, बिट्स नहीं, जो बहुत लंबा है। निम्नलिखित कुछ कोड है जिसका मैं अब उपयोग कर रहा हूं कि मेरे पास इसके साथ कुछ और अनुभव है।
byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)
"TODO" बिट्स आपको स्वयं को करने की आवश्यकता है :-)
@ वूफू के संपादन में जोड़कर, निम्न संस्करण विभिन्न फ़ाइलों के साथ काम करने के लिए फ़ाइलों के बजाय इनपुटस्ट्रीम का उपयोग करता है। यह फ़ाइल की शुरुआत में चतुर्थ और नमक भी स्टोर करता है, जिससे इसे केवल पासवर्ड को ट्रैक करने की आवश्यकता होती है। चूंकि चतुर्थ और नमक को गुप्त होने की आवश्यकता नहीं है, इससे जीवन थोड़ा आसान हो जाता है।
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
public final static int SALT_LEN = 8;
static final String HEXES = "0123456789ABCDEF";
String mPassword = null;
byte[] mInitVec = null;
byte[] mSalt = new byte[SALT_LEN];
Cipher mEcipher = null;
Cipher mDecipher = null;
private final int KEYLEN_BITS = 128; // see notes below where this is used.
private final int ITERATIONS = 65536;
private final int MAX_FILE_BUF = 1024;
/**
* create an object with just the passphrase from the user. Don't do anything else yet
* @param password
*/
public AES(String password) {
mPassword = password;
}
public static String byteToHex(byte[] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
public static byte[] hexToByte(String hexString) {
int len = hexString.length();
byte[] ba = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return ba;
}
/**
* debug/print messages
* @param msg
*/
private void Db(String msg) {
System.out.println("** Crypt ** " + msg);
}
/**
* This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
* Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
*
* there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
* into uncertain problems with that.
*
* @param input - the cleartext file to be encrypted
* @param output - the encrypted data file
* @throws IOException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream)
throws IOException, IllegalBlockSizeException, BadPaddingException {
try {
long totalread = 0;
int nread = 0;
byte[] inbuf = new byte[MAX_FILE_BUF];
SecretKeyFactory factory = null;
SecretKey tmp = null;
// crate secureRandom salt and store as member var for later use
mSalt = new byte[SALT_LEN];
SecureRandom rnd = new SecureRandom();
rnd.nextBytes(mSalt);
Db("generated salt :" + byteToHex(mSalt));
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
/*
* Derive the key, given password and salt.
*
* in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
* The end user must also install them (not compiled in) so beware.
* see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
*/
KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/*
* Create the Encryption cipher object and store as a member variable
*/
mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mEcipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = mEcipher.getParameters();
// get the initialization vectory and store as member var
mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
Db("mInitVec is :" + byteToHex(mInitVec));
outputStream.write(mSalt);
outputStream.write(mInitVec);
while ((nread = inputStream.read(inbuf)) > 0) {
Db("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
// and results in full blocks of MAX_FILE_BUF being written.
byte[] trimbuf = new byte[nread];
for (int i = 0; i < nread; i++) {
trimbuf[i] = inbuf[i];
}
// encrypt the buffer using the cipher obtained previosly
byte[] tmpBuf = mEcipher.update(trimbuf);
// I don't think this should happen, but just in case..
if (tmpBuf != null) {
outputStream.write(tmpBuf);
}
}
// finalize the encryption since we've done it in blocks of MAX_FILE_BUF
byte[] finalbuf = mEcipher.doFinal();
if (finalbuf != null) {
outputStream.write(finalbuf);
}
outputStream.flush();
inputStream.close();
outputStream.close();
outputStream.close();
Db("wrote " + totalread + " encrypted bytes");
} catch (InvalidKeyException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidParameterSpecException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeySpecException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
* to disk as (output) File.
*
* I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
* and still have a correctly decrypted file in the end. Seems to work so left it in.
*
* @param input - File object representing encrypted data on disk
* @param output - File object of cleartext data to write out after decrypting
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws IOException
*/
public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream)
throws IllegalBlockSizeException, BadPaddingException, IOException {
try {
CipherInputStream cin;
long totalread = 0;
int nread = 0;
byte[] inbuf = new byte[MAX_FILE_BUF];
// Read the Salt
inputStream.read(this.mSalt);
Db("generated salt :" + byteToHex(mSalt));
SecretKeyFactory factory = null;
SecretKey tmp = null;
SecretKey secret = null;
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Decrypt the message, given derived key and initialization vector. */
mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Set the appropriate size for mInitVec by Generating a New One
AlgorithmParameters params = mDecipher.getParameters();
mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
// Read the old IV from the file to mInitVec now that size is set.
inputStream.read(this.mInitVec);
Db("mInitVec is :" + byteToHex(mInitVec));
mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
// creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
cin = new CipherInputStream(inputStream, mDecipher);
while ((nread = cin.read(inbuf)) > 0) {
Db("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
byte[] trimbuf = new byte[nread];
for (int i = 0; i < nread; i++) {
trimbuf[i] = inbuf[i];
}
// write out the size-adjusted buffer
outputStream.write(trimbuf);
}
outputStream.flush();
cin.close();
inputStream.close();
outputStream.close();
Db("wrote " + totalread + " encrypted bytes");
} catch (Exception ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* adding main() for usage demonstration. With member vars, some of the locals would not be needed
*/
public static void main(String[] args) {
// create the input.txt file in the current directory before continuing
File input = new File("input.txt");
File eoutput = new File("encrypted.aes");
File doutput = new File("decrypted.txt");
String iv = null;
String salt = null;
AES en = new AES("mypassword");
/*
* write out encrypted file
*/
try {
en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput));
System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
e.printStackTrace();
}
/*
* decrypt file
*/
AES dc = new AES("mypassword");
/*
* write out decrypted file
*/
try {
dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput));
System.out.println("decryption finished to " + doutput.getName());
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
e.printStackTrace();
}
}
}
बाइट सरणी से अपनी खुद की कुंजी बनाना आसान है:
byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES");
लेकिन 256-बिट कुंजी बनाना पर्याप्त नहीं है। यदि कुंजी जनरेटर आपके लिए 256-बिट कुंजी उत्पन्न नहीं कर सकता है, तो Cipher
कक्षा शायद एईएस 256-बिट का समर्थन नहीं करती है। आप कहते हैं कि आपके पास असीमित क्षेत्राधिकार पैच स्थापित है, इसलिए एईएस -256 सिफर समर्थित होना चाहिए (लेकिन फिर 256-बिट कुंजी भी होनी चाहिए, इसलिए यह एक कॉन्फ़िगरेशन समस्या हो सकती है)।
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
एईएस -256 समर्थन की कमी के लिए एक कामकाज एईएस -256 के कुछ स्वतंत्र रूप से उपलब्ध कार्यान्वयन करना है, और इसे कस्टम प्रदाता के रूप में उपयोग करना है। इसमें अपना खुद का Provider
उप Cipher.getInstance(String, Provider)
और इसे Cipher.getInstance(String, Provider)
साथ उपयोग करना शामिल है। लेकिन यह एक शामिल प्रक्रिया हो सकती है।
मैंने अतीत में जो किया है वह है SHA256 जैसे कुछ के माध्यम से कुंजी है, फिर हैश से बाइट को मुख्य बाइट [] में निकालें।
आपके बाइट के बाद [] आप बस कर सकते हैं:
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
password
साझा करें (एक char[]
) और salt
(एक SecureRandom
द्वारा चुने गए byte[]
-8 बाइट्स एक अच्छा नमक बनाता है - जिसे प्राप्तकर्ता के बाहर प्राप्तकर्ता के साथ गुप्त रखा जाना आवश्यक नहीं है)। फिर इस जानकारी से एक अच्छी कुंजी प्राप्त करने के लिए:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
जादू संख्या (जिसे कहीं भी स्थिरांक के रूप में परिभाषित किया जा सकता है) 65536 और 256 मुख्य व्युत्पन्न पुनरावृत्ति गणना और मुख्य आकार क्रमशः हैं।
महत्वपूर्ण व्युत्पन्न कार्य को महत्वपूर्ण कम्प्यूटेशनल प्रयास की आवश्यकता के लिए पुनरावृत्त किया जाता है, और यह हमलावरों को कई अलग-अलग पासवर्डों की कोशिश करने से रोकता है। उपलब्ध कंप्यूटिंग संसाधनों के आधार पर पुनरावृत्ति गणना को बदला जा सकता है।
मुख्य आकार को 128 बिट्स तक घटाया जा सकता है, जिसे अभी भी "मजबूत" एन्क्रिप्शन माना जाता है, लेकिन अगर एईएस कमजोर पड़ता है तो हम सुरक्षा सुरक्षा मार्जिन नहीं देते हैं।
उचित ब्लॉक-चेनिंग मोड के साथ प्रयुक्त, एक ही व्युत्पन्न कुंजी का उपयोग कई संदेशों को एन्क्रिप्ट करने के लिए किया जा सकता है। सीबीसी में, प्रत्येक संदेश के लिए एक यादृच्छिक प्रारंभिक वेक्टर (IV) उत्पन्न होता है, सादा पाठ समान होने पर भी विभिन्न सिफर टेक्स्ट प्रदान करता है। सीबीसी आपके लिए उपलब्ध सबसे सुरक्षित मोड नहीं हो सकता है (नीचे एईएडी देखें); विभिन्न सुरक्षा गुणों के साथ कई अन्य तरीके हैं, लेकिन वे सभी एक समान यादृच्छिक इनपुट का उपयोग करते हैं। किसी भी मामले में, प्रत्येक एन्क्रिप्शन ऑपरेशन के आउटपुट सिफर टेक्स्ट और प्रारंभिक वेक्टर होते हैं:
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
ciphertext
और iv
स्टोर करें। डिक्रिप्शन पर, उसी नमक और पुनरावृत्ति पैरामीटर के साथ पासवर्ड का उपयोग करके, SecretKey
बिल्कुल उसी तरह पुन: उत्पन्न होता है। इस कुंजी के साथ सिफर शुरू करें और संदेश के साथ प्रारंभिक प्रारंभिक वेक्टर प्रारंभ करें:
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
जावा 7 में एईएडी सिफर मोड के लिए एपीआई समर्थन शामिल था, और "सनजेसीई" प्रदाता ओपनजेडीके और ओरेकल वितरण के साथ शामिल है, इन्हें जावा 8 के साथ शुरू होता है। इन तरीकों में से एक को सीबीसी के स्थान पर दृढ़ता से अनुशंसा की जाती है; यह डेटा की अखंडता और साथ ही उनकी गोपनीयता की रक्षा करेगा।
एक java.security.InvalidKeyException
संदेश के साथ "अवैध कुंजी आकार या डिफ़ॉल्ट पैरामीटर" का अर्थ है कि क्रिप्टोग्राफी शक्ति सीमित है; असीमित ताकत क्षेत्राधिकार नीति फाइल सही स्थान पर नहीं हैं। जेडीके में, उन्हें ${jdk}/jre/lib/security
तहत रखा जाना चाहिए
समस्या विवरण के आधार पर, ऐसा लगता है जैसे नीति फ़ाइलों को सही ढंग से स्थापित नहीं किया गया है। सिस्टम में आसानी से एकाधिक जावा रनटाइम हो सकते हैं; यह सुनिश्चित करने के लिए दोबारा जांच करें कि सही स्थान का उपयोग किया जा रहा है।
Consider using Encryptor4j
First make sure you have Unlimited Strength Jurisdiction Policy files installed before your proceed so that you can use 256-bit AES keys.
Then do the following:
String password = "mysupersecretpassword";
Key key = KeyFactory.AES.keyFromPassword(password.toCharArray());
Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16);
You can now use the encryptor to encrypt your message. You can also perform streaming encryption if you'd like. It automatically generates and prepends a secure IV for your convenience.
If it's a file that you wish to compress take a look at this answer Encrypting a large file with AES using JAVA for an even simpler approach.