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();
Related
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.
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.
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);
}
}
I want to encrypt some data between a c++ client and a java server. Searching the web I found crypto++ to use on the client and on the java side to use the KeyAgreement built in java class(btw, I already have bouncycastle jar in my project, I can use it if it's somehow better than the built in one).
I was able to perform DH on on every side with itself as an example, but due to key sizes I cannot make them to agree on a secret key.
My C++ code:
// g++ -g3 -ggdb -O0 -I. -I/usr/include/cryptopp dh-agree.cpp -o dh-agree.exe -lcryptopp -lpthread
// g++ -g -O2 -I. -I/usr/include/cryptopp dh-agree.cpp -o dh-agree.exe -lcryptopp -lpthread
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
using std::string;
#include <stdexcept>
using std::runtime_error;
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;
#include "integer.h"
using CryptoPP::Integer;
#include "nbtheory.h"
using CryptoPP::ModularExponentiation;
#include "dh.h"
using CryptoPP::DH;
#include "secblock.h"
using CryptoPP::SecByteBlock;
#include <hex.h>
using CryptoPP::HexEncoder;
#include <filters.h>
using CryptoPP::StringSink;
int main(int argc, char** argv)
{
try
{
// RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup
// http://tools.ietf.org/html/rfc5114#section-2.1
Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
"9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0"
"13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
"98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0"
"A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
"DF1FB2BC2E4A4371");
Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
"D6406CFF14266D31266FEA1E5C41564B777E690F5504F213"
"160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
"909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A"
"D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
"855E6EEB22B3B2E5");
Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353");
// Schnorr Group primes are of the form p = rq + 1, p and q prime. They
// provide a subgroup order. In the case of 1024-bit MODP Group, the
// security level is 80 bits (based on the 160-bit prime order subgroup).
// For a compare/contrast of using the maximum security level, see
// dh-agree.zip. Also see http://www.cryptopp.com/wiki/Diffie-Hellman
// and http://www.cryptopp.com/wiki/Security_level .
DH dh;
AutoSeededRandomPool rnd;
dh.AccessGroupParameters().Initialize(p, q, g);
if(!dh.GetGroupParameters().ValidateGroup(rnd, 3))
throw runtime_error("Failed to validate prime and generator");
size_t count = 0;
p = dh.GetGroupParameters().GetModulus();
q = dh.GetGroupParameters().GetSubgroupOrder();
g = dh.GetGroupParameters().GetGenerator();
// http://groups.google.com/group/sci.crypt/browse_thread/thread/7dc7eeb04a09f0ce
Integer v = ModularExponentiation(g, q, p);
if(v != Integer::One())
throw runtime_error("Failed to verify order of the subgroup");
//////////////////////////////////////////////////////////////
SecByteBlock priv(dh.PrivateKeyLength());
SecByteBlock pub(dh.PublicKeyLength());
dh.GenerateKeyPair(rnd, priv, pub);
printf("lengths: %d %d\n",dh.PrivateKeyLength(),dh.PublicKeyLength());
byte* pubData = pub.data();
for(int j = 0; j < pub.size()-1; j++)
printf("%02X:", pubData[j]);
printf("%02X\n", pubData[pub.size()-1]);
// Send pub to Java
sendData(pub.data(),pub.size());
// Read pubB from Java
byte pubBbytes[10000];
int n = readData(pubBbytes,sizeof(pubBbytes));
SecByteBlock pubB(pubBbytes,n);
//////////////////////////////////////////////////////////////
SecByteBlock sharedA(dh.AgreedValueLength());
if(!dh.Agree(sharedA, priv, pubB))
throw runtime_error("Failed to reach shared secret (1A)");
//////////////////////////////////////////////////////////////
Integer a;
a.Decode(sharedA.BytePtr(), sharedA.SizeInBytes());
cout << "Shared secret (A): " << std::hex << a << endl;
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
return -2;
}
catch(const std::exception& e)
{
cerr << e.what() << endl;
return -1;
}
return 0;
}
Server code in Java:
package test;
/*
* Copyright (c) 1997, 2001, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.DHParameterSpec;
/**
* This program executes the Diffie-Hellman key agreement protocol
* between 2 parties: Alice and Bob.
*
* By default, preconfigured parameters (1024-bit prime modulus and base
* generator used by SKIP) are used.
* If this program is called with the "-gen" option, a new set of
* parameters is created.
*/
public class DHKeyAgreement2 {
private DHKeyAgreement2() {}
public final static String sP = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" +
"9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" +
"13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" +
"98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" +
"A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" +
"DF1FB2BC2E4A4371";
public final static String sG = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" +
"D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" +
"160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" +
"909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" +
"D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" +
"855E6EEB22B3B2E5";
public static void main(String argv[]) {
try {
DHKeyAgreement2 keyAgree = new DHKeyAgreement2();
keyAgree.run();
} catch (Exception e) {
System.err.println("Error: " + e);
System.exit(1);
}
}
private void run() throws Exception {
DHParameterSpec dhSkipParamSpec;
// use some pre-generated, default DH parameters
System.out.println("Using SKIP Diffie-Hellman parameters");
BigInteger p = new BigInteger(sP,16);
BigInteger g = new BigInteger(sG,16);
System.out.println("P " + sP.length() + " is: " + toHexString(p.toByteArray()));
System.out.println("G " + sG.length() + " is: " + toHexString(g.toByteArray()));
dhSkipParamSpec = new DHParameterSpec(p,g);
/*
* Alice creates her own DH key pair, using the DH parameters from
* above
*/
System.out.println("ALICE: Generate DH keypair ...");
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
aliceKpairGen.initialize(dhSkipParamSpec);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();
// Alice creates and initializes her DH KeyAgreement object
System.out.println("ALICE: Initialization ...");
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKpair.getPrivate());
// Alice encodes her public key, and sends it over to Bob.
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
byte[] alicePrivKeyEnc = aliceKpair.getPrivate().getEncoded();
System.out.println("Alice pub key size: " + alicePubKeyEnc.length);
System.out.println("Alice pub key: " + toHexString(alicePubKeyEnc));
System.out.println("Alice priv key size: " + alicePrivKeyEnc.length);
System.out.println("Alice priv key: " + toHexString(alicePrivKeyEnc));
ServerSocket ss = new ServerSocket(5454);
Socket accept = ss.accept();
byte[] bobPubKeyEnc = readByteArrayFromSocket(accept);
System.out.println("Read bob's pubkey: " + toHexString(bobPubKeyEnc));
sendByteArrayToBob(alicePubKeyEnc);
System.out.println("Sent Alice's pubkey");
/*
* Alice uses Bob's public key for the first (and only) phase
* of her version of the DH
* protocol.
* Before she can do so, she has to instantiate a DH public key
* from Bob's encoded key material.
*/
KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
System.out.println("ALICE: Execute PHASE1 ...");
aliceKeyAgree.doPhase(bobPubKey, true);
/*
* At this stage, both Alice and Bob have completed the DH key
* agreement protocol.
* Both generate the (same) shared secret.
*/
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
int aliceLen = aliceSharedSecret.length;
System.out.println("Alice secret: " +
toHexString(aliceSharedSecret));
}
}
My output from C++:
lengths: 20 128
77:61:FD:93:D9:23:38:41:6D:B0:9B:F8:7A:FB:CE:CA:0E:DF:7D:0A:95:F6:B4:55:FF:64:32:03:2C:B5:9C:47:05:06:FF:1B:72:F3:C6:8A:91:68:13:98:DE:56:0C:D6:02:30:C2:4B:DB:AD:0A:B3:7D:2A:7E:DD:13:A8:7C:97:4A:46:79:6A:85:C7:5B:79:29:D8:E5:2B:F4:59:21:B3:29:EA:6A:2F:FB:70:A1:C8:FD:5C:31:E1:92:A9:B0:67:74:65:3D:C1:1B:33:4B:DE:1C:EB:1E:A1:3A:36:29:0F:DF:A2:FA:5D:DA:69:DC:6D:00:D7:76:95:3A:FD:7D:76
sent 128 bytes, remaining 0
Failed to reach shared secret (1A)
And output from Java:
Using SKIP Diffie-Hellman parameters
P 256 is: 00:B1:0B:8F:96:A0:80:E0:1D:DE:92:DE:5E:AE:5D:54:EC:52:C9:9F:BC:FB:06:A3:C6:9A:6A:9D:CA:52:D2:3B:61:60:73:E2:86:75:A2:3D:18:98:38:EF:1E:2E:E6:52:C0:13:EC:B4:AE:A9:06:11:23:24:97:5C:3C:D4:9B:83:BF:AC:CB:DD:7D:90:C4:BD:70:98:48:8E:9C:21:9A:73:72:4E:FF:D6:FA:E5:64:47:38:FA:A3:1A:4F:F5:5B:CC:C0:A1:51:AF:5F:0D:C8:B4:BD:45:BF:37:DF:36:5C:1A:65:E6:8C:FD:A7:6D:4D:A7:08:DF:1F:B2:BC:2E:4A:43:71
G 256 is: 00:A4:D1:CB:D5:C3:FD:34:12:67:65:A4:42:EF:B9:99:05:F8:10:4D:D2:58:AC:50:7F:D6:40:6C:FF:14:26:6D:31:26:6F:EA:1E:5C:41:56:4B:77:7E:69:0F:55:04:F2:13:16:02:17:B4:B0:1B:88:6A:5E:91:54:7F:9E:27:49:F4:D7:FB:D7:D3:B9:A9:2E:E1:90:9D:0D:22:63:F8:0A:76:A6:A2:4C:08:7A:09:1F:53:1D:BF:0A:01:69:B6:A2:8A:D6:62:A4:D1:8E:73:AF:A3:2D:77:9D:59:18:D0:8B:C8:85:8F:4D:CE:F9:7C:2A:24:85:5E:6E:EB:22:B3:B2:E5
ALICE: Generate DH keypair ...
ALICE: Initialization ...
Alice pub key size: 426
Alice pub key: 30:82:01:A6:30:82:01:1B:06:09:2A:86:48:86:F7:0D:01:03:01:30:82:01:0C:02:81:81:00:B1:0B:8F:96:A0:80:E0:1D:DE:92:DE:5E:AE:5D:54:EC:52:C9:9F:BC:FB:06:A3:C6:9A:6A:9D:CA:52:D2:3B:61:60:73:E2:86:75:A2:3D:18:98:38:EF:1E:2E:E6:52:C0:13:EC:B4:AE:A9:06:11:23:24:97:5C:3C:D4:9B:83:BF:AC:CB:DD:7D:90:C4:BD:70:98:48:8E:9C:21:9A:73:72:4E:FF:D6:FA:E5:64:47:38:FA:A3:1A:4F:F5:5B:CC:C0:A1:51:AF:5F:0D:C8:B4:BD:45:BF:37:DF:36:5C:1A:65:E6:8C:FD:A7:6D:4D:A7:08:DF:1F:B2:BC:2E:4A:43:71:02:81:81:00:A4:D1:CB:D5:C3:FD:34:12:67:65:A4:42:EF:B9:99:05:F8:10:4D:D2:58:AC:50:7F:D6:40:6C:FF:14:26:6D:31:26:6F:EA:1E:5C:41:56:4B:77:7E:69:0F:55:04:F2:13:16:02:17:B4:B0:1B:88:6A:5E:91:54:7F:9E:27:49:F4:D7:FB:D7:D3:B9:A9:2E:E1:90:9D:0D:22:63:F8:0A:76:A6:A2:4C:08:7A:09:1F:53:1D:BF:0A:01:69:B6:A2:8A:D6:62:A4:D1:8E:73:AF:A3:2D:77:9D:59:18:D0:8B:C8:85:8F:4D:CE:F9:7C:2A:24:85:5E:6E:EB:22:B3:B2:E5:02:02:02:00:03:81:84:00:02:81:80:25:E7:BD:24:57:C9:59:EE:E0:EC:7A:F3:D6:22:1C:84:68:52:D9:19:40:5F:1B:C6:CB:A9:3A:42:BF:AB:3C:C2:EC:6F:BC:F9:F9:B2:70:AC:A2:E5:CE:36:FC:06:4F:2D:B3:4F:B5:25:D9:59:AD:D6:AD:B6:17:FA:09:76:AE:89:99:91:52:9B:E0:10:1D:9F:AC:50:AF:02:6D:25:F6:E8:DD:B8:6C:51:17:44:59:98:52:4B:E9:75:E1:D1:26:FE:EA:73:EF:C7:89:7F:70:A8:ED:6F:57:28:A4:0F:1B:F8:21:7D:A5:A2:59:B9:74:42:42:45:BA:EC:E2:53:B3:C4
Alice priv key size: 362
Alice priv key: 30:82:01:66:02:01:00:30:82:01:1B:06:09:2A:86:48:86:F7:0D:01:03:01:30:82:01:0C:02:81:81:00:B1:0B:8F:96:A0:80:E0:1D:DE:92:DE:5E:AE:5D:54:EC:52:C9:9F:BC:FB:06:A3:C6:9A:6A:9D:CA:52:D2:3B:61:60:73:E2:86:75:A2:3D:18:98:38:EF:1E:2E:E6:52:C0:13:EC:B4:AE:A9:06:11:23:24:97:5C:3C:D4:9B:83:BF:AC:CB:DD:7D:90:C4:BD:70:98:48:8E:9C:21:9A:73:72:4E:FF:D6:FA:E5:64:47:38:FA:A3:1A:4F:F5:5B:CC:C0:A1:51:AF:5F:0D:C8:B4:BD:45:BF:37:DF:36:5C:1A:65:E6:8C:FD:A7:6D:4D:A7:08:DF:1F:B2:BC:2E:4A:43:71:02:81:81:00:A4:D1:CB:D5:C3:FD:34:12:67:65:A4:42:EF:B9:99:05:F8:10:4D:D2:58:AC:50:7F:D6:40:6C:FF:14:26:6D:31:26:6F:EA:1E:5C:41:56:4B:77:7E:69:0F:55:04:F2:13:16:02:17:B4:B0:1B:88:6A:5E:91:54:7F:9E:27:49:F4:D7:FB:D7:D3:B9:A9:2E:E1:90:9D:0D:22:63:F8:0A:76:A6:A2:4C:08:7A:09:1F:53:1D:BF:0A:01:69:B6:A2:8A:D6:62:A4:D1:8E:73:AF:A3:2D:77:9D:59:18:D0:8B:C8:85:8F:4D:CE:F9:7C:2A:24:85:5E:6E:EB:22:B3:B2:E5:02:02:02:00:04:42:02:40:3C:16:B6:8F:73:CD:9D:0F:19:D5:A7:54:61:FC:A9:AF:3E:79:78:B8:5E:3E:3D:58:52:2F:95:5E:0D:3F:E0:19:92:17:22:B4:06:9A:E4:ED:9D:55:54:3F:1F:DE:20:36:31:5A:AC:58:FB:A3:C2:7E:65:31:A4:F0:43:37:A2:37
Read 128 bytes
Read bob's pubkey: 77:61:FD:93:D9:23:38:41:6D:B0:9B:F8:7A:FB:CE:CA:0E:DF:7D:0A:95:F6:B4:55:FF:64:32:03:2C:B5:9C:47:05:06:FF:1B:72:F3:C6:8A:91:68:13:98:DE:56:0C:D6:02:30:C2:4B:DB:AD:0A:B3:7D:2A:7E:DD:13:A8:7C:97:4A:46:79:6A:85:C7:5B:79:29:D8:E5:2B:F4:59:21:B3:29:EA:6A:2F:FB:70:A1:C8:FD:5C:31:E1:92:A9:B0:67:74:65:3D:C1:1B:33:4B:DE:1C:EB:1E:A1:3A:36:29:0F:DF:A2:FA:5D:DA:69:DC:6D:00:D7:76:95:3A:FD:7D:76
Sent Alice's pubkey
Error: java.security.spec.InvalidKeySpecException: Inappropriate key specification
Seems like altough I use the same G and P in both programs, the keys generated in C++ are 128\20 byte long (public\private) while in java they are 426\362 byte long. I was wondering where I can set the desired key length in crypto++ but yet to discover that. Also I removed some helper function from the codes above, so I guess copy-paste and trying to compile them won't work. If anyone will actually want to do that I will submit my complete code.
Thanks
Has anyone implemented a mod_auth_tkt cookie generation using Java?
I'm stuck on how to generate the iptstamp (can be done in PHP using the pack function) and hextimestamp in Java.
The algorithm for generating the cookie is below:
cookie := digest + hextimestamp + user_id + '!' + token_list + '!' + user_data
digest := MD5(digest0 + key)
digest0 := MD5(iptstamp + key + user_id + '\0' + token_list + '\0' + user_data)
iptstamp is a 8 bytes long byte array, bytes 0-3 are filled with client's IP address as a binary number in network byte order, bytes 4-7 are filled with timestamp as a binary number in network byte order.
hextimestamp is 8 character long hexadecimal number expressing timestamp used in iptstamp.
token_list is an optional comma-separated list of access tokens for this user.
This list is checked if TKTAuthToken is set for a particular area.
user_data is optional
Simply convert the IP Address to 4 bytes and append the timestamp.
iptstamp = ip_chars + ts_chars
http://code.cmlenz.net/diva/changeset/173/branches
http://www.mail-archive.com/modauthtkt-users#lists.sourceforge.net/msg00003.html
Using the following to get network byte order.
ByteBuffer bb = ByteBuffer.allocate(4096);
bb.order(ByteOrder.BIG_ENDIAN);
See
Network Order short (Java)