我使用pycrypto
在python中生成了一个密钥对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,'')
然后我写了一些使用公钥来验证它已经验证好的东西......我的令牌中的签名工作得很好..
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和python中使用它,我将类似的库移植到java,但我开始遇到问题。也就是说,我的验证总是会失败......
首先从我导出的PEM中创建PublicKey对象...
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表示它们为无符号,但它们'重复相同的二进制表示)。但它似乎总是无法验证我的签名......这就是我用来做的事情:
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中它需要一个字节序列。我最好的猜测是获取那个long的字符串表示并将其转换为一堆字节,但是失败了。我的所有其他尝试都失败了(查看底层大整数的字节表示,查看数组的字节表示等)。我觉得我错过了一些非常简单的东西,但对于我的生活,我无法弄清楚它是什么......
有关签名的示例,在python中,我给出了:
[688304594898632574115230115201042030356261470845487427579402264460794863484312 120410963342371307037749493750151877472804877900061168981924606440672704577286260 395240971170923041153667805814235978868869872792318501209376911650132169706471509 89646220735762034864029622135210042186666476516651349805320771941650]
答案 0 :(得分: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。