从Java Framework管理openssl证书时遇到了一个问题。
openssl x509 -subject_hash ...
输出与Java框架在调用X509_NAME_hash()
时返回的输出不同,见下文。
原因是openssl改变了计算SHA1的方式。现在,不是像在MD5中那样将哈希基于主题的ASN.1 DER表示,而是首先计算CANONICAL表示,然后在此基础上计算ASN.1 DER,然后将其用作输入SHA1算法。
NativeCrypto.java :
// --- X509_NAME -----------------------------------------------------------
public static int X509_NAME_hash(X500Principal principal) {
return X509_NAME_hash(principal, "SHA1");
}
private static int X509_NAME_hash(X500Principal principal, String algorithm) {
try {
byte[] digest = MessageDigest.getInstance(algorithm).digest(principal.getEncoded());
return Memory.peekInt(digest, 0, ByteOrder.LITTLE_ENDIAN);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
我正在研究openssl库中的x_name.c
和x509_cmp.c
,以尝试在Java底层中修复它。但我没有成功。
我知道我必须修改X509_NAME_hash
中的x509_cmp.c
方法。但不确定在i2d_X509_NAME(x,NULL);
之前或之后是否应该改变
这个方法是计算主题名称的CANONICAL表示,对吧?那么,我需要根据输出计算ASN1 DER,对吧?但我无法做到。
如果有人可以指导我或通过一些解决这个问题来解决这个问题,我将感激不尽。
x509_cmp.c :
unsigned long X509_NAME_hash(X509_NAME *x)
{
unsigned long ret=0;
unsigned char md[SHA_DIGEST_LENGTH];
/* Make sure X509_NAME structure contains valid cached encoding */
i2d_X509_NAME(x,NULL);
if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, EVP_sha1(),
NULL))
return 0;
ret=( ((unsigned long)md[0] )|((unsigned long)md[1]<<8L)|
((unsigned long)md[2]<<16L)|((unsigned long)md[3]<<24L)
)&0xffffffffL;
return(ret);
}
函数x509_name_canon
显然执行重新编码。这是crypto/asn1/x_name.c
:
/* This function generates the canonical encoding of the Name structure.
* In it all strings are converted to UTF8, leading, trailing and
* multiple spaces collapsed, converted to lower case and the leading
* SEQUENCE header removed.
*
*/
答案 0 :(得分:2)
你离它不是很远,如果你想要与OpenSSL新主题相同的结果,你必须删除DN的前导序列。因此你必须做这样的事情:
// --- X509_NAME -----------------------------------------------------------
public static int X509_NAME_hash(X500Principal principal) {
return X509_NAME_hash(principal, "SHA1");
}
private static int X509_NAME_hash(X500Principal principal, String algorithm) {
try {
byte[] princ = principal.getEncoded();
final ASN1Sequence obj = (ASN1Sequence) ASN1Object.fromByteArray( princ );
// Remove the leading sequence ...
final DERSet enc = (DERSet) obj.getObjectAt(0);
final byte[] toHash = enc.getDEREncoded();
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] digest = md.digest(toHash);
return Memory.peekInt(digest, 0, ByteOrder.LITTLE_ENDIAN);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (IOException e) {
throw new AssertionError(e);
}
}
这样结果与OpenSSL新的Subject_hash相同。