Difference between Go DSA and Java DSA [closed] - java

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.

Related

How to find preferred signature algorithm for given private key

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).

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.

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);
}
}

ECC decryption corresponding to ECIES from Bouncy Castle

I am trying to decrypt files in Ruby which were encrypted with ECIES in Java using
BouncyCastle as JCE Provider
ECIES Algorithm
P-384 named curve
In Java the encryption was done using Cipher.doFinal() and then written to a file. A test case implements encryption and decryption.
In Ruby all i can find is the OpenSSL::PKey::EC implementation, but this does not seem to have encryption or decryption but only Verify and Sign.
Does anyone know of a way to do this in Ruby? My worst case scenario would be to simply call the Java decryption routing from Ruby, but that really isn't the way i want to go.
ECC decryption corresponding to ECIES from Bouncy Castle
ECIES is Shoup's Integrated Encryption System. Its much more than simply EC encrypt and decrypt. You can find Shoup's paper at A Proposal for an ISO Standard for Public Key Encryption.
In Ruby all i can find is the OpenSSL::PKey::EC implementation
OpenSSL does not have an ECIES implementation. It does not have an implementation of Abdalla, Bellare, and Rogaway's DHAES, either.
My worst case scenario would be to simply call the Java decryption routing from Ruby, but that really isn't the way i want to go.
You will probably have to shell out to Java.
Related (sorry about wandering into C++): Crypto++ also has ECIES. However, the Bouncy Castle and Crypto++ implementations do not inter-operate out of the box. See Problem with the way gfpcrypt HMAC's the encoding parameters' length in DHAES_MODE on the Crypto++ mailing list.
The interop issue is a difference in the size of a label that gets hashed as a security parameter when using DHAES_MODE. Crypto++ uses a 8-octet label, while Bouncy Castle uses a 4-octet label. I don't recall who was/is correct.
At the bottom of the Crypto++ page on Elliptic Curve Integrated Encryption Scheme, there's a patch available based on Jessie Wilson's comments at cryptopp-ecies-bc.zip. Download it, apply it, and use the ECIES_BC class rather than the ECIES class.
Here's the essence of the patch. BC_COMPAT is a template parameter.
diff --git a/gfpcrypt.h b/gfpcrypt.h
index 7af993f..18ea104 100644
--- a/gfpcrypt.h
+++ b/gfpcrypt.h
## -408,7 +408,9 ## CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_GFP<DL_GroupParameters_DSA>;
CRYPTOPP_DLL_TEMPLATE_CLASS DL_PrivateKey_WithSignaturePairwiseConsistencyTest<DL_PrivateKey_GFP<DL_GroupParameters_DSA>, DSA2<SHA> >;
//! the XOR encryption method, for use with DL-based cryptosystems
-template <class MAC, bool DHAES_MODE>
+// Set BC_COMPAT=true if interop'ing with Bouncy Castle. Thanks to Jesse Wilson and Daniele Perito.
+// See https://groups.google.com/d/msg/cryptopp-users/vR8GSL8wxPA/Bf9koUDyZ88J.
+template <class MAC, bool DHAES_MODE, bool BC_COMPAT = false>
class DL_EncryptionAlgorithm_Xor : public DL_SymmetricEncryptionAlgorithm
{
public:
## -442,9 +444,17 ## public:
mac.Update(encodingParameters.begin(), encodingParameters.size());
if (DHAES_MODE)
{
- byte L[8] = {0,0,0,0};
- PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size()));
- mac.Update(L, 8);
+ if (BC_COMPAT) {
+ byte L[4];
+ PutWord(false, BIG_ENDIAN_ORDER, L, word32(8 * encodingParameters.size()));
+ mac.Update(L, 4);
+ }
+ else
+ {
+ byte L[8] = {0,0,0,0};
+ PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size()));
+ mac.Update(L, 8);
+ }
}
mac.Final(ciphertext + plaintextLength);
}
## -471,9 +481,17 ## public:
mac.Update(encodingParameters.begin(), encodingParameters.size());
if (DHAES_MODE)
{
- byte L[8] = {0,0,0,0};
- PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size()));
- mac.Update(L, 8);
+ if (BC_COMPAT) {
+ byte L[4];
+ PutWord(false, BIG_ENDIAN_ORDER, L, word32(8 * encodingParameters.size()));
+ mac.Update(L, 4);
+ }
+ else
+ {
+ byte L[8] = {0,0,0,0};
+ PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size()));
+ mac.Update(L, 8);
+ }
}
if (!mac.Verify(ciphertext + plaintextLength))
return DecodingResult();

Categories