Java 如何验证私钥是否与证书匹配..?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24121801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to verify if the private key matches with the certificate..?
提问by surendhar_s
I have the private key stored as .key file..
我将私钥存储为 .key 文件..
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQD5YBS6V3APdgqaWAkijIUHRK4KQ6eChSaRWaw9L/4u8o3T1s8J
rUFHQhcIo5LPaQ4BrIuzHS8yzZf0m3viCTdZAiDn1ZjC2koquJ53rfDzqYxZFrId
7a4QYUCvM0gqx5nQ+lw1KoY/CDAoZN+sO7IJ4WkMg5XbgTWlSLBeBg0gMwIDAQAB
AoGASKDKCKdUlLwtRFxldLF2QPKouYaQr7u1ytlSB5QFtIih89N5Avl5rJY7/SEe
rdeL48LsAON8DpDAM9Zg0ykZ+/gsYI/C8b5Ch3QVgU9m50j9q8pVT04EOCYmsFi0
DBnwNBRLDESvm1p6NqKEc7zO9zjABgBvwL+loEVa1JFcp5ECQQD9/sekGTzzvKa5
SSVQOZmbwttPBjD44KRKi6LC7rQahM1PDqmCwPFgMVpRZL6dViBzYyWeWxN08Fuv
p+sIwwLrAkEA+1f3VnSgIduzF9McMfZoNIkkZongcDAzjQ8sIHXwwTklkZcCqn69
qTVPmhyEDA/dJeAK3GhalcSqOFRFEC812QJAXStgQCmh2iaRYdYbAdqfJivMFqjG
vgRpP48JHUhCeJfOV/mg5H2yDP8Nil3SLhSxwqHT4sq10Gd6umx2IrimEQJAFNA1
ACjKNeOOkhN+SzjfajJNHFyghEnJiw3NlqaNmEKWNNcvdlTmecObYuSnnqQVqRRD
cfsGPU661c1MpslyCQJBAPqN0VXRMwfU29a3Ve0TF4Aiu1iq88aIPHsT3GKVURpO
XNatMFINBW8ywN5euu8oYaeeKdrVSMW415a5+XEzEBY=
-----END RSA PRIVATE KEY-----
And i extracted public key from ssl certificate file..
我从 ssl 证书文件中提取了公钥..
Below is the code i tried to verify if private key matches with ssl certificate or not..
下面是我试图验证私钥是否与 ssl 证书匹配的代码..
I used the modulus[i.e. private key get modulus==public key get modulus] to check if they are matching..
我使用模数[即私钥获取模数==公钥获取模数]来检查它们是否匹配..
And this seems to hold only for RSAKEYS..
这似乎只适用于 RSAKEYS ..
But i want to check for other keys as well..
但我也想检查其他键..
Is there any other alternative to do the same..??
有没有其他替代方法可以做同样的事情..??
private static boolean verifySignature(File serverCertificateFile, File serverCertificateKey) {
try {
byte[] certificateBytes = FileUtils.readFileToByteArray(serverCertificateFile);
//byte[] keyBytes = FileUtils.readFileToByteArray(serverCertificateKey);
RandomAccessFile raf = new RandomAccessFile(serverCertificateKey, "r");
byte[] buf = new byte[(int) raf.length()];
raf.readFully(buf);
raf.close();
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf);
KeyFactory kf;
try {
kf = KeyFactory.getInstance("RSA");
RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(kspec);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certificateBytes);
//Generate Certificate in X509 Format
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
RSAPublicKey publicKey = (RSAPublicKey) cert.getPublicKey();
in.close();
return privKey.getModulus() == publicKey.getModulus();
} catch (NoSuchAlgorithmException ex) {
logger.log(Level.SEVERE, "Such algorithm is not found", ex);
} catch (CertificateException ex) {
logger.log(Level.SEVERE, "certificate exception", ex);
} catch (InvalidKeySpecException ex) {
Logger.getLogger(CertificateConversion.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Signature verification failed.. This could be because the file is in use", ex);
}
return false;
}
And the code isn't working either.. throws invalidkeyspec exception
并且代码也不起作用.. 抛出 invalidkeyspec 异常
采纳答案by Anton Samsonov
So, what's the problem with pairwise parameter checking?
那么,成对参数检查有什么问题呢?
- If certificate specifies public key of type “RSA”, then:
- Extract
n
,e
from key file. - Compare these values with those in certificate.
- Extract
- If certificate specifies public key of type “DSA”, then:
- Extract
p
,q
,g
,y
from key file. - Compare these values with those in certificate.
- Extract
- If certificate specifies public key of type “blah-blah”, then:
- Extract corresponding values from key file.
- Compare these values with those in certificate.
- 如果证书指定了“RSA”类型的公钥,则:
- 提取
n
,e
从密钥文件。 - 将这些值与证书中的值进行比较。
- 提取
- 如果证书指定了“DSA”类型的公钥,则:
- 从密钥文件中提取
p
,q
,g
,y
。 - 将这些值与证书中的值进行比较。
- 从密钥文件中提取
- 如果证书指定了“blah-blah”类型的公钥,则:
- 从密钥文件中提取相应的值。
- 将这些值与证书中的值进行比较。
And so on, i. e. each algorithm requires its own proper handling. No general algorithm may exist, provided that key file format is actually custom. However, you may still slightly generalize it by specifying value indexes only:
依此类推,即每个算法都需要自己的正确处理。如果密钥文件格式实际上是自定义的,则可能不存在通用算法。但是,您仍然可以通过仅指定值索引来稍微概括它:
ComparisonScheme = new Dictionary<String, Integer[2][]> {
{ "RSA", {{0, 0}, {1, 1}} },
{ "DSA", {{0, 1}, {1, 2}, {2, 3}, {3, 0}} },
}
This is just an illustration, of course, — don't get syntax and numbers seriously.
当然,这只是一个说明——不要认真对待语法和数字。
回答by matthrms
I'm a bit rusty on this, but a little help is better than none right?
我对此有点生疏,但有一点帮助总比没有好,对吧?
In general, once you have a public/private key, you should be able to use a Cipher to encrypt some small amount of data with one key, and see if it can be successfully decrypted with the other. This is brute force and could have performance implications, but may be suitable for what you are trying to do (I would provide code, but I am in an environment where I can't test it, and I don't want to post anything untested. Encrypt and decrypt large string in java using RSAhas code snippets using a Cipher if you are interested).
一般来说,一旦你有了公钥/私钥,你应该可以使用Cipher用一个密钥加密一些少量数据,看看它是否可以用另一个成功解密。这是蛮力,可能会影响性能,但可能适合您尝试做的事情(我会提供代码,但我处于无法测试它的环境中,我不想发布任何内容未经测试。如果您有兴趣,使用 RSA 加密和解密 java 中的大字符串有使用密码的代码片段)。
Also, unless the headers have been tampered with in your example private key file, it is not in PKCS8 format. PKCS8 files won't specify an algorithm (such as RSA) in the header, as they can use different algorithms and thus specify the algorithm being used in the encoding itself.
此外,除非标题在您的示例私钥文件中被篡改,否则它不是 PKCS8 格式。PKCS8 文件不会在标头中指定算法(例如 RSA),因为它们可以使用不同的算法,从而指定在编码本身中使用的算法。
In addition, if i recall correctly, native java has terrible support for PEM. I believe you need to remove the the PEM headers from the key file and base64 decode it before you can do anything with it. Alternatively, if possible, you can ensure that your keyfiles are DER encoded, in which case the key spec should read it just fine.
此外,如果我没记错的话,原生 Java 对 PEM 的支持很糟糕。我相信您需要从密钥文件中删除 PEM 标头并对其进行 base64 解码,然后才能对其进行任何操作。或者,如果可能,您可以确保您的密钥文件是 DER 编码的,在这种情况下,密钥规范应该可以很好地读取它。
回答by user207421
Sign something with the private key and verify it with the public key from the certificate. This will fail unless they are a pair.
使用私钥对某些内容进行签名,并使用证书中的公钥对其进行验证。除非他们是一对,否则这将失败。
回答by Steffen Heil
If you want to check, if a RSA publicKey and a RSA privateKey belong together, you can use the following code:
如果要检查RSA publicKey 和RSA privateKey 是否属于一起,可以使用以下代码:
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
return rsaPublicKey.getModulus().equals( rsaPrivateKey.getModulus() )
&& BigInteger.valueOf( 2 ).modPow( rsaPublicKey.getPublicExponent()
.multiply( rsaPrivateKey.getPrivateExponent() ).subtract( BigInteger.ONE ),
rsaPublicKey.getModulus() ).equals( BigInteger.ONE );
I am searching for a similar solution for EC...
我正在为 EC 寻找类似的解决方案...
By now we already found a way for ECC: It is however a little more complicated:
到目前为止,我们已经找到了 ECC 的方法:但是它有点复杂:
ECPublicKey pk = (ECPublicKey) publicKey;
ECPrivateKey sk = (ECPrivateKey) privateKey;
ECParameterSpec pkSpec = pk.getParams(), skSpec = sk.getParams();
EllipticCurve skCurve = skSpec.getCurve(), pkCurve = pkSpec.getCurve();
ECField skField = skCurve.getField(), pkField = pkCurve.getField();
BigInteger skA = skCurve.getA(), skB = skCurve.getB();
if ( pkSpec != skSpec //
&& ( pkSpec.getCofactor() != skSpec.getCofactor() //
|| ! pkSpec.getOrder().equals( skSpec.getOrder() ) //
|| ! pkSpec.getGenerator().equals( skSpec.getGenerator() ) //
|| pkCurve != skCurve //
&& ( ! pkCurve.getA().equals( skA ) //
|| ! pkCurve.getB().equals( skB ) //
|| skField.getFieldSize() != pkField.getFieldSize() ) ) ) //
return false;
ECPoint w = pk.getW();
BigInteger x = w.getAffineX(), y = w.getAffineY();
if ( skField instanceof ECFieldFp ) {
BigInteger skP = ( (ECFieldFp) skField ).getP();
return pkField instanceof ECFieldFp && skP.equals( ( (ECFieldFp) pkField ).getP() ) //
&& y.pow( 2 ).subtract( x.pow( 3 ) ).subtract( skA.multiply( x ) ).subtract( skB ).mod( skP ).signum() == 0;
}
if ( skField instanceof ECFieldF2m ) {
int m = ( (ECFieldF2m) skField ).getM();
BigInteger rp = ( (ECFieldF2m) skField ).getReductionPolynomial();
if ( ! ( pkField instanceof ECFieldF2m ) || m != ( (ECFieldF2m) skField ).getM() || ! rp.equals( ( (ECFieldF2m) skField ).getReductionPolynomial() ) )
return false;
BigInteger x2 = f2mReduce( f2mMultiply( x, x ), rp, m );
return f2mReduce( f2mSum( f2mMultiply( y, y ), f2mMultiply( x, y ), f2mMultiply( x, x2 ), f2mMultiply( skA, x2 ), skB ), rp, m ).signum() == 0;
}
And here are the math helper function:
这是数学辅助函数:
public static final BigInteger f2mSum( BigInteger ... values )
{
if ( values.length == 0 )
return BigInteger.ZERO;
BigInteger result = values[ 0 ];
for ( int i = values.length - 1; i > 0; i -- )
result = result.xor( values[ i ] );
return result;
}
public static final BigInteger f2mAdd( BigInteger a, BigInteger b )
{
return a.xor( b );
}
public static final BigInteger f2mSubtract( BigInteger a, BigInteger b )
{
return a.xor( b );
}
public static final BigInteger f2mMultiply( BigInteger a, BigInteger b )
{
BigInteger result = BigInteger.ZERO, sparse, full;
if ( a.bitCount() > b.bitCount() ) {
sparse = b;
full = a;
} else {
sparse = b;
full = a;
}
for ( int i = sparse.bitLength(); i >= 0; i -- )
if ( sparse.testBit( i ) )
result = result.xor( full.shiftLeft( i ) );
return result;
}
public static final BigInteger f2mReduce( BigInteger input, BigInteger reductionPolynom, int bitLength )
{
while ( input.bitLength() > bitLength )
input = input.xor( reductionPolynom.shiftLeft( input.bitLength() - reductionPolynom.bitLength() ) );
return input;
}
回答by USer22999299
Your code is fine, just add the following:
您的代码很好,只需添加以下内容:
String file = "qwerty";
byte[] fileBytes = file.getBytes();
byte[] digitalSignature = signData(fileBytes, privKey);
System.out.println("SIGNATURE MADE");
boolean verified;
verified = verifySig(fileBytes, publicKey, digitalSignature);
System.out.println("verified: " + verified) ;
public static byte[] signData(byte[] data, PrivateKey key) throws Exception {
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(key);
signer.update(data);
return (signer.sign());
}
public static boolean verifySig(byte[] data, PublicKey key, byte[] sig) throws Exception {
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initVerify(key);
signer.update(data);
return (signer.verify(sig));
}
回答by Vadzim
This solution works in my case for preliminary check if elliptic curve keys do match:
此解决方案适用于我的情况,用于初步检查椭圆曲线键是否匹配:
boolean matches(PrivateKey privateKey, PublicKey publicKey) {
return ((ECKey) privateKey).getParams().equals(((ECKey) publicKey).getParams());
}
See also @SteffenHeil's answer for more math checks which give the same result for me.
另请参阅@SteffenHeil 的答案以获取更多数学检查,这些检查对我来说是相同的结果。
It's also interesting that Merchant Payment Processing certificate issued by Apple Pay has exactly the same ((ECKey) publicKey).getParams()
as parent "Apple Worldwide Developer Relations CA - G2" certificate.
So they both match the private key and the leaf can be resolved only with getSubjectDN()
= getIssuerDN()
checks involved.
有趣的是,Apple Pay 颁发的 Merchant Payment Processing 证书与((ECKey) publicKey).getParams()
父级“Apple Worldwide Developer Relations CA - G2”证书完全相同。所以它们都匹配私钥,并且叶子只能通过getSubjectDN()
=getIssuerDN()
检查来解析。