我一直在为此奋斗了几天。我正在研究在嵌入式Linux环境中运行的Java 1.7应用程序。 OpenSSL不可用,我无法控制设备上操作系统映像中的内容。我需要计算自签名X.509证书的主题散列,产生与OpenSSL 1.0+相同的结果。这个现有的回答让我开始:Java 1.7主题Hash of X.509证书OpenSSL 1.0+兼容
The new subject hash openssl algorithm differs
从我的测试应用程序的代码如下所示。我的计算适用于主题名称仅包含CN值的证书,但对于指定了任何其他主题组件(OU,O,L,ST或C)的证书不起作用。对于那些证书,整个主题上的散列(不包括前奏序列)不匹配。根据上面的答案,我已经提取了每个组件(使用getObjectAt()方法),并单独对每个组件进行散列处理(没有快乐),颠倒了它们的顺序并且散列了它们(没有喜悦),以及其他一些变体主题。我一直在努力避免我担心的是下载OpenSSL源代码并使其运行更耗时,因此我可以检查中间结果并查看出错的地方。也许有人这样做可以提供一些指导。
private static void getSubjectHash(X509Certificate x509Cert)
{
try {
// get the subject principal
X500Principal x500Princ = x509Cert.getSubjectX500Principal();
// create a new principal using canonical name (order, spacing, etc.) and get it in ANS1 DER format
byte[] newPrincEnc = new X500Principal(x500Princ.getName(X500Principal.CANONICAL)).getEncoded();
// read it in as an ASN1 Sequence to avoid custom parsing
ASN1InputStream aIn = new ASN1InputStream(newPrincEnc);
ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
List<byte[]> terms = new ArrayList<>();
int finalLen = 0;
int i = 0;
// hash the encodables for each term individually and accumulate them in a list
for (ASN1Encodable asn1Set : seq.toArray()) {
byte[] term = ((ASN1Set) asn1Set).getEncoded();
terms.add(term);
finalLen += term.length;
// digest the term
byte[] hashBytes = truncatedHash(getDigest(term), 4);
printByteArray(String.format("hash of object at %d:", i++), hashBytes);
System.out.println("");
}
// hash all terms together in order of appearance
int j = 0;
byte[] finalEncForw = new byte[finalLen];
for (byte[] term : terms)
for (byte b : term)
finalEncForw[j++] = b;
// digest and truncate
byte[] hashBytes = truncatedHash(getDigest(finalEncForw), 4);
printByteArray("hash of all terms in forward order", hashBytes);
System.out.println("");
// hash all terms together in reverse order
j = 0;
byte[] finalEncRev = new byte[finalLen];
for (int k = terms.size() - 1; k >= 0; --k)
for (byte b : terms.get(k))
finalEncRev[j++] = b;
// digest and truncate
hashBytes = truncatedHash(getDigest(finalEncRev), 4);
printByteArray("hash of all terms in reverse order", hashBytes);
}
catch (Exception ex) {
throw new RuntimeException("uh-oh");
}
}
private static byte[] getDigest(byte[] toHash)
{
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA1");
}
catch (NoSuchAlgorithmException nsa) {
throw new RuntimeException("no such algorithm");
}
return md.digest(toHash);
}
private static byte[] truncatedHash(byte[] hash, int truncatedLength)
{
if (truncatedLength < 1 || hash.length < 1)
return new byte[0];
byte[] result = new byte[truncatedLength];
for (int i = 0; i < truncatedLength; ++i)
result[truncatedLength - 1 - i] = hash[i];
return result;
}
private static void printByteArray(String name, byte[] bytes)
{
System.out.println(name + " length=" + String.valueOf(bytes.length));
for (byte b: bytes) {
System.out.print(String.format("%02X ", Byte.toUnsignedInt(b)));
}
System.out.println();
}
构建OpenSSL。我的openssl和DER编码的数据略有不同。看来,openssl将每个主题名称条目标记为0x0C而不是0x13(可打印的字符串)。尽管如此,0x0C并没有出现在X.208允许标签的表格中,所以我不确定这是什么意思。我使用上面的标签CN作为0x0C,但所有其他主题名称条目为0x13的ASN1库。这就是为什么当CN以外的项目存在时,我不匹配openssl的原因。我可以用胶带粘贴,但我想明白为什么。规格更新的MOD? – NefariousB