Monthly Archives: April 2009

[对谈录]是否可以把logger的输出视为一个方法的output, 并针对它进行单元测试?

   A:logger的输出和一个方法的return, exception, output parameter一样,都可以视为这个方法的输出。如果logger的输出的确代表了功能需求,那我们就应该去测试它,包括单元测试(比如用程序读log文件然后与expectation比较)。典型的用况是批量操作中部分失败的日志。按需求,如果部分失败,不能抛exception,而应该把部分的失败放到日志中去。

   B:log是跟业务相去甚远的东西,不应该把它当作模块的一种输出,因此不能做单元测试;至于前面提到的批量操作的例子,应该把日志将要打印的错误信息置为单个操作的返回值或者异常消息,然后只针对批量中的单个操作进行单元测试。只要单个操作的测试通过了,没必要再对批量操作施加日志相关的测试了。

   A: 但是批量操作的日志可能不仅是各个单步操作的简单叠加。它的日志可能跟批量操作的上下文有关,而单步操作的方法可能不方便访问这个上下文;又或者批量操作输出的日志可能跟若干单步操作结果的组合有关,针对单步操作的方法无法进行这种测试。

   B: 那批量操作的代码就应该重构了。应该把所有的出错信息作为return值或output parameter的值,输出来,再用单元测试的代码去验证它。

   A:这似乎是比较优雅的方案。它不但解决了我们的争论,而且还使得log代码不能入侵到业务逻辑之中,使业务逻辑更干净。不过,这可能会存在性能问题。如果一个批量操作中有十几万条错误信息,把这些错误信息放到一个变量里,可能会导致内存溢出。如果用log来做,就没有这个问题,因为它见一条处理一条,不会产生巨大的对象。

   B:…

   A:…

 

Something about Triple Des Algorithm

http://www.informit.com/articles/article.aspx?p=26343&seqNum=4

Encrypting and Decrypting Data

Although SSL may handle most of your cryptography needs, you may discover that you need to manually encrypt and decrypt data from time to time. Basically, to encrypt or decrypt data, you need a key and a cipher. You can use the SecretKeyFactory to generate new keys. You can either generate a random key or use an existing array of bytes for the key. In the latter case, you first create a key "spec" from the original bytes and then pass this spec to the key factory.

The following code snippet creates a key spec from an array of bytes and then creates a key. The key is a DESede key, which is commonly known as Triple-DES—a more secure version of the original Data Encryption Standard (DES) algorithm. Some of the other available algorithms are DES (the original DES algorithm), Blowfish (a block cipher invented by Bruce Schneier), and PBEWithMD5AndDES (password-based encryption with MD5 digest mode and DES encryption).

// Create an array to hold the key
      byte[] encryptKey = "This is a test DESede key".getBytes();

// Create a DESede key spec from the key
      DESedeKeySpec spec = new DESedeKeySpec(encryptKey);

// Get the secret key factor for generating DESede keys
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(
        "DESede");

// Generate a DESede SecretKey object
      SecretKey theKey = keyFactory.generateSecret(spec);

Once you have a key, you can create a cipher using the Cipher.getInstance method:

Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");

In this case, you create a DESede (Triple-DES) cipher with some extra parameters. The second parameter (CBC) controls how you handle successive blocks of data. The simplest method of encrypting multiple blocks is called Electronic Code Book (ECB). When you use ECB, you simply encrypt one block at a time. For each block, you just take the original data, and encrypt it without any additional information. ECB is rather unsafe, however, because someone may be able to learn something about the content of the data by analyzing identical blocks. You can make your data safer by using Chained Block Cipher (CBC) mode. When you encrypt using CBC mode, you combine the data you want to encrypt with a special array of data called an initialization vector (IV) and then encrypt the combined data. For the second block, however, you combine the encrypted first block with the data you want to encrypt (usually with a simple XOR) and then encrypt the combined block. This eliminates repetitive patterns in the encrypted data.

Two other modes, Output Feedback (OFB) and Cipher Feedback (CFB), create a stream cipher byte generator out of a block cipher; that is, they make a block cipher behave like a stream cipher.

The other parameter—PKCS5Padding, in this case—indicates how to handle incomplete blocks. Remember that a block cipher operates on blocks of a fixed size. By using one of the common padding algorithms, you include the block size in the encrypted data, ensuring that when you decrypt, you get the correct number of bytes back. You can also specify NoPadding if you don’t want any padding.

When you use CBC mode, you must create an initialization vector before you encrypt or decrypt data. The contents of the IV don’t matter much, but you must use the same IV to encrypt and decrypt.

IvParameterSpec IvParameters = new IvParameterSpec(
  new byte[] { 12, 34, 56, 78, 90, 87, 65, 43 });

The following program encrypts a string of data and writes it out to a file:

import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class EncryptData
{
  public static void main(String[] args)
  {
    try
    {
// Create an array to hold the key
      byte[] encryptKey = "This is a test DESede key".getBytes();

// Create a DESede key spec from the key
      DESedeKeySpec spec = new DESedeKeySpec(encryptKey);

// Get the secret key factor for generating DESede keys
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(
        "DESede");

// Generate a DESede SecretKey object
      SecretKey theKey = keyFactory.generateSecret(spec);

// Create a DESede Cipher
      Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");

// Create an initialization vector (necessary for CBC mode)

      IvParameterSpec IvParameters = new IvParameterSpec(
        new byte[] { 12, 34, 56, 78, 90, 87, 65, 43 });

// Initialize the cipher and put it into encrypt mode
      cipher.init(Cipher.ENCRYPT_MODE, theKey, IvParameters);

      byte[] plaintext =
        "This is a sentence that has been encrypted".getBytes();
      
// Encrypt the data
      byte[] encrypted = cipher.doFinal(plaintext);

// Write the data out to a file
      FileOutputStream out = new FileOutputStream("encrypted.dat");
      out.write(encrypted);
      out.close();
    }
    catch (Exception exc)
    {
      exc.printStackTrace();
    }
  }
}

The process of encryption and decryption is almost identical from a coding perspective. To decrypt data, you use Cipher.DECRYPT_MODE in the init method for the Cipher object. Beyond that, everything else is the same. The following program decrypts the data that was encrypted by the EncryptData program:

import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;

public class DecryptData
{
  public static void main(String[] args)
  {
    try
    {
// Create an array to hold the key
      byte[] encryptKey = "This is a test DESede key".getBytes();

// Create a DESede key spec from the key
      DESedeKeySpec spec = new DESedeKeySpec(encryptKey);

// Get the secret key factor for generating DESede keys
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(
        "DESede");

// Generate a DESede SecretKey object
      SecretKey theKey = keyFactory.generateSecret(spec);

// Create a DESede Cipher
      Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");

// Create the initialization vector required for CBC mode
      IvParameterSpec ivParameters = new IvParameterSpec(
        new byte[] { 12, 34, 56, 78, 90, 87, 65, 43 } );

// Initialize the cipher and put it in decrypt mode
      cipher.init(Cipher.DECRYPT_MODE, theKey, ivParameters);

      File encryptedFile = new File("encrypted.dat");

// Create a byte block to hold the entire encrypted file
      byte[] encryptedText = new byte[(int) encryptedFile.length()];

      FileInputStream fileIn = new FileInputStream(encryptedFile);

// Read the entire encrypted file
      fileIn.read(encryptedText);

      fileIn.close();

// Decrypt the data
      byte[] plaintext = cipher.doFinal(encryptedText);

      String plaintextStr = new String(plaintext);

      System.out.println("The plaintext is:");
      System.out.println(plaintextStr);
    }
    catch (Exception exc)
    {
      exc.printStackTrace();
    }
  }
}

The cryptography libraries in Java 2 SDK 1.4 are pretty robust, and there are many options available. You can digitally sign or encrypt whole objects. You can also encrypt a stream of data without resorting to SSL. Take a look at the javax.crypto package to get an idea of some of the other available features.

NoSuchAlgorithmException: DESede/ECB/PKCS5Padding

To solve this problem, you may want to check your security provider.

Or, you can just check your code of Key generation.

For example, if you find

SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede/ECB/PKCS5Padding");

Then you are wrong at coding.

Instead, you should write

SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede")

In other words,
only the algorithm part can be accepted by the key factory,although you can use the full transformation to do cipher.

Cipher desCipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");