Thursday, August 14, 2008
SignatureTool (byte to char to byte)
之前要把簽章的值放到文字檔案內
簽章後得到的值是byte[]
放到文字檔案則需要String,或是char[]
但不知道為什麼
總是會出現 java.security.SignatureException: invalid encoding for signature
我試過許多byte轉成char的方式
包括自己位元運算硬寫出來的
用了不下四、五種API
看了一堆encoding的東西之後還是會有問題
實在是相當納悶
不過最後終於找到下面這種用BigInteger來轉的方式才OK
package swanky.util;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class SignatureTool {
public static byte[] getSignature(String message, PrivateKey privKey) {
byte[] signature = null;
try {
// initial signature object
Signature signalg = Signature.getInstance("DSA");
signalg.initSign(privKey);
// sign message
signalg.update(message.getBytes());
signature = signalg.sign();
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
public static boolean verify(String message, PublicKey pubKey,
byte[] signature) {
boolean result = true;
try {
// initial signature object
Signature verifyalg = Signature.getInstance("DSA");
verifyalg.initVerify(pubKey);
// update signature object
verifyalg.update(message.getBytes());
if (!verifyalg.verify(signature))
result = false;
} catch (java.security.SignatureException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private static final int KEYSIZE = 512;
public static void main(String[] args) throws Exception {
String message = "test";
KeyPairGenerator pairgen = KeyPairGenerator.getInstance("DSA");
SecureRandom random = new SecureRandom();
pairgen.initialize(KEYSIZE, random);
KeyPair keyPair = pairgen.generateKeyPair();
PublicKey pubKey = keyPair.getPublic();
PrivateKey privKey = keyPair.getPrivate();
byte[] signature = getSignature(message, privKey);
System.out.println("raw signature: " + signature.toString());
String encodeSig = toHexString(signature);
System.out.println("string signature: " + encodeSig);
byte[] decodeSig = fromHexString(encodeSig);
System.out.println("signature: " + decodeSig);
boolean result = verify(message, pubKey, decodeSig);
System.out.println("verify result=" + result);
result = verify(message + "fake", pubKey, decodeSig);
System.out.println("verify result=" + result);
}
public static String toHexString(byte[] in) {
BigInteger temp = new BigInteger(in);
return temp.toString(16);
}
public static byte[] fromHexString(String in) {
BigInteger temp = new BigInteger(in, 16);
return temp.toByteArray();
}
}
執行結果
raw signature: [B@1bf73fa string signature: 302d0215008a1cf6cb2a0228fb8a4330f308c19789e1992bb5021444db35e4c9f40b5efce53e9ea3c3821b3d3d4058 signature: [B@5740bb verify result=true verify result=false
但是呢你看
原本簽章的值跟轉回來的簽章值print出來的東西不一樣耶...
這到底是怎麼回事? @@"
Labels: code, digital signature, java, security
Comments:
<< Home
你是指
raw signature: [B@1bf73fa
signature: [B@5740bb
這兩個東西不一樣嗎?
這兩個東西印出來的資料分別是指
"signature"還有"decodeSig"這兩個變數的記憶體位址
所以當然會不一樣啊。
另外 byte[] 轉 String 可以考慮使用 base64喔
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public static String toBase64(byte[] in){
BASE64Encoder e = new BASE64Encoder();
return e.encode(in);
}
public static byte[] fromBase64(String in){
BASE64Decoder d = new BASE64Decoder();
try {
return d.decodeBuffer(in);
} catch (IOException e) {
return null;
}
}
raw signature: [B@1bf73fa
signature: [B@5740bb
這兩個東西不一樣嗎?
這兩個東西印出來的資料分別是指
"signature"還有"decodeSig"這兩個變數的記憶體位址
所以當然會不一樣啊。
另外 byte[] 轉 String 可以考慮使用 base64喔
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public static String toBase64(byte[] in){
BASE64Encoder e = new BASE64Encoder();
return e.encode(in);
}
public static byte[] fromBase64(String in){
BASE64Decoder d = new BASE64Decoder();
try {
return d.decodeBuffer(in);
} catch (IOException e) {
return null;
}
}
直接寫個byte array to string 的api就可以了。一個迴圈把他變成16進位數字。
char[] hexChar = {'0',...'F'};
char[] ca = new char[2*ba.length];
for (int i=0; i < ca.length; i+=2){
byte b = ba[i / 2];
ca[i] = hexChar[(b >> 4) & 0xf];
ca[i + 1] = hexChar[b & 0xf];
}
不然,如果有控制寫binary檔,那byte直接用8859_1當encoding轉換字串就好了,連base64都不用,畢竟什麼時候sun.misc的東西會不見也不知道。
String out=new String(ba,"8859_1");
byte [] ba=out.getBytes("8859_1");
[B@開頭都是記憶體位址,常看debug mode應該會很熟的。
Post a Comment
char[] hexChar = {'0',...'F'};
char[] ca = new char[2*ba.length];
for (int i=0; i < ca.length; i+=2){
byte b = ba[i / 2];
ca[i] = hexChar[(b >> 4) & 0xf];
ca[i + 1] = hexChar[b & 0xf];
}
不然,如果有控制寫binary檔,那byte直接用8859_1當encoding轉換字串就好了,連base64都不用,畢竟什麼時候sun.misc的東西會不見也不知道。
String out=new String(ba,"8859_1");
byte [] ba=out.getBytes("8859_1");
[B@開頭都是記憶體位址,常看debug mode應該會很熟的。
<< Home