PKCS#5 v2.0 java 語言實現參考
再接再厲:PKCS#5的實現:
PKCS#5主要講了基於口令的加密標準,具體包括:
1.金鑰派生:
1.1 金鑰派生介面
package com.broadthinking.pkcs.pkcs_5;
/**
* A typical application of the key derivation functions defined here might
* include the following steps:
* 1. Select a salt S and an iteration count c, as outlined in Section 4.
* 2. Select a length in octets for the derived key,dkLen.
* 3. Apply the key derivation function to the password, the salt, the iteration
* count and the key length to produce a derived key.
* 4. Output the derived key.
*
* @author CaesarZou
*
*/
public interface IPBKDF {
/**
* produce a derived key.
* @param P
* @param S
* @param c
* @param dkLen
* @return DK
*/
public byte [] derive(byte [] P, byte [] S, long c, int dkLen) throws PKCS5Exception;
}
1.2金鑰派生實現版本1
package com.broadthinking.pkcs.pkcs_5;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class PBKDF1 implements IPBKDF {
public PBKDF1(MessageDigest Hash) {
this.hash = Hash;
}
//default use SHA-1
public PBKDF1() {
try {
this.hash = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
}
}
/**
* PBKDF1 (P, S, c, dkLen)
* Options: Hash underlying hash function
* Input:
* P password, an octet string
* S salt, an eight-octet string
* c iteration count, a positive integer
* dkLen intended length in octets of derived key, a positive integer, at most
* 16 for MD2 or MD5 and 20 for SHA-1
* Output: DK derived key, a dkLen-octet string
* @throws PKCS5Exception
*
*/
@Override
public byte[] derive(byte[] P, byte[] S, long c, int dkLen) throws PKCS5Exception {
int hLen = hash.getDigestLength();
//1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output “derived key
// too long” and stop.
if(dkLen > hLen) {
throw new PKCS5Exception(PKCS5Exception.ERROR_PBKDF_DERIVEDKEY_TOO_LONG);
}
/*
* 2. Apply the underlying hash function Hash for c iterations to the
* concatenation of the password P and the salt S, then extract the
* first dkLen octets to produce a derived key DK: T1 = Hash (P || S) ,
* T2 = Hash (T1) , ... Tc = Hash (Tc-1) , DK = Tc<0..dkLen-1> .
*/
byte [] T = new byte [hLen];
try {
hash.reset();
hash.update(P);
hash.update(S);
for(long i=0;i<c;i++) {
hash.digest(T, 0, hLen);
hash.update(T);
}
} catch (DigestException e) {
//ignore
}
byte [] DK = new byte[(int)dkLen];
System.arraycopy(T, 0, DK, 0, (int)dkLen);
//3. Output the derived key DK.
return DK;
}
MessageDigest hash;
}
1.3金鑰派生版本2
package com.broadthinking.pkcs.pkcs_5;
public class PBKDF2 implements IPBKDF {
public PBKDF2() {
prf = new HMAC_SHA_1();
}
/**
* PBKDF2 (P, S, c, dkLen)
* Options: PRF underlying pseudorandom function (hLen denotes the length in
* octets of the pseudorandom function output)
* Input:
* P password, an octet string
* S salt, an octet string
* c iteration count, a positive integer
* dkLen intended length in octets of the derived key, a positive integer, at
* most (232 – 1) × hLen
* Output: DK derived key, a dkLen-octet string
* @throws PKCS5Exception
*
*/
@Override
public byte[] derive(byte[] P, byte[] S, long c, int dkLen) throws PKCS5Exception {
//1. If dkLen > (2^32 – 1) × hLen, output “derived key too long” and stop.
//int value can never big then 2^32-1
byte [] DK = new byte[dkLen];
/*
* 2. Let l be the number of hLen-octet blocks in the derived key, rounding up, and let r
* be the number of octets in the last block:
* l = dkLen / hLen ,
* r = dkLen – (l – 1) × hLen .
*/
int hLen = prf.getDigestLen();
int l = (dkLen/hLen) + (dkLen%hLen==0 ? 0: 1);
//3. For each block of the derived key apply the function F defined below to the
// password P, the salt S, the iteration count c, and the block index to compute the
// block:
for(int i=0;i<l;i++) {
//Ti = F (P, S, c, i) ,
byte [] T = new byte[hLen];
//F (P, S, c, i) = U1 \xor U2 \xor ⋅⋅⋅ \xor Uc
byte [] U = null;
for(int j=0;j<c;j++) {
if(j==0) {
U = new byte[S.length+4];
System.arraycopy(S, 0, U, 0, S.length); //S
U[S.length+0] = (byte)(i>>24); //INT(i)
U[S.length+1] = (byte)(i>>16);
U[S.length+2] = (byte)(i>>8);
U[S.length+3] = (byte)(i>>0);
}
//U1 = PRF (P, S || INT (i)) , U2 = PRF (P, U1) , ... Uc = PRF(P, Uc-1) .
U = prf.digest(P, U);
for(int k=0;k<hLen;k++) {
T[i] ^= U[i];
}
}
//4.Concatenate the blocks and extract the first dkLen octets to produce a derived key
// DK = T1 || T2 || ⋅⋅⋅ || Tl<0..r-1> .
int length = hLen;
if(i==(l-1)) {
length = dkLen - (i*hLen);
}
System.arraycopy(T, 0, DK, i*hLen, length);
}
//5. Output the derived key DK.
return DK;
}
HMAC_SHA_1 prf;
}
2.基於金鑰派生和塊加密演算法的加密/解密操作。
2.1 塊加密演算法介面
package com.broadthinking.pkcs.pkcs_5;
public interface IBlockCipher {
public byte [] encrypt(byte [] DK, byte [] M) throws PKCS5Exception;
public byte [] decrypt(byte [] DK, byte [] C) throws PKCS5Exception;
}
2.2 塊加密演算法例項:DES_CBC_PKCS5Padding
package com.broadthinking.pkcs.pkcs_5;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class BlockCipher_DES_CBC_Pad implements IBlockCipher {
public BlockCipher_DES_CBC_Pad() {
try {
des = Cipher.getInstance("DES/CBC/PKCS5Padding");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public byte [] encrypt(byte[] DK, byte[] M) throws PKCS5Exception {
return op(Cipher.ENCRYPT_MODE, DK, M);
}
@Override
public byte [] decrypt(byte[] DK, byte[] C) throws PKCS5Exception{
return op(Cipher.DECRYPT_MODE, DK, C);
}
private byte[] op(int mode, byte [] DK, byte [] data) throws PKCS5Exception {
if(DK.length != 16) {
throw new PKCS5Exception(PKCS5Exception.ERROR_BLOCKCIPHER_WRONG_KEY_LEN);
}
byte [] key = new byte[8];
byte [] iv = new byte[8];
System.arraycopy(DK, 0, key, 0, 8);
System.arraycopy(DK, 8, iv, 0, 8);
try {
DESKeySpec deskeySpec = new DESKeySpec(key);
Key deskey = SecretKeyFactory.getInstance("DES").generateSecret(deskeySpec);
IvParameterSpec param = new IvParameterSpec(iv);
des.init(mode, deskey, param);
return des.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cipher des;
}
2.3 塊加密演算法例項:DES_EDE3_CBC_PKCS5Padding
package com.broadthinking.pkcs.pkcs_5;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class BlockCipher_DES_EDE3_CBC_Pad implements IBlockCipher {
public BlockCipher_DES_EDE3_CBC_Pad() {
try {
des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public byte[] encrypt(byte[] DK, byte[] M) throws PKCS5Exception {
return op(Cipher.ENCRYPT_MODE, DK, M);
}
@Override
public byte[] decrypt(byte[] DK, byte[] C) throws PKCS5Exception{
return op(Cipher.DECRYPT_MODE, DK, C);
}
private byte[] op(int mode, byte [] DK, byte [] data) throws PKCS5Exception {
if(DK.length != 32) {
throw new PKCS5Exception(PKCS5Exception.ERROR_BLOCKCIPHER_WRONG_KEY_LEN);
}
byte [] key = new byte[24];
byte [] iv = new byte[8];
System.arraycopy(DK, 0, key, 0, 24);
System.arraycopy(DK, 24, iv, 0, 8);
try {
DESedeKeySpec deskeySpec = new DESedeKeySpec(key);
Key deskey = SecretKeyFactory.getInstance("DES").generateSecret(deskeySpec);
IvParameterSpec param = new IvParameterSpec(iv);
des.init(mode, deskey, param);
return des.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cipher des;
}
2.4 塊加密演算法例項:RC2_CBC_PKCS5Padding
package com.broadthinking.pkcs.pkcs_5;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.RC2ParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class BlockCipher_RC2_CBC_Pad implements IBlockCipher {
public BlockCipher_RC2_CBC_Pad(int keybits) {
try {
rc2 = Cipher.getInstance("RC2/CBC/PKCS5Padding");
effective_key_bits = keybits;
} catch (Exception e) {
e.printStackTrace();
}
}
public BlockCipher_RC2_CBC_Pad() {
this(32);
}
@Override
public byte[] encrypt(byte[] DK, byte[] M) throws PKCS5Exception {
return op(Cipher.ENCRYPT_MODE, DK, M);
}
@Override
public byte[] decrypt(byte[] DK, byte[] C) throws PKCS5Exception {
return op(Cipher.DECRYPT_MODE, DK, C);
}
private byte[] op(int mode, byte [] DK, byte [] data) throws PKCS5Exception {
//key between 1 - 128
if((DK.length < 9) || (DK.length > 136)) {
throw new PKCS5Exception(PKCS5Exception.ERROR_BLOCKCIPHER_WRONG_KEY_LEN);
}
byte [] key = new byte[DK.length-8];
byte [] iv = new byte[8];
System.arraycopy(DK, 0, key, 0, key.length);
System.arraycopy(DK, key.length, iv, 0, 8);
try {
SecretKeySpec rc2keySPec = new SecretKeySpec(key,"RC2");
Key rckey = SecretKeyFactory.getInstance("RC2").generateSecret(rc2keySPec);
RC2ParameterSpec param = new RC2ParameterSpec(effective_key_bits, iv);
rc2.init(mode, rckey, param);
return rc2.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Cipher rc2;
int effective_key_bits;
}
2.5 加解密操作1
package com.broadthinking.pkcs.pkcs_5;
public class PBES1 {
public PBES1(IPBKDF kdf, IBlockCipher cipher) {
//only support DES_CBC_PAD/RC2_CBC_PAD
this.kdf = kdf;
//only support MD2/MD5/SHA-1
this.cipher = cipher;
}
public PBES1() {
this.kdf = new PBKDF1();
this.cipher = new BlockCipher_DES_CBC_Pad();
}
public byte [] encrypt(byte [] M, byte [] P, byte [] S, long c) throws PKCS5Exception {
//1. Select an eight-octet salt S and an iteration count c, as outlined in Section 4.
//2. Apply the PBKDF1 key derivation function (Section 5.1) to the password P, the
// salt S, and the iteration count c to produce a derived key DK of length 16 octets:
// DK = PBKDF1 (P, S, c, 16) .
byte [] DK = kdf.derive(P, S, c, 16);
//3. Separate the derived key DK into an encryption key K consisting of the first eight
// octets of DK and an initialization vector IV consisting of the next eight octets:
//4. Concatenate M and a padding string PS to form an encoded message EM:
//5. Encrypt the encoded message EM with the underlying block cipher
//6. Output the ciphertext C.
return cipher.encrypt(DK, M);
}
public byte [] decrypt(byte [] C, byte [] P, byte [] S, long c) throws PKCS5Exception {
//1. Obtain the eight-octet salt S and the iteration count c.
//2. Apply the PBKDF1 key derivation function (Section 5.1) to the password P, the
// salt S, and the iteration count c to produce a derived key DK of length 16 octets:
// DK = PBKDF1 (P, S, c, 16) .
byte [] DK = kdf.derive(P, S, c, 16);
//3. Separate the derived key DK into an encryption key K consisting of the first eight
// octets of DK and an initialization vector IV consisting of the next eight octets:
//4. Decrypt the ciphertext C with the underlying block cipher
//5. Separate the encoded message EM into a message M and a padding string PS
//6. Output the recovered message M.
return cipher.decrypt(DK, C);
}
IBlockCipher cipher;
IPBKDF kdf;
}
2.6 加解密操作2
package com.broadthinking.pkcs.pkcs_5;
public class PBES2 {
public PBES2(IPBKDF kdf, IBlockCipher cipher) {
this.kdf = kdf;
this.cipher = cipher;
}
public PBES2() {
this.kdf = new PBKDF2();
this.cipher = new BlockCipher_DES_CBC_Pad();
}
public byte [] encrypt(byte [] M, byte [] P, byte [] S, long c, int dkLen) throws PKCS5Exception {
//1. Select a salt S and an iteration count c, as outlined in Section 4.
//2. Select the length in octets, dkLen, for the derived key for the underlying encryption scheme.
//3. Apply the selected key derivation function to the password P, the salt S, and the
// iteration count c to produce a derived key DK of length dkLen octets:
// DK = KDF (P, S, c, dkLen) .
byte [] DK = kdf.derive(P, S, c, dkLen);
//4. Encrypt the message M with the underlying encryption scheme under the derived
// key DK to produce a ciphertext C.
//5. Output the ciphertext C.
return cipher.encrypt(DK, M);
}
public byte [] decrypt(byte [] C, byte [] P, byte [] S, long c, int dkLen) throws PKCS5Exception {
//1. Obtain the salt S for the operation.
//2. Obtain the iteration count c for the key derivation function.
//3. Obtain the key length in octets, dkLen, for the derived key for the underlying
// encryption scheme.
//4. Apply the selected key derivation function to the password P, the salt S, and the
// iteration count c to produce a derived key DK of length dkLen octets:
// DK = KDF (P, S, c, dkLen) .
byte [] DK = kdf.derive(P, S, c, dkLen);
//5. Decrypt the ciphertext C with the underlying encryption scheme under the derived
// key DK to recover a message M. If the decryption function outputs “decryption
// error,” then output “decryption error” and stop.
//6. Output the recovered message M.
return cipher.decrypt(DK, C);
}
IBlockCipher cipher;
IPBKDF kdf;
}
3.基於金鑰派生和MAC演算法的訊息校驗碼生成和驗證操作。
3.1 MAC演算法
package com.broadthinking.pkcs.pkcs_5;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HMAC_SHA_1 {
public HMAC_SHA_1() {
try {
this.hash = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
//ignore
}
}
public byte [] digest(byte [] key, byte [] text) {
hash.reset();
hash.update(key);
hash.update(text);
return hash.digest();
}
public int getDigestLen() {
return hash.getDigestLength();
}
MessageDigest hash;
}
3.2 訊息驗證碼生成/驗證
package com.broadthinking.pkcs.pkcs_5;
public class PBMAC1 {
public PBMAC1() {
this.kdf = new PBKDF2();
this.macauth = new HMAC_SHA_1();
}
public byte [] sign(byte [] M, byte [] P, byte [] S, long c, int dkLen) throws PKCS5Exception {
//1. Select a salt S and an iteration count c, as outlined in Section 4.
//2. Select a key length in octets, dkLen, for the derived key for the underlying message
// authentication function.
//3. Apply the selected key derivation function to the password P, the salt S, and the
// iteration count c to produce a derived key DK of length dkLen octets:
// DK = KDF (P, S, c, dkLen) .
byte [] DK = kdf.derive(P, S, c, dkLen);
//4. Process the message M with the underlying message authentication scheme under
// the derived key DK to generate a message authentication code T.
//5. Output the message authentication code T.
return macauth.digest(DK, M);
}
public boolean verify(byte [] M, byte [] P, byte [] S, long c, int dkLen, byte [] EM) throws PKCS5Exception {
//1. Obtain the salt S and the iteration count c.
//2. Obtain the key length in octets, dkLen, for the derived key for the underlying
// message authentication scheme.
//3. Apply the selected key derivation function to the password P, the salt S, and the
// iteration count c to produce a derived key DK of length dkLen octets:
// DK = KDF (P, S, c, dkLen) .
byte [] DK = kdf.derive(P, S, c, dkLen);
//4. Process the message M with the underlying message authentication scheme under
// the derived key DK to verify the message authentication code T.
byte [] T = macauth.digest(DK, M);
if(T.length != EM.length) {
return false;
}
for(int i=0;i<T.length;i++) {
if(T[i]!=EM[i]) {
return false;
}
}
return true;
}
IPBKDF kdf;
HMAC_SHA_1 macauth;
}
4. 異常
package com.broadthinking.pkcs.pkcs_5;
public class PKCS5Exception extends Exception {
public PKCS5Exception(int errorcode) {
super("PKCS5Exception "+errorcode);
this.errorcode = errorcode;
}
public static final int ERROR_PBKDF_DERIVEDKEY_TOO_LONG = 1;
public static final int ERROR_BLOCKCIPHER_WRONG_KEY_LEN = 10;
/**
*
*/
private static final long serialVersionUID = -6228905820937466408L;
int errorcode;
}
5.測試
package com.broadthinking.pkcs.pkcs_5.test;
import com.broadthinking.pkcs.pkcs_5.PBES1;
import com.broadthinking.pkcs.pkcs_5.PBES2;
import com.broadthinking.pkcs.pkcs_5.PKCS5Exception;
public class TestVect {
public static void printHex(String Message, byte [] M) {
printHex(Message, M, 0, M.length);
}
public static void printHex(String Message, byte [] M, int offset , int length) {
System.out.print("# " + Message);
System.out.print(": ");
for(int i=0;i<length;i++) {
if((i%16)==0) {
System.out.println();
}
System.out.print(String.format("%02x ", M[offset+i]));
}
System.out.println();
System.out.println();
}
/**
* @param args
* @throws PKCS5Exception
*/
public static void main(String[] args) throws PKCS5Exception {
byte [] M = {1,2,3,4,5};
byte [] P = {(byte)0x98,0x76,0x54,0x32,(byte)0x10};
byte [] S = {9,9,9,9,9,9,9,9,9};
PBES1 esf = new PBES1();
byte [] C = esf.encrypt(M, P, S, 10);
byte [] M2 = esf.decrypt(C, P, S, 10);
printHex("M", M);
printHex("M2",M2);
PBES2 esf2 = new PBES2();
C = esf2.encrypt(M2, P, S, 10, 16);
M2 = esf2.decrypt(C, P, S, 10, 16);
printHex("M3",M2);
System.out.println("OVER");
}
}
相關文章
- PKCS#3 v1.4 java 語言實現參考Java
- ORACLE PLSQL語言參考OracleSQL
- C語言考試大綱(參考)C語言
- JavaScript語言參考手冊JavaScript
- 複習PHP-語言參考-常量PHP
- 簡單語法解析器實現參考
- C語言 形參和實參C語言
- C語言學習參考(基礎&進階)C語言
- MarkDown語法參考
- 複習PHP-語言參考-預定義變數PHP變數
- 吐血推薦:VBScript教程及語言參考電子書
- Oracle EBS 11.5.10 中文語言包安裝參考Oracle
- Go語言實現的Java Stream APIGoJavaAPI
- java語言實現二叉樹Java二叉樹
- SQL語法參考(轉)SQL
- 使用 Rust 語言編寫 Java JNI 實現RustJava
- SQL語法參考手冊SQL
- protobuf 語法,proto3 語法參考
- 參考微信模組化通訊具體實現
- 七牛重新整理介面php實現參考PHP
- JAVA期末簡答題參考Java
- Objective-C 語法快速參考Object
- SQL 語法參考手冊(轉)
- SQL語法參考手冊(轉)SQL
- JavaScript-快速語法參考-全-JavaScript
- HTML 字元實體參考HTML字元
- GoldenGate實施參考Go
- Java 語言實現簡易版掃碼登入Java
- 用java語言用sort方法實現groupby分組Java
- 程式語言實現模式模式
- Laravel 驗證碼類實現 (供學習、參考)Laravel
- 簡單sql欄位解析器實現參考SQL
- NGINX LDAP參考實現中的零日漏洞 - nginxNginxLDA
- 有沒有cqrs的具體實現可供參考?
- Java同步問題面試參考指南Java面試
- SQL 語法參考手冊(SQL) (轉)SQL
- Junit4 實踐參考
- 經典排序演算法的 C語言 | Java 實現排序演算法C語言Java