2013-10-26 48 views
1

我產生Python中的密鑰對使用pycrypto用Python驗證的密鑰簽名,不會用Java?

key=RSA.generate(bit_size,os.urandom) 

exportedPrivateKey = key.exportKey('PEM', None, pkcs=1).decode("utf-8") 
exportedPublicKey = key.publickey().exportKey('PEM', None, pkcs=1).decode("utf-8") 

我寫了一個小程序,需要一個消息和體徵哈希散列...

hash = MD5.new(json_info.encode("utf-8")).digest() 
privateKey = RSA.importKey(USER_TOKEN_PRIVATE_KEY) 
signature = privateKey.sign(hash,'') 

然後我寫的東西,所使用的公鑰來驗證它驗證okay..the簽名在我的令牌做工精細..

hash = MD5.new(packet.encode("utf-8")).digest() 
publicKey = RSA.importKey(tokenPublicKey) 

if publicKey.verify(hash, signature): 
    return json.loads(packet) 
else: 
    return None 

現在,因爲我需要在Java中使用此作爲WEL l作爲python,我正在向java移植一個類似的庫,但我開始遇到問題。也就是說,我的驗證總是失敗......

我通過創建從我導出PEM的公鑰對象開始......

byte[] encoded = Base64.decodeBase64(USER_TOKEN_PUBLIC_KEY); 

//decode the encoded RSA public key 
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); 
KeyFactory kf = KeyFactory.getInstance("RSA"); 
PublicKey pubKey = kf.generatePublic(keySpec); 

我能得到簽名,這是完全一樣的簽名和值散列到完全相同的值(好吧,類似; java代表字節作爲有符號整數,而python代表它們是無符號的,但它們是相同的二進制表示)。但它似乎總是無法驗證我的signature..here是我用來做什麼:

byte[] hash = hasher.digest(packet.getBytes("UTF-8")); 

InputStream hashStream = new ByteArrayInputStream(hash); 

final Signature sign = Signature.getInstance("MD5withRSA"); 
sign.initVerify(pubKey); 

byte[] buffer = new byte[256]; 
int length; 
while ((length = hashStream.read (buffer)) != -1) 
    sign.update (buffer, 0, length); 

hashStream.close(); 

System.out.println(sign.verify(signature.getBytes("UTF-8"))); 

不幸的是,這只是打印錯誤。

我可以真正看到的唯一區別是,當我通過它來驗證在Java中,它要求一個long數組,而在python中它需要一個字節序列。我最好的猜測是採取這個長的字符串表示,並將其轉換成一堆字節,但失敗了。我所有的其他嘗試都失敗了(查看底層大整數的字節表示,查看數組的字節表示等)。我覺得我錯過了一些非常簡單的事情,但對於我的生活,我無法弄清楚它是什麼...

對於簽名看起來像在python中的例子,我給了:

[688304594898632574115230115201042030356261470845487427579402264460794863484312 120410963342371307037749493750151877472804877900061168981924606440672704577286260 395240971170923041153667805814235978868869872792318501209376911650132169706471509 89646220735762034864029622135210042186666476516651349805320771941650]

回答

2

你正在處理簽名作爲Java字符串,使用該字符串的UTF-8編碼作爲簽名值。由於簽名可以是任何編碼,包括不編碼成可打印字符串的字節,因此不能正確。

[編輯]

行,所以整數看起來像表示爲括號之間的數1024比特的簽名。因此,此代碼應幫助:

import java.math.BigInteger; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class SignatureFromPython { 
    private static final Pattern PAT = Pattern.compile("\\[(\\d+)\\]"); 

    private static byte[] i2osp(final BigInteger i, final int bitSize) { 
     if (i == null || i.signum() == -1) { 
      throw new IllegalArgumentException(
        "input parameter should not be null or negative"); 
     } 

     if (bitSize < Byte.SIZE) { 
      throw new IllegalArgumentException(
        "bitSize parameter should not be negative and a multiple of 8"); 
     } 

     final int byteSize = (bitSize - 1)/Byte.SIZE + 1; 
     final byte[] signedBigEndian = i.toByteArray(); 
     final int signedBigEndianLength = signedBigEndian.length; 
     if (signedBigEndianLength == byteSize) { 
      return signedBigEndian; 
     } 

     final byte[] leftPadded = new byte[byteSize]; 

     if (signedBigEndianLength == byteSize + 1) { 
      System.arraycopy(signedBigEndian, 1, leftPadded, 0, byteSize); 
     } else if (signedBigEndianLength < byteSize) { 
      System.arraycopy(signedBigEndian, 0, leftPadded, byteSize 
        - signedBigEndianLength, signedBigEndianLength); 
     } else { 
      throw new IllegalArgumentException(
        "Integer i is too large to fit into " + bitSize + " bits"); 
     } 
     return leftPadded; 
    } 

    public static String toHex(final byte[] data) { 
     final StringBuilder hex = new StringBuilder(data.length * 2); 
     for (int i = 0; i < data.length; i++) { 
      hex.append(String.format("%02X", data[i])); 
     } 
     return hex.toString(); 
    } 

    public static void main(String[] args) { 
     String sigString = "[68830459489863257411523011520104203035626147084548742757940226446079486348431212041096334237130703774949375015187747280487790006116898192460644067270457728626039524097117092304115366780581423597886886987279231850120937691165013216970647150989646220735762034864029622135210042186666476516651349805320771941650]"; 
     Matcher sigMatcher = PAT.matcher(sigString); 
     if (!sigMatcher.matches()) { 
      throw new IllegalArgumentException("Whatever"); 
     } 
     BigInteger sigBI = new BigInteger(sigMatcher.group(1)); 
     // requires bouncy castle libraries 
     System.out.println(toHex(i2osp(sigBI, 1024))); 
    } 
} 

[EDIT2]

privateKey.sign(hash,'')使用 「原始」 RSA簽名。它需要使用PKCS115_SigScheme來代替。

爲了更安全,請嘗試使用PSS樣式簽名和higher key size。此外,對於簽名應用程序,MD5的使用也被破壞了。改用SHA-256或SHA-512。

+0

那麼我該如何對待簽名?它傳遞給我作爲一個消息,所以我把它作爲一個字符串,但我可以適當地投它。我遇到的問題是,它的任何合理的轉換也失敗了,我只是不知道該怎麼做。 – DivineWolfwood

+0

告訴我們簽名的樣子,如果我不知道編碼簽名字符串。如果它只是作爲一種編碼傳遞而沒有不可打印的字符,那麼您正在丟失傳輸中的數據,而且您幾乎無法做到。 –

+0

在python中,它基本上是作爲一個大整數的數組傳遞的,並且它適當地進行了驗證。這裏有一個例子:「[68830459489863257411523011520104203035626147084548742757940226446079486348431212041096334237130703774949375015187747280487790006116898192460644067270457728626039524097117092304115366780581423597886886987279231850120937691165013216970647150989646220735762034864029622135210042186666476516651349805320771941650]」 – DivineWolfwood