我有用於SSL連接的RSA密鑰和X.509證書。驗證RSA密鑰是否與Java中的X.509證書匹配
密鑰和證書以PEM格式(由OpenSSL生成)存儲在文件中,並在Apache HTTP服務器環境中使用。
是否有一種簡單的方法來驗證密鑰是否與僅使用Java代碼(不執行openssl二進制文件並解析輸出)的證書相匹配,例如通過使用Java安全性和/或Bouncycastle庫方法?
我有用於SSL連接的RSA密鑰和X.509證書。驗證RSA密鑰是否與Java中的X.509證書匹配
密鑰和證書以PEM格式(由OpenSSL生成)存儲在文件中,並在Apache HTTP服務器環境中使用。
是否有一種簡單的方法來驗證密鑰是否與僅使用Java代碼(不執行openssl二進制文件並解析輸出)的證書相匹配,例如通過使用Java安全性和/或Bouncycastle庫方法?
下面的代碼比較公鑰和私鑰的SHA-1模數。每對模數應該是唯一的(除非你的密鑰對生成機制或隨機生成器當然被破壞)。
請注意,以下代碼要求密鑰採用未加密的PKCS#8格式。最好使用PKCS#12,並將二進制PKCS#12文件加載到KeyStore(提供密碼)中。
openssl pkcs8 -topk8 -in key.pem -out keypk8.pem -nocrypt
最後的Java代碼:
import static org.bouncycastle.util.encoders.Hex.toHexString;
import java.io.ByteArrayInputStream;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
public class CompareCertAndKey {
/**
* Checks if the certificate and RSA private key match.
*
* @param args the path to the certificate file in args[0] and that of the private key in args[1]
*/
public static void main(String[] args) {
try {
final PemReader certReader = new PemReader(new FileReader(args[0]));
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new IllegalArgumentException("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
final Certificate cert = fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new IllegalArgumentException("Certificate file does not contain an X509 certificate");
}
final PublicKey publicKey = cert.getPublicKey();
if (!(publicKey instanceof RSAPublicKey)) {
throw new IllegalArgumentException("Certificate file does not contain an RSA public key but a " + publicKey.getClass().getName());
}
final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
final byte[] certModulusData = rsaPublicKey.getModulus().toByteArray();
final MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
final byte[] certID = sha1.digest(certModulusData);
final String certIDinHex = toHexString(certID);
final PemReader keyReader = new PemReader(new FileReader(args[1]));
final PemObject keyAsPemObject = keyReader.readPemObject();
if (!keyAsPemObject.getType().equalsIgnoreCase("PRIVATE KEY")) {
throw new IllegalArgumentException("Key file does not contain a private key but a " + keyAsPemObject.getType());
}
final byte[] privateKeyData = keyAsPemObject.getContent();
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
final KeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyData);
final PrivateKey privateKey = keyFact.generatePrivate(keySpec);
if (!(privateKey instanceof RSAPrivateKey)) {
throw new IllegalArgumentException("Key file does not contain an X509 encoded private key");
}
final RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
final byte[] keyModulusData = rsaPrivateKey.getModulus().toByteArray();
final byte[] keyID = sha1.digest(keyModulusData);
final String keyIDinHex = toHexString(keyID);
System.out.println(args[0] + " : " + certIDinHex);
System.out.println(args[1] + " : " + keyIDinHex);
if (certIDinHex.equalsIgnoreCase(keyIDinHex)) {
System.out.println("Match");
System.exit(0);
} else {
System.out.println("No match");
System.exit(-1);
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(-2);
}
}
}
+1像往常一樣,一個非常詳細的答案。但是,如果這是實現這一目標最簡明的方法,那就讓我難過。我希望Java標準庫(或第三方庫)能夠使它成爲一個單行(當然,忽略從文件中讀取數據)。 –
@Duncan在這方面有很多選擇。最簡單的方法是創建一個OpenSSL兼容性層。但是OpenSSL非常龐大,沒有很好的記錄。 –
如何在Java中將PEM證書轉換爲pkcs8(無需調用OpenSSL)? – Calonthar
非常感謝您對上面的代碼片斷。它的工作與我bouncycastle版本1.51
<bcprov-jdk15on-version>1.51</bcprov-jdk15on-version>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bcprov-jdk15on-version}</version>
</dependency>
很多感謝您的代碼段。
除了owlstead的回答,OWASP還提供了一個關於[證書和公共密鑰鎖定](https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning)上的公共密鑰鎖定的Android Java示例。它使用自定義的'TrustManager',因爲這是Java的方式。我不相信Sun Java和Android Java在代碼方面有什麼區別。 – jww