使用 Signature.sign 签名 Java 字符串:为什么每次签名都不一样?
Signing Java String using Signature.sign: why is the signature is different every time?
我已经写了一个class,它的目的是使用服务器上的私钥对一串文本进行签名和base64,然后return签名。每次为同一文本 运行 时,它都会生成不同的签名。它为什么要这样做?
我已通过在我的测试机器上暂时禁用转换来检查正在更改的是签名,而不是 base64 转换的问题。
我的代码:
public class SigningPIP implements SigningPIPInterface {
private static final String SIGNING_ALGORITHM = "SHA1withDSA";
/**
* Provides a signature for a stringified JSON license
* @param license stringified JSON license to be used for signature generation
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore to be used for signing
* @param privateKeyPassword The password for the private key to be used for signing
* @return Properties object containing the base64 encoded signature, algorithm used and certificate DN
*/
@Override
public Properties getLicenseSignature(String license, String keystoreFilePath, String keystorePassword, String privateKeyAlias, String privateKeyPassword) {
PrivateKey privateKey = getPrivateKey(keystoreFilePath, keystorePassword, privateKeyAlias, privateKeyPassword);
Properties licenseSignature = new Properties();
licenseSignature.setProperty("sig_algorithm", SIGNING_ALGORITHM);
licenseSignature.setProperty("cert_DN", getCertificateIssuerDN(keystoreFilePath, keystorePassword, privateKeyAlias));
byte[] licenseByteArray = license.getBytes();
System.out.println(new String(licenseByteArray));
try {
Signature dsa = Signature.getInstance(SIGNING_ALGORITHM);
dsa.initSign(privateKey);
dsa.update(licenseByteArray);
byte[] signature = dsa.sign();
licenseSignature.setProperty("signature_base64", DatatypeConverter.printBase64Binary(signature));
}
catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
//TODO: Add logging
}
return licenseSignature;
}
/**
* Pulls the private key from the specified keystore
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore
* @param privateKeyPassword The password for the private key in the keystore
* @return The specified private key from the keystore
*/
private PrivateKey getPrivateKey(String keystoreFilePath, String keystorePassword, String privateKeyAlias, String privateKeyPassword) {
try {
FileInputStream keyStoreFile = new FileInputStream(keystoreFilePath);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStoreFile, keystorePassword.toCharArray());
return (PrivateKey)keyStore.getKey(privateKeyAlias, privateKeyPassword.toCharArray());
}
catch(KeyStoreException | NoSuchAlgorithmException | IOException | CertificateException | UnrecoverableKeyException e) {
//TODO: Add logging;
}
return null;
}
/**
* Pulls the Issuer DN from a keystore for the specified private key
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore
* @return The Issuer DN for the private key
*/
private String getCertificateIssuerDN(String keystoreFilePath, String keystorePassword, String privateKeyAlias) {
try {
FileInputStream keyStoreFile = new FileInputStream(keystoreFilePath);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStoreFile, keystorePassword.toCharArray());
X509Certificate certificate = (X509Certificate)keyStore.getCertificate(privateKeyAlias);
return certificate.getIssuerDN().getName();
} catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
//TODO: Add logging
}
return null;
}
}
查看 wikipedia article 我猜是因为它实现了 RFC 6979,
This ensures that k is different for each H(m) and unpredictable for attackers who do not know the private key x.
这是为了防止对密钥的攻击。
我已经写了一个class,它的目的是使用服务器上的私钥对一串文本进行签名和base64,然后return签名。每次为同一文本 运行 时,它都会生成不同的签名。它为什么要这样做? 我已通过在我的测试机器上暂时禁用转换来检查正在更改的是签名,而不是 base64 转换的问题。
我的代码:
public class SigningPIP implements SigningPIPInterface {
private static final String SIGNING_ALGORITHM = "SHA1withDSA";
/**
* Provides a signature for a stringified JSON license
* @param license stringified JSON license to be used for signature generation
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore to be used for signing
* @param privateKeyPassword The password for the private key to be used for signing
* @return Properties object containing the base64 encoded signature, algorithm used and certificate DN
*/
@Override
public Properties getLicenseSignature(String license, String keystoreFilePath, String keystorePassword, String privateKeyAlias, String privateKeyPassword) {
PrivateKey privateKey = getPrivateKey(keystoreFilePath, keystorePassword, privateKeyAlias, privateKeyPassword);
Properties licenseSignature = new Properties();
licenseSignature.setProperty("sig_algorithm", SIGNING_ALGORITHM);
licenseSignature.setProperty("cert_DN", getCertificateIssuerDN(keystoreFilePath, keystorePassword, privateKeyAlias));
byte[] licenseByteArray = license.getBytes();
System.out.println(new String(licenseByteArray));
try {
Signature dsa = Signature.getInstance(SIGNING_ALGORITHM);
dsa.initSign(privateKey);
dsa.update(licenseByteArray);
byte[] signature = dsa.sign();
licenseSignature.setProperty("signature_base64", DatatypeConverter.printBase64Binary(signature));
}
catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
//TODO: Add logging
}
return licenseSignature;
}
/**
* Pulls the private key from the specified keystore
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore
* @param privateKeyPassword The password for the private key in the keystore
* @return The specified private key from the keystore
*/
private PrivateKey getPrivateKey(String keystoreFilePath, String keystorePassword, String privateKeyAlias, String privateKeyPassword) {
try {
FileInputStream keyStoreFile = new FileInputStream(keystoreFilePath);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStoreFile, keystorePassword.toCharArray());
return (PrivateKey)keyStore.getKey(privateKeyAlias, privateKeyPassword.toCharArray());
}
catch(KeyStoreException | NoSuchAlgorithmException | IOException | CertificateException | UnrecoverableKeyException e) {
//TODO: Add logging;
}
return null;
}
/**
* Pulls the Issuer DN from a keystore for the specified private key
* @param keystoreFilePath The file path where the keystore can be found
* @param keystorePassword The password to the keystore
* @param privateKeyAlias The alias of the private key in the keystore
* @return The Issuer DN for the private key
*/
private String getCertificateIssuerDN(String keystoreFilePath, String keystorePassword, String privateKeyAlias) {
try {
FileInputStream keyStoreFile = new FileInputStream(keystoreFilePath);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyStoreFile, keystorePassword.toCharArray());
X509Certificate certificate = (X509Certificate)keyStore.getCertificate(privateKeyAlias);
return certificate.getIssuerDN().getName();
} catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
//TODO: Add logging
}
return null;
}
}
查看 wikipedia article 我猜是因为它实现了 RFC 6979,
This ensures that k is different for each H(m) and unpredictable for attackers who do not know the private key x.
这是为了防止对密钥的攻击。