How to find preferred signature algorithm for given private key - java

I'm using BouncyCastle to issue X509 certificates. I've found many code examples where the signature algorithm name is fixed like "SHA256WithRSAEncryption" here:
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
.setProvider(BC).build(privKey);
Is there a method in BouncyCastle or JDK to find the preferred signature algorithm name for given PrivateKey? Something like getPreferredSignatureAlgorithm() here:
// is there method like this?
String signatureAlgorithm = getPreferredSignatureAlgorithm(issuerPrivKey);
JcaX509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(...);
ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm)
.build(issuerPrivKey);
X509Certificate certificate = new JcaX509CertificateConverter()
.setProvider(new BouncyCastleProvider())
.getCertificate(builder.build(signer));

Answering to my own question, I ended up simply implementing my own method like this
private static String signatureAlgorithm(PublicKey pub) {
switch (pub.getAlgorithm()) {
case "EC":
EllipticCurve curve = ((ECPublicKey) pub).getParams().getCurve();
switch (curve.getField().getFieldSize()) {
case 224:
case 256:
return "SHA256withECDSA";
case 384:
return "SHA384withECDSA";
case 521:
return "SHA512withECDSA";
default:
throw new IllegalArgumentException("unknown elliptic curve: " + curve);
}
case "RSA":
return "SHA256WithRSAEncryption";
default:
throw new UnsupportedOperationException("unsupported private key algorithm: " + pub.getAlgorithm());
}
}
Recommended message digest algorithms for EC curves are given in RFC5480 in a table on page 9 (errata).

Related

How can I set the value of TBScertificate::signiture during I generate a X509cert with java?

I'm going to generate a cert and set this value:
TBSCertificate::=SEQUENCE{
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,***<---this one***
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] EXPLICIT Extensions OPTIONAL
}
this is my code,right now I can only set SerialNumber,IssuerDN,NotBefore,NotAfter,SubjectDN,PublicKey,SignatureAlgorithm,:
public X509Certificate generateCert(String[] info, KeyPair keyPair_root,KeyPair keyPair_user) throws InvalidKeyException, NoSuchProviderException, SecurityException, SignatureException {
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X509Certificate cert = null;
certGen.setSerialNumber(new BigInteger(info[8]));
certGen.setIssuerDN(new X509Name(
"CN=huahua, OU=hnu, O=university , C=china"));
certGen.setNotBefore(new Date(Long.parseLong(info[6])));
certGen.setNotAfter(new Date(Long.parseLong(info[7])));
certGen.setSubjectDN(new X509Name("C=" + info[0] + ",OU=" + info[1]
+ ",O=" + info[2] + ",C=" + info[3] + ",L=" + info[4] + ",ST="
+ info[3]));
certGen.setPublicKey(keyPair_user.getPublic());
certGen.setSignatureAlgorithm("SHA1WithRSA");
cert = certGen.generateX509Certificate(keyPair_root.getPrivate(), "BC");
return cert;
}
I will be appreciate it if any one could help me!I have find many solutions but none of them can help me.
TBSCertificate.signature is named poorly, it isn't a signature, just the signature algorithm identifier.
This value is presumably controlled by certGen.setSignatureAlgorithm(...), which you're already using.

sign a SHA-256 hash with DSA in Go

I want to sign a SHA-256 hash with DSA.
Using Java I can write:
Signature sig = Signature.getInstance("SHA256withDSA");
sig.initSign(priKey);
sig.update(new byte[]{1});
byte[] sign = sig.sign();
System.out.println(HexUtil.encodeHexStr(sign));
Using the Go language, I couldn't find any way to resolve it
The only instance of checking a DSAWithSHA256 signature in go is in github.com/avast/apkverifier
case x509.DSAWithSHA256:
hash := sha256.Sum256(signed)
pub := cert.PublicKey.(*dsa.PublicKey)
reqLen := pub.Q.BitLen() / 8
if reqLen > len(hash) {
return fmt.Errorf("Digest algorithm is too short for given DSA parameters.")
}
digest := hash[:reqLen]
dsaSig := new(dsaSignature)
if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
return err
} else if len(rest) != 0 {
return errors.New("x509: trailing data after DSA signature")
}
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
return errors.New("x509: DSA signature contained zero or negative values")
}
if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) {
return errors.New("x509: DSA verification failure")
}
But actually using the signature algorithm is indeed unsupported, for reason illustrated in github.com/grantae/certinfo
Issues:
Unfortunately, OpenSSL uses non-deterministic signing for DSA and ECDSA certificate requests, so running make-certs.sh will not reproduce the same CSRs despite having static keys.
These files have to be kept in-sync manually.
The x509 package does not currently set CertificateRequest.SignatureAlgorithm for DSA CSRs.
Therefore the 'leaf2.csr.text' contains the line 'Signature Algorithm: 0'
instead of 'Signature Algorithm: DSAWithSHA256' to allow the test to pass and indicate that the problem is with x509 and not this package.
Hence its unsupported status in Go crypto/x509 package.

Difference between Go DSA and Java DSA [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Go generates a signature using a DSA private key
Java verifies first step result using the DSA public key
Java should return true, but returns false
package main
import (
"crypto/dsa"
"crypto/rand"
"encoding/asn1"
"encoding/hex"
"fmt"
"golang.org/x/crypto/ssh"
"math/big"
)
func main() {
// a dsa private key
pemData := []byte("-----BEGIN DSA PRIVATE KEY-----\n" +
"MIIBvAIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR\n" +
"+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb\n" +
"+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg\n" +
"UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX\n" +
"TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj\n" +
"rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB\n" +
"TDv+z0kqAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJGrcxHiN2sW8IztEbqrKKiMxp\n" +
"NlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowWWVRdhdFXZlpCyp1gMWqJ11dh\n" +
"3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6y8DPH4/4EBT7KvnVAhR4Vwun\n" +
"Fhu/+4AGaVeMEa814I3dqg==\n" +
"-----END DSA PRIVATE KEY-----")
// parse dsa
p, _ := ssh.ParseRawPrivateKey(pemData)
pp := p.(*dsa.PrivateKey)
// orign data
hashed := []byte{1}
r, s, _ := dsa.Sign(rand.Reader, pp, hashed)
type dsaSignature struct {
R, S *big.Int
}
var ss dsaSignature
ss.S = s
ss.R = r
signatureBytes, _ := asn1.Marshal(ss)
// print sign
fmt.Println(hex.EncodeToString(signatureBytes))
}
Java reads the DSA public key and initialize a signer
Java verify first step sign result
returns false
#Test
public void ttt() throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
// DSA public key
String pubKey = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E\n" +
"AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f\n" +
"6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv\n" +
"8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtc\n" +
"NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwky\n" +
"jMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/h\n" +
"WuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJ\n" +
"GrcxHiN2sW8IztEbqrKKiMxpNlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowW\n" +
"WVRdhdFXZlpCyp1gMWqJ11dh3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6\n" +
"y8DPH4/4EBT7KvnV\n" +
"-----END PUBLIC KEY-----";
String publicKeyPEM = pubKey
.replace("-----BEGIN PUBLIC KEY-----\n", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END PUBLIC KEY-----", "");
byte[] publicEncoded = Base64.decodeBase64(publicKeyPEM);
KeyFactory keyFactory1 = KeyFactory.getInstance("DSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicEncoded);
DSAPublicKey pubKeyy = (DSAPublicKey) keyFactory1.generatePublic(publicKeySpec);
// init signer
Signature sig1 = Signature.getInstance("DSA");
sig1.initVerify(pubKeyy);
sig1.update(new byte[]{1});
// verify first result
System.out.println(sig1.verify(HexUtil.decodeHex("first step result")));
}
i tred to use NONEwithDSA within the Java implementation but it didnt do it
Signature sig1 = Signature.getInstance("NONEwithDSA");
java.security.SignatureException: Data for RawDSA must be exactly 20 bytes long
i tred to use SHA1withDSA within the Java implementation but it didnt do it
Signature sig1 = Signature.getInstance("SHA1withDSA");
returns false
In Java the (Signature) algorithm name DSA is an alias for SHA1withDSA, i.e. the original FIPS186-0 algorithm. This is not the same as the nonstandard 'raw' primitive apparently implemented by Go. NONEwithDSA is indeed the correct Java name for what you want, but the implementation in the 'standard' (SUN) provider is something of a kludge that requires exactly 20 bytes of data, not more or less, because that was the size of the SHA1 hash which was the only standard hash for DSA prior to FIPS186-3.
If you (have or can get and) use the BouncyCastle provider, it does not have this restriction, and should work for your code changed to NONEwithDSA (and either the code or security config modified so that BC is selected as the provider, of course).
If you don't use Bouncy, I think you'll have to code the algorithm yourself; I don't think there's any way to get the SUN implementation to do what you want.
Although it would be better to sign a properly-sized hash as specified in the standard, not raw data, and then you could use the Java providers as specified and designed.

Using openssh public key (ecdsa-sha2-nistp256) with Java Security

Is there a Java library/example to read an openssh format ecdsa public key to a JCE PublicKey in Java? I want to use EC for JWT .
The format I'm trying to read is as per authorized_keys, or Github API (e.g. https://api.github.com/users/davidcarboni/keys): ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK8hPtB72/sfYgNw1WTska2DNOJFx+QhUxuV6OLINSD2ty+6gxcM8yZrvMqWdMePGRb2cGh8L/0bGOk+64IQ/pM=
I've found this answer, which is fine for RSA and DSS:
Using public key from authorized_keys with Java security, and this discussion of the openssh format for ECDSA: https://security.stackexchange.com/questions/129910/ecdsa-why-do-ssh-keygen-and-java-generated-public-keys-have-different-sizes
However I'm getting lost trying to adapt the RSS/DSA code for ECDSA - I'm not sure how to set up an ECPublicKeySpec. It needs ECPoint, EllipticCurve, ECParameterSpec, ECField. The openssh format only contains two integers, which makes sense for ECPoint, but I don't know how to set up the rest.
I've been poking around a bunch of libraries, including jsch, sshj, ssh-tools and good old Bouncycastle. The closest I have is:
com.jcraft.jsch.KeyPair load = com.jcraft.jsch.KeyPair.load(jsch, null, bytes[openSshKey]);
Which loads the key fine, but doesn't get me to a JCE PublicKey - just a byte[] getPublicKeyBlob() method.
Am I missing something obvious?
I've found a way to do this using Bouncycastle (but would like to find a JCE way).
Adapting the code from Using public key from authorized_keys with Java security, and refering to RFC 5656, section 3.1, the following block added to decodePublicKey will parse the single BigInt value Q, which is "the public key encoded from an elliptic curve point":
if (type.startsWith("ecdsa-sha2-") &&
(type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {
// Based on RFC 5656, section 3.1 (https://www.rfc-editor.org/rfc/rfc5656#section-3.1)
// The string [identifier] is the identifier of the elliptic curve
// domain parameters. The format of this string is specified in
// Section 6.1 (https://www.rfc-editor.org/rfc/rfc5656#section-6.1).
// Information on the REQUIRED and RECOMMENDED sets of
// elliptic curve domain parameters for use with this algorithm can be
// found in Section 10 (https://www.rfc-editor.org/rfc/rfc5656#section-10).
String identifier = decodeType();
if (!type.endsWith(identifier)) {
throw new IllegalArgumentException("Invalid identifier " + identifier + " for key type " + type + ".");
}
// Q is the public key encoded from an elliptic curve point into an
// octet string as defined in Section 2.3.3 of [SEC1];
// (https://www.rfc-editor.org/rfc/rfc5656#ref-SEC1)
// point compression MAY be used.
BigInteger q = decodeBigInt();
ECPublicKey keyBC = getKeyBC(q, identifier);
return keyBC;
}
The solution I've found for getting from Q to an ECPublicKey is the following, using the Bouncycastle API (credit to Generate ECPublicKey from ECPrivateKey for providing the starting point):
ECPublicKey getKeyBC(BigInteger q, String identifier) {
// https://stackoverflow.com/questions/42639620/generate-ecpublickey-from-ecprivatekey
try {
// This only works with the Bouncycastle library:
Security.addProvider(new BouncyCastleProvider());
// http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269#SupportedCurves(ECDSAandECGOST)-NIST(aliasesforSECcurves)
String name = identifier.replace("nist", "sec") + "r1";
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
ECPoint point = ecSpec.getCurve().decodePoint(q.toByteArray());
ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);
ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(pubSpec);
return publicKey;
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
That gets you from an openssh format elliptic curve public key (ssh-keygen -t ecdsa -b [256|384|521]) to a JCE ECPublicKey.
For completeness, here's the code I've gone with. It's nearly-pure JCE, with a sprinkling of Bouncycastle inside helper methods (this updates the example code in Using public key from authorized_keys with Java security):
...
} else if (type.startsWith("ecdsa-sha2-") &&
(type.endsWith("nistp256") || type.endsWith("nistp384") || type.endsWith("nistp521"))) {
// Based on RFC 5656, section 3.1 (https://tools.ietf.org/html/rfc5656#section-3.1)
String identifier = decodeType();
BigInteger q = decodeBigInt();
ECPoint ecPoint = getECPoint(q, identifier);
ECParameterSpec ecParameterSpec = getECParameterSpec(identifier);
ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
return KeyFactory.getInstance("EC").generatePublic(spec);
} ...
/**
* Provides a means to get from a parsed Q value to the X and Y point values.
* that can be used to create and ECPoint compatible with ECPublicKeySpec.
*
* #param q According to RFC 5656:
* "Q is the public key encoded from an elliptic curve point into an octet string"
* #param identifier According to RFC 5656:
* "The string [identifier] is the identifier of the elliptic curve domain parameters."
* #return An ECPoint suitable for creating a JCE ECPublicKeySpec.
*/
ECPoint getECPoint(BigInteger q, String identifier) {
String name = identifier.replace("nist", "sec") + "r1";
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
org.bouncycastle.math.ec.ECPoint point = ecSpec.getCurve().decodePoint(q.toByteArray());
BigInteger x = point.getAffineXCoord().toBigInteger();
BigInteger y = point.getAffineYCoord().toBigInteger();
System.out.println("BC x = " + x);
System.out.println("BC y = " + y);
return new ECPoint(x, y);
}
/**
* Gets the curve parameters for the given key type identifier.
*
* #param identifier According to RFC 5656:
* "The string [identifier] is the identifier of the elliptic curve domain parameters."
* #return An ECParameterSpec suitable for creating a JCE ECPublicKeySpec.
*/
ECParameterSpec getECParameterSpec(String identifier) {
try {
// http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269#SupportedCurves(ECDSAandECGOST)-NIST(aliasesforSECcurves)
String name = identifier.replace("nist", "sec") + "r1";
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
parameters.init(new ECGenParameterSpec(name));
return parameters.getParameterSpec(ECParameterSpec.class);
} catch (InvalidParameterSpecException | NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Unable to get parameter spec for identifier " + identifier, e);
}
}

PDF document verify exception

When I try to verify signed pdf document I get RuntimeException:
Exception in thread "main" java.lang.RuntimeException: algorithm identifier 1.2.398.3.10.1.1.1.1 in key not recognised
at org.bouncycastle.jce.provider.JDKKeyFactory.createPublicKeyFromPublicKeyInfo(Unknown Source)
at org.bouncycastle.jce.provider.X509CertificateObject.getPublicKey(Unknown Source)
at com.itextpdf.text.pdf.PdfPKCS7.<init>(PdfPKCS7.java:582)
at com.itextpdf.text.pdf.PdfPKCS7.<init>(PdfPKCS7.java:421)
at com.itextpdf.text.pdf.AcroFields.verifySignature(AcroFields.java:2307)
at Main.verifyPDF(Main.java:62)
at Main.main(Main.java:90)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
my verification piece of code looks like this:
public static boolean verifyPDF(String fileToVerify, KeyStore trustedStore, CRL crl) throws IOException, GeneralSecurityException {
List<CRL> crls = null;
if (crl != null) {
crls = new ArrayList<CRL>(1);
crls.add(crl);
}
boolean result = false;
PdfReader checker = new PdfReader(fileToVerify);
AcroFields af = checker.getAcroFields();
ArrayList<String> names = af.getSignatureNames();
for (int k = 0; k < names.size(); ++k) {
String name = (String) names.get(k);
System.out.println("Signature: " + name);
com.itextpdf.text.pdf.PdfPKCS7 pk = af.verifySignature(name, "KALKAN");
result = pk.verify();
System.out.println("Signer certificate DN: " + pk.getSigningCertificate().getSubjectDN());
Calendar cal = pk.getSignDate();
X509Certificate pkc[] = (X509Certificate[]) pk.getSignCertificateChain();
System.out.println("Document modified: " + !result);
Object fails[] = PdfPKCS7.verifyCertificates(pkc, trustedStore, crls, cal);
if (fails == null)
System.out.println("Certificates verified against the KeyStore");
else
System.out.println("Certificate failed: " + fails[1]);
}
return result;
}
the exception occurs on this string:
com.itextpdf.text.pdf.PdfPKCS7 pk = af.verifySignature(name, "KALKAN");
I use patched iText library. I had to patch it because there was no algorithm like ECGOST34310 and I just added it. Signing is performed in usual way, there is no problem with it.
Please help!
Thanks.
At first glance that OID 1.2.398.3.10.1.1.1.1 seems to be defined by a Kazakh authority (cf. this page), related to GOST 34,310-2.004 represented by the parent OID, without having yet been included in the mainstream BouncyCastle distribution, cf. the BouncyCastle specifications.
Thus, just like you have extended iText to be able to sign using GOST 34,310-2.004
I use patched iText library. I had to patch it because there was no algorithm like ECGOST34310 and I just added it.
you have to extend it (or in this case more exactly the crypto library BouncyCastle used by iText) to be able to verify signatures using GOST 34,310-2.004. Maybe, though, someone else already has done that and comes forth to help?
By the way, it would be great if you shared the results as soon as they work.
That all been said I am not aware of GOST being mentioned in the context of either ISO 32000-1 or PAdES integrated PDF signatures. Using GOST for PDF signatures, therefore, will likely result in very limited interoperability.

Categories