I’m trying to use web push notifications with the web push protocol in my app. In order to use the Push API with VAPID I need an applicationServerKey.
The PushManager subscribe method takes a VAPID key (public key alone) as a parameter and will give a subscription end point and keys to push messages.
To generate VAPID keys, I have been using node.js (google web-push package) and openssl till now. But in my use case VAPID keys should be generated within Java and passed to JavaScript to subscribe from the browser.
I am trying with the code below in Java to generate VAPID keys. I am able to create keys successfully but when I pass the generated public key (base64-encoded string), the subscribe method returns an error saying:
Unable to register service worker. DOMException: Failed to execute
'subscribe' on 'PushManager': The provided applicationServerKey is not
valid..
Please help me resolve this issue. Below is my Java code:
ECNamedCurveParameterSpec parameterSpec =
ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPairGenerator =
KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair serverKey = keyPairGenerator.generateKeyPair();
PrivateKey priv = serverKey.getPrivate();
PublicKey pub = serverKey.getPublic();`
System.out.println(Base64.toBase64String(pub.getEncoded()));
Please refer below link for answer from MartijnDwars.
https://github.com/web-push-libs/webpush-java/issues/30
you can use Utils.savePublicKey to convert your Java-generated
PublicKey to a byte[]. This byte[] is then passed to the
PushManager.subscribe method.
It may be more convenient to base64 encode the byte[] in Java and
base64 decode the string in JavaScript. For example, after generating
the keypair in Java:
KeyPair keyPair = generateKeyPair();
byte[] publicKey = Utils.savePublicKey((ECPublicKey) keyPair.getPublic());
String publicKeyBase64 = BaseEncoding.base64Url().encode(publicKey);
System.out.println("PublicKey = " + publicKeyBase64);
// PublicKey = BPf36QAqZNNvvnl9kkpTDerXUOt6Nm6P4x9GEvmFVFKgVyCVWy24KUTs6wLQtbV2Ug81utbNnx86_vZzXDyrl88=
Then in JavaScript:
function subscribe() {
const publicKey = base64UrlToUint8Array('BPf36QAqZNNvvnl9kkpTDerXUOt6Nm6P4x9GEvmFVFKgVyCVWy24KUTs6wLQtbV2Ug81utbNnx86_vZzXDyrl88=');
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: publicKey
})
.then(function (subscription) {
return sendSubscriptionToServer(subscription);
})
.catch(function (e) {
if (Notification.permission === 'denied') {
console.warn('Permission for Notifications was denied');
} else {
console.error('Unable to subscribe to push.', e);
}
});
});
}
function base64UrlToUint8Array(base64UrlData) {
const padding = '='.repeat((4 - base64UrlData.length % 4) % 4);
const base64 = (base64UrlData + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = atob(base64);
const buffer = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
buffer[i] = rawData.charCodeAt(i);
}
return buffer;
}
Having spent hours on this, I thought I would share the solution I worked out from gleaning several web sites, avoiding using Bouncy Castle which generated lots of other issues. Try the below:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
keyPairGenerator.initialize(spec, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPoint ecp = publicKey.getW();
byte[] x = ecp.getAffineX().toByteArray();
byte[] y = ecp.getAffineY().toByteArray();
// Convert 04 to bytes
String s= "04";
int len = s.length();
byte[] firstBit = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
firstBit[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) +
Character.digit(s.charAt(i+1), 16));
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
outputStream.write(firstBit);
outputStream.write(x);
outputStream.write(y);
publicKeyBytes = outputStream.toByteArray( );
Base64 encoder = new Base64(-1,null,true);
byte[] encodedBytes = encoder.encode(publicKeyBytes);
String publicKeyBase64 = new String(encodedBytes, StandardCharsets.UTF_8);
With bountycastle you can generate vapid keys with this code :
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.getUrlEncoder().withoutPadding().encodeToString(publicKey.getQ().getEncoded(false));
System.out.println(publicKeyString);
String privateKeyString = Base64.getUrlEncoder().withoutPadding().encodeToString(privateKey.getD().toByteArray());
System.out.println(privateKeyString);
This is a solution with poor Java 8 that handle the random length of the BigIntegers.
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "EC" );
keyPairGenerator.initialize( new ECGenParameterSpec( "secp256r1" ), new SecureRandom() );
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey)keyPair.getPublic();
ECPoint ecp = publicKey.getW();
byte[] applicationServerKey = new byte[65];
applicationServerKey[0] = 4;
// copy getAffineX() to the target
byte[] affine = ecp.getAffineX().toByteArray(); // typical 31 to 33 bytes
int pos = 1;
int off, length;
off = affine.length - 32;
if( off >= 0 ) {
// there are leading zero values which we cut
length = 32;
} else {
pos -= off;
length = 32 + off;
off = 0;
}
System.arraycopy( affine, off, applicationServerKey, pos, length );
// copy getAffineY() to the target
affine = ecp.getAffineY().toByteArray(); // typical 31 to 33 bytes
pos = 33;
off = affine.length - 32;
if( off >= 0 ) {
// there are leading zero values which we cut
length = 32;
} else {
pos -= off;
length = 32 + off;
off = 0;
}
System.arraycopy( affine, off, applicationServerKey, pos, length );
return Base64.getEncoder().encodeToString( applicationServerKey );
or more simpler with the Java buildin encoding:
ECPublicKey publicKey = ...
byte[] keyBytes = publicKey.getEncoded();
// 26 -> X509 overhead, length ever 91, results in 65 bytes
keyBytes = Arrays.copyOfRange( keyBytes, 26, 91 );
return this.applicationServerKey = Base64.getEncoder().encodeToString( keyBytes );
Related
I need to implement encrpytion in NodeJS using public key RSA from java language to encrypt HttpPost params.
public class RSAUtil {
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes = Base64.decodeBase64(key.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
public static String encrypt(String publicKeyFrom, byte[] waitEncrypt) throws Exception {
String publicK = publicKeyFrom.replaceAll("\\r\\n", "");
PublicKey publicKey = getPublicKey(publicK);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(1, publicKey);
int inputLen = waitEncrypt.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
for (int i = 0; inputLen - offSet > 0; offSet = i * 117) {
byte[] cache;
if (inputLen - offSet > 117) {
cache = cipher.doFinal(waitEncrypt, offSet, 117);
} else {
cache = cipher.doFinal(waitEncrypt, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
++i;
}
byte[] encryptedData = out.toByteArray();
out.close();
return Base64.encodeBase64String(encryptedData);
}
}
And here is how to use it in Java.
String PUBLIC_KEY = "MIIBIjANxxxxxxxxlGqQIDAQAB";
String reqParams = "{\"code\":\"A12345\"}";
String encryptedParams = RSAUtil.encrypt(PUBLIC_KEY, reqParams.getBytes());
And I already tried to translate the code into NodeJS as follow.
const Crypto = require('crypto');
const encryptParams = function (params, publicKey) {
const buffer = Buffer.from(params)
const encrypted = Crypto.publicEncrypt(publicKey, buffer)
const ecryptedParams = encrypted.toString('base64');
return ecryptedParams;
}
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIIBIjANxxxxxxxxlGqQIDAQAB
-----END PUBLIC KEY-----`;
const reqParams = { code: 'A12345' };
const encryptedParams = encryptParams(JSON.stringify(reqParams), PUBLIC_KEY);
But as long as I used the encrptedParams from NodeJS code to existing Java code HttpPost, it does not work. And if I used encrptedParams from Java to NodeJS Axios it works.
Could you please help pointing me out what is wrong with my NodeJS code? or show me the correct way to have encryption from above Java code into NodeJS code?
Thank you.
While performing Elliptic Curve cryptography with secp256k1 curve, I noticed that while the code and test cases compile on the Android Studio IDE they do not compile on the android device since the curve isn't defined in the jre/jdk that the mobile device uses. Changing the curve to a prime256v1 I seem to be facing difficulties in converting a hex string of the publicKey to PublicKey object.
Given the hex string of a PublicKey.getEncoded() which is in a database. I want an android client to convert the byte[] from converting the hex string into a PublicKey object. I am converting the byte[] using X509EncodedKeySpec() as follows:
public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(publicKeySpec);
return pub;
}
The conversion from a hex string to a byte[] happens as follows:
public static byte[] hexStringToByteArray(String hexString) {
byte[] bytes = new byte[hexString.length() / 2];
for(int i = 0; i < hexString.length(); i += 2){
String sub = hexString.substring(i, i + 2);
Integer intVal = Integer.parseInt(sub, 16);
bytes[i / 2] = intVal.byteValue();
String hex = "".format("0x%x", bytes[i / 2]);
}
return bytes;
}
The conversion from a byte[] to Hex string is as follows:
public static String convertBytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars).toLowerCase();
}
However when I run this on the android app (7.0, API 24) I get the following System Error
W/System.err: java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000b9:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG
at com.android.org.conscrypt.OpenSSLKey.getPublicKey(OpenSSLKey.java:295)
at com.android.org.conscrypt.OpenSSLECKeyFactory.engineGeneratePublic(OpenSSLECKeyFactory.java:47)
at java.security.KeyFactory.generatePublic(KeyFactory.java:357)
What is the recommended approach for converting a Hex string to a PublicKey for EC instance on an android device.
Here's sample code that you can execute:
ECDSA.java
public class ECDSA {
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime256v1");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(ecSpec, random);
KeyPair pair = keyGen.generateKeyPair();
return pair;
}
public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey pub = kf.generatePublic(publicKeySpec);
return pub;
}
public static PrivateKey getPrivateKey(byte[] privk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec privateKeySpec = new X509EncodedKeySpec(privk);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey privateKey = kf.generatePrivate(privateKeySpec);
return privateKey;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
KeyPair keyPair = ECDSA.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// Converting byte[] to Hex
publicKeyHex = convertBytesToHex(publicKey.getEncoded());
privateKeyHex = convertBytesToHex(privateKey.getEncoded());
// Trying to convert Hex to PublicKey/PrivateKey objects
PublicKey pkReconstructed = ECDSA.getPublicKey(hexStringToByteArray(publicKeyHex));
PrivateKey skReconstructed = ECDSA.getPrivateKey(hexStringToByteArray(privateKeyHex));
// This throws an error when running on an android device
// because there seems to be some library mismatch with
// java.security.* vs conscrypt.OpenSSL.* on android.
}
Finally we get a real MCVE and we can now see that you are not using the correct class for encoded private keys. X509EncodedKeySpec is only for public keys. From the javadocs (emphasis mine):
This class represents the ASN.1 encoding of a public key, encoded
according to the ASN.1 type SubjectPublicKeyInfo.
For private keys, the correct encoding is usually a PKCS8EncodedKeySpec. The encoding can be determined by examining the output of Key.getFormat(). Therefore, change your method getPrivateKey of ECDSA to
public static PrivateKey getPrivateKey(byte[] privk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privk);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey privateKey = kf.generatePrivate(privateKeySpec);
return privateKey;
}
I am trying to learn some Go and blockchains.. Starting with ECDSA signatures. Trying to figure out how to test if I had a correctly working Go implementation of ECDSA signatures, I figured I would try to create a similar version in Java and compare the results to see if I can get them to match.
So Java attempt:
public static void main(String[] args) throws Exception {
//the keys below are previously generated with "generateKey();" and base64 encoded
generateKey();
String privStr = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAQ7bMVIcWr9NpSD3hPkns5C0qET87UvyY5WI6UML2p0Q==";
String pubStr = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAES8VdACZT/9u1NmaiQk0KIjEXxiaxms74nu/ps6bP0OvYMIlTdIWWU2s35LEKsNJH9u5QM2ocX53BPjwbsENXJw==";
PrivateKey privateKey = base64ToPrivateKey(privStr);
PublicKey publicKey = base64ToPublicKey(pubStr);
String str = "This is string to sign";
byte[] signature = signMsg(str, privateKey);
boolean ok = verifySignature(publicKey, str, signature);
System.out.println("signature ok:" + ok);
String privHex = getPrivateKeyAsHex(privateKey);
}
public static byte[] signMsg(String msg, PrivateKey priv) throws Exception {
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initSign(priv);
byte[] strByte = msg.getBytes("UTF-8");
ecdsa.update(strByte);
byte[] realSig = ecdsa.sign();
//the printed signature from here is what is used in the Go version (hex string)
System.out.println("Signature: " + new BigInteger(1, realSig).toString(16));
return realSig;
}
//https://stackoverflow.com/questions/30175149/error-when-verifying-ecdsa-signature-in-java-with-bouncycastle
private static boolean verifySignature(PublicKey pubKey, String msg, byte[] signature) throws Exception {
byte[] message = msg.getBytes("UTF-8");
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initVerify(pubKey);
ecdsa.update(message);
return ecdsa.verify(signature);
}
public static String generateKey() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(256, random); //256 bit key size
KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
ECPrivateKey ePriv = (ECPrivateKey) priv;
PublicKey pub = pair.getPublic();
//https://stackoverflow.com/questions/5355466/converting-secret-key-into-a-string-and-vice-versa
String encodedPrivateKey = Base64.getEncoder().encodeToString(priv.getEncoded());
byte[] pubEncoded = pub.getEncoded();
String encodedPublicKey = Base64.getEncoder().encodeToString(pubEncoded);
System.out.println(encodedPrivateKey);
System.out.println(encodedPublicKey);
return encodedPrivateKey;
}
public static PrivateKey base64ToPrivateKey(String encodedKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
return bytesToPrivateKey(decodedKey);
}
public static PrivateKey bytesToPrivateKey(byte[] pkcs8key) throws GeneralSecurityException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("EC");
PrivateKey privateKey = factory.generatePrivate(spec);
return privateKey;
}
public static PublicKey base64ToPublicKey(String encodedKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
return bytesToPublicKey(decodedKey);
}
public static PublicKey bytesToPublicKey(byte[] x509key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(x509key);
KeyFactory factory = KeyFactory.getInstance("EC");
PublicKey publicKey = factory.generatePublic(spec);
return publicKey;
}
//https://stackoverflow.com/questions/40552688/generating-a-ecdsa-private-key-in-bouncy-castle-returns-a-public-key
private static String getPrivateKeyAsHex(PrivateKey privateKey) {
ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
byte[] privateKeyBytes = ecPrivateKey.getS().toByteArray();
System.out.println("S:"+ecPrivateKey.getS());
String hex = bytesToHex(privateKeyBytes);
System.out.println("Private key bytes: " + Arrays.toString(privateKeyBytes));
System.out.println("Private key hex: " + hex);
return hex;
}
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0 ; j < bytes.length ; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Doing the signing and verification in Java works just fine. Easy to configure of course, since they are all the same libs, parameters, and all.
To verify the same signature in Go, I tried:
func TestSigning(t *testing.T) {
privKey := hexToPrivateKey("10EDB31521C5ABF4DA520F784F927B390B4A844FCED4BF2639588E9430BDA9D1")
pubKey := privKey.Public()
sig := "3045022071f06054f450f808aa53294d34f76afd288a23749628cc58add828e8b8f2b742022100f82dcb51cc63b29f4f8b0b838c6546be228ba11a7c23dc102c6d9dcba11a8ff2"
sigHex, _ := hex.DecodeString(sig)
ePubKey := pubKey.(*ecdsa.PublicKey)
ok := verifyMySig(ePubKey, "This is string to sign", sigHex)
println(ok)
}
func verifyMySig(pub *ecdsa.PublicKey, msg string, sig []byte) bool {
r := new(big.Int).SetBytes(sig[:len(sig)/2])
s := new(big.Int).SetBytes(sig[len(sig)/2:])
return ecdsa.Verify(pub, []byte(msg), r, s)
}
func hexToPrivateKey(hexStr string) *ecdsa.PrivateKey {
bytes, _ := hex.DecodeString(hexStr)
k := new(big.Int)
k.SetBytes(bytes)
println("k:")
fmt.Println(k.String())
priv := new(ecdsa.PrivateKey)
curve := elliptic.P256()
priv.PublicKey.Curve = curve
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
return priv
}
Initially, I tried to just export the Private key in Java as a base64 encoded string, and import that into Go. But I could not figure out how to get Go to load the key in the format Java stores if (X509EncodedKeySpec). So instead, I tried this way to copy the big integer of the private key only, and generate the public key from that. If I get that to work, then try to copy just the public key..
Anyway, the Go code fails to verify the signature. It is always false. Also, I cannot figure out where to put the SHA function in Go from "SHA1withECDSA" part.
I am sure I am missing some basic concepts here. How to do this properly?
Managed to get this to work. So just to document it for myself and anyone interested..
As pointed by in comments, the signature from Java is in ASN1 format. Found a nice description of the format here: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1.
I also found some good examples on how to do SHAxx with ECDSA in Go at https://github.com/gtank/cryptopasta (sign.go and sign_test.go). Just need to run the relevant SHA function before the ECDSA code.
Found example code for building the public keys from parameters in Go at http://codrspace.com/supcik/golang-jwt-ecdsa/.
I paste the relevant code below, if someone finds an issue, please let me know..
Relevant Java code:
public static PublicKey bytesToPublicKey(byte[] x509key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(x509key);
KeyFactory factory = KeyFactory.getInstance("EC");
ECPublicKey publicKey = (ECPublicKey) factory.generatePublic(spec);
//We should be able to use these X and Y in Go to build the public key
BigInteger x = publicKey.getW().getAffineX();
BigInteger y = publicKey.getW().getAffineY();
System.out.println(publicKey.toString());
return publicKey;
}
//we can either use the Java standard signature ANS1 format output, or just take the R and S parameters from it, and pass those to Go
//https://stackoverflow.com/questions/48783809/ecdsa-sign-with-bouncycastle-and-verify-with-crypto
public static BigInteger extractR(byte[] signature) throws Exception {
int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
int lengthR = signature[startR + 1];
return new BigInteger(Arrays.copyOfRange(signature, startR + 2, startR + 2 + lengthR));
}
public static BigInteger extractS(byte[] signature) throws Exception {
int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
int lengthR = signature[startR + 1];
int startS = startR + 2 + lengthR;
int lengthS = signature[startS + 1];
return new BigInteger(Arrays.copyOfRange(signature, startS + 2, startS + 2 + lengthS));
}
public static byte[] signMsg(String msg, PrivateKey priv) throws Exception {
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initSign(priv);
byte[] strByte = msg.getBytes("UTF-8");
ecdsa.update(strByte);
byte[] realSig = ecdsa.sign();
//this is the R and S we could also pass as the signature
System.out.println("R: "+extractR(realSig));
System.out.println("S: "+extractS(realSig));
return realSig;
}
Relevant Go code:
func verifyMySig(pub *ecdsa.PublicKey, msg string, sig []byte) bool {
//https://github.com/gtank/cryptopasta
digest := sha1.Sum([]byte(msg))
var esig ecdsaSignature
asn1.Unmarshal(sig, &esig)
//above is ASN1 decoding from the Java format. Alternatively, we can just transfer R and S parameters and set those
// esig.R.SetString("89498588918986623250776516710529930937349633484023489594523498325650057801271", 0)
// esig.S.SetString("67852785826834317523806560409094108489491289922250506276160316152060290646810", 0)
fmt.Printf("R: %d , S: %d", esig.R, esig.S)
println()
return ecdsa.Verify(pub, digest[:], esig.R, esig.S)
}
func hexToPrivateKey(hexStr string) *ecdsa.PrivateKey {
bytes, err := hex.DecodeString(hexStr)
print(err)
k := new(big.Int)
k.SetBytes(bytes)
println("k:")
fmt.Println(k.String())
priv := new(ecdsa.PrivateKey)
curve := elliptic.P256()
priv.PublicKey.Curve = curve
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
//we can check these against the Java implementation to see if it matches to know key was transferred OK
fmt.Printf("X: %d, Y: %d", priv.PublicKey.X, priv.PublicKey.Y)
println()
return priv
}
I have an android project in which am getting an Triple DES encrypted piece of text from my web services. I need to Triple DES decrypt.
However, I am getting invalid key exceptions. My key is converted to HEX format and I got an error: W/System.err﹕ java.security.InvalidKeyException: DES key too long - should be 8 bytes I found here a forum explaining that hex may cause issue
"DES keys are 56 bits normally packaged in 8 bytes so the chances are that the 16 bytes/characters they have given you are the hex encoded bytes of the key. You can get a hex decoder"
So I converted my hex string to a byte array using
private static byte[] hexStringtoByteArray(String hex){
int len = hex.length();
byte [] data = new byte[len/2];
for(int i=0; i<len;i+=2){
data[i/2] = (byte)((Character.digit(hex.charAt(i), 16)<<4) + Character.digit(hex.charAt(i+1),16));
}
return data;
}
and passed that in to the cipher and I get an error:
W/System.err﹕ java.security.InvalidKeyException
W/System.err﹕ at javax.crypto.spec.DESedeKeySpec.
here is my decrypt method. I would appreciate if someone could shine some light on where I might be going wrong.
public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String UNICODE_FORMAT = "UTF8";
String decryptedPinText = null;
byte[] hexConvert = hexStringtoByteArray(encryptKey);
SecretKey desKey = null;
KeySpec desKeySpec = new DESedeKeySpec(hexConvert); // Exception HERE
Cipher desCipher;
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
desCipher = Cipher.getInstance("DES/ECB/NoPadding");
try {
desKey = skf.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
desCipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] decryptPin = desCipher.doFinal(pin.getBytes());
decryptedPinText = new String(decryptPin, "UTF-8");
return decryptedPinText;
}
My key is C9AF269DF8A78A06D1216BFFF8F0536A.
I have checked with the client and the key is correct, so the same key is being used for encryption.
Encryption Code
public string TripleDESEncrypt(string strClearText, string strKey)
{
byte[] bytClearText;
byte[] bytClearTextChunk = new byte[8];
byte[] bytEncryptedChunk = new byte[8];
int BytesCount = 0;
int nArrayPosition = 0;
string strEncryptedChar;
string strEncryptedText = "";
ArrayList Input = new ArrayList();
ArrayList Output = new ArrayList();
TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();
tdes.Key = HexToByteArray(strKey);
tdes.Mode = CipherMode.ECB;
ICryptoTransform tdesEncrypt = tdes.CreateEncryptor();
bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
BytesCount = bytClearText.Length;
for (int i = 0; i < BytesCount; i++)
{
if (nArrayPosition == 8)
{
Input.Add(bytClearTextChunk);
bytClearTextChunk = new byte[8];
nArrayPosition = 0;
}
bytClearTextChunk[nArrayPosition] = bytClearText[i];
nArrayPosition++;
}
if (nArrayPosition != 0)
Input.Add(bytClearTextChunk);
foreach (byte[] Cbyte in Input)
{
tdesEncrypt.TransformBlock(Cbyte, 0, 8, bytEncryptedChunk, 0);
Output.Add(bytEncryptedChunk);
bytEncryptedChunk = null;
bytEncryptedChunk = new byte[8];
}
foreach (byte[] Cbyte in Output)
{
foreach (byte BByte in Cbyte)
{
strEncryptedChar = BByte.ToString("X");
strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
strEncryptedText += strEncryptedChar;
}
}
return strEncryptedText;
}
Here is an example of the decrypted text with 14 chars: 12345678901234
DES would expect an 8 byte key (with parity). So Triple DES expects a 24 byte key (with parity). Since you only have a 16 byte key, you have to replicate some of it, to get the final key. Oftentimes the first and last 8 bytes are the same. You can try two variants:
byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 0, tdesKey, 0, 16);
System.arraycopy(hexConvert, 0, tdesKey, 16, 8);
// tdesKey := K1 || K2 || K1
or
byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 8, tdesKey, 0, 8);
System.arraycopy(hexConvert, 0, tdesKey, 8, 16);
// tdesKey := K2 || K1 || K2
when
hexConvert := K1 || K2
Maybe you must to use this cipher:
public byte[] encTripleDes (String txt, byte [] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException{
DESedeKeySpec keySpec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
SecretKey ky = keyfactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, ky);
return cipher.doFinal(txt.getBytes("UTF-8"));
}
And for decrypting:
public byte[] uncTripleDes (byte [] encryptedTextBytes, byte [] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException{
DESedeKeySpec keySpec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
SecretKey ky = keyfactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, ky);
return cipher.doFinal(encryptedTextBytes);
}
Look that I use a "PKCS5Padding" in the instance of the cipher.
Notice that the padding is used for give the same size at all the blocks (for example if the last block is 788 instead of 1024).
For create the key, in my solution (it's not the only one), I calculate a sha-256 hash and then I get the necessary bytes for the Des key:
Calculating the hash:
public byte[] sumCalc (){
String key = "anyKey";
byte[] hashedKey = null;
try {
byte [] byteKey = key.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("SHA-256");
hashedKey = md.digest(byteKey);
}catch (Exception ex){
System.err.println("Error generant clau" + ex);
}
return hashedKey;
}
Finally get only the 128 bytes that we need for the Des key:
bytedKey = Arrays.copyOf(bytedKey, 16 ); // 16 use only first 128 bit. if 32 use only 256
It's my solution but not the only one!
You instantiate your Cipher as a (singel) DES Cipher with:
desCipher = Cipher.getInstance("DES/ECB/NoPadding");
But your key is a 16 byte 3Des key, and thuse you get the error
DES key too long - should be 8 bytes
Try instantiating your cipher as a 3DES cipher with:
desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
I am trying to upgrade my RSA key pair size from 1024 to 2048. I am going to give the sample code here.
For 1024:
The first step in my code is to build a signature using Java code
try {
// Generate Public & Private Keys
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.genKeyPair();
RSAPublicKey key = (RSAPublicKey) keyPair.getPublic();
System.out.println("Modulus");
System.out.println(key.getModulus());
String publicKey = encode(key.getModulus().toByteArray()) + encode(key.getPublicExponent().toByteArray());
// Build the Signature
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(keyPair.getPrivate());
seedData = seedData + "ABC";
sig.update(seedData.getBytes("UTF-8"));
String signature = encode(sig.sign());
signatureString.append(signature);
// Embed the public key in the signature
int publicKeyLocation = signatureString.charAt(signatureString.length() - 2) % 40;
// Add some random bytes at the end
int randomBytesToAdd = signatureString.charAt(signatureString.length() - 10) % 9;
System.out.println("Random bytes to add");
System.out.println(randomBytesToAdd);
System.out.println("Adding Random bytes that are:");
String s = randomString(randomBytesToAdd);
signatureString.insert(publicKeyLocation, s);
} catch (Exception e) {
}
return signatureString.toString();
}
Now this encoded string in passed to .NET application
Where in the .NET application i used to get public key and signature
protected RSAParameters GetPublicKey(string encodedString)
{
int bytesToSkip = encodedString.Substring((encodedString.Length - 10), 1).ToCharArray()[0] % 9;
int publicKeyStarts = bytesToSkip + encodedString.Substring((encodedString.Length - 2), 1).ToCharArray()[0] % 40;
string keyString = encodedString.Substring(publicKeyStarts, 176);
string modulusString = keyString.Substring(0, 172);
string exponentString = keyString.Substring(172);
RSAParameters publicKey = new RSAParameters();
publicKey.Modulus = new byte[128];
Array.Copy(System.Convert.FromBase64String(modulusString), 1, publicKey.Modulus, 0, 128);
publicKey.Exponent = System.Convert.FromBase64String(exponentString);
return publicKey;
}
protected byte[] GetSignature(string encodedString)
{
int bytesToSkip = encodedString.Substring((encodedString.Length - 10), 1).ToCharArray()[0] % 9;
int publicKeyStarts = bytesToSkip + encodedString.Substring((encodedString.Length - 2), 1).ToCharArray()[0] % 40;
String digSig = encodedString.Substring(0, publicKeyStarts - bytesToSkip) + encodedString.Substring(publicKeyStarts + 176);
return System.Convert.FromBase64String(digSig);
}
If I change the key pair size from 1024 to 2048 what are the changes i need to make in my .NET code
The answer I am looking for is: from the generated RSA public key how can I get length of Modulus and Exponent
I found the answer to get the size or length of modulus and exponent from the RSA public key
code to generate public key
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.genKeyPair();
RSAPublicKey key = (RSAPublicKey) keyPair.getPublic();
String publicKey = encode(key.getModulus().toByteArray()) + encode(key.getPublicExponent().toByteArray());
int modLength = key.getModulus().toByteArray().length;
int exponentLength = key.getPublicExponent().toByteArray().length;