I am trying to generate ECDSA key pair using SpongyCastle in Android.
This is the code:
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
public static KeyPair generate() {
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "SC");
generator.initialize(ecSpec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
Log.i(TAG, "EC Pub Key generated: " + utils.bytesToHex(keyPair.getPublic().getEncoded()));
Log.i(TAG, "EC Private Key generated: " + utils.bytesToHex(keyPair.getPrivate().getEncoded()));
return generator.generateKeyPair();
}
Something is wrong since I always get something like that example of
Public Key:
3059301306072A8648CE3D020106082A8648CE3D03010703420004483ABA9F322240010ECF00E818C041A60FE71A2BD64C64CD5A60519985F110AEDE6308027D2730303F5E2478F083C7F5BB683DCAC22BFEB62F3A48BD01009F40
and Private Key:
308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420219AB4B3701630973A4B2917D53F69A4BE6DAD61F48016BFEF147B2999575CB2A00A06082A8648CE3D030107A14403420004483ABA9F322240010ECF00E818C041A60FE71A2BD64C64CD5A60519985F110AEDE6308027D2730303F5E2478F083C7F5BB683DCAC22BFEB62F3A48BD01009F40
The site ECDSA sample give me "Invalid ECDSA signature message", and them seems really very different from that smaller Private Key and always starting with "04" Public Key generated in the same site.
Also, my backend verification gives me the error "Invalid point encoding 0x30"
The backend Java method check is:
public ECPublicKey getPublicKeyFromHex(String publicKeyHex)
throws NoSuchAlgorithmException, DecoderException, ApplicationGenericException {
byte[] rawPublicKey = Hex.decodeHex(publicKeyHex.toCharArray());
ECPublicKey ecPublicKey = null;
KeyFactory kf = null;
ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
ECCurve curve = ecNamedCurveParameterSpec.getCurve();
EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecNamedCurveParameterSpec.getSeed());
java.security.spec.ECPoint ecPoint = ECPointUtil.decodePoint(ellipticCurve, rawPublicKey);
java.security.spec.ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve,
ecNamedCurveParameterSpec);
java.security.spec.ECPublicKeySpec publicKeySpec = new java.security.spec.ECPublicKeySpec(ecPoint,
ecParameterSpec);
kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
try {
ecPublicKey = (ECPublicKey) kf.generatePublic(publicKeySpec);
} catch (Exception e) {
throw new ApplicationGenericException(e.getMessage(), e.getCause());
}
return ecPublicKey;
}
Java's default encoding for a PublicKey is "X.509" which is not just the EC point; it is an ASN.1 structure identifying the algorithm (EC) and parameters (here prime256v1) PLUS a BIT STRING wrapping the point; see rfc5280 section 4.2.1.7 and rfc3279 section 2.3.5.
Similarly the default encoding for PrivateKey is "PKCS#8" (unencrypted) which is a structure containing an AlgorithmIdentifier plus an OCTET STRING wrapping the data which in this case contains both the private key value and a copy of the public key, see rfc5208 section 5 and C.4 of document SEC 1 at http://www.secg.org with tag [0] omitted but tag [1] present.
To read (either or both of) them back in to Java, get a KeyFactory.getInstance("EC") and use generate{Public,Private} on an X509EncodedKeySpec or PKCS8EncodedKeySpec respectively.
ECDSA and ECDH (and ECMQV etc) use the same key structures, unlike classic integer DSA and DH which use the same mathematical structure ($Z_p^*$) but slightly different representations.
PS: the javadoc for java.security.Key tells you most of this.
More practical example. Convert generated public key to decoded bytes array or hex string:
public String getPublicKeyAsHex(PublicKey publicKey){
ECPublicKey ecPublicKey = (ECPublicKey)publicKey;
ECPoint ecPoint = ecPublicKey.getW();
byte[] publicKeyBytes = new byte[PUBLIC_KEY_LENGTH];
writeToStream(publicKeyBytes, 0, ecPoint.getAffineX(), PRIVATE_KEY_LENGTH);
writeToStream(publicKeyBytes, PRIVATE_KEY_LENGTH, ecPoint.getAffineY(), PRIVATE_KEY_LENGTH);
String hex = Hex.toHexString(publicKeyBytes);
logger.debug("Public key bytes: " + Arrays.toString(publicKeyBytes));
logger.debug("Public key hex: " + hex);
return hex;
}
private void writeToStream(byte[] stream, int start, BigInteger value, int size) {
byte[] data = value.toByteArray();
int length = Math.min(size, data.length);
int writeStart = start + size - length;
int readStart = data.length - length;
System.arraycopy(data, readStart, stream, writeStart, length);
}
Convert decoded bytes array back to PublicKey:
KeyFactory factory = KeyFactory.getInstance(ALGORITHM, ALGORITHM_PROVIDER);
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(CURVE);
ECNamedCurveSpec params = new ECNamedCurveSpec(CURVE, spec.getCurve(), spec.getG(), spec.getN());
BigInteger xCoordinate = new BigInteger(1, Arrays.copyOfRange(decodedPublicKey, 0, PRIVATE_KEY_LENGTH));
BigInteger yCoordinate = new BigInteger(1, Arrays.copyOfRange(decodedPublicKey, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH));
java.security.spec.ECPoint w = new java.security.spec.ECPoint(xCoordinate, yCoordinate);
PublicKey encodedPublicKey = factory.generatePublic(new java.security.spec.ECPublicKeySpec(w, params));
Related
I am using this great method, found in this article https://codingstill.com/2016/01/verify-jwt-token-signed-with-rs256-using-the-public-key/#comment-3232, to validate my Azure idToken, which is signed with RS256.
I noticed that Azure AD changes the public key in a period of time. By reading this article, https://nicksnettravels.builttoroam.com/post-2017-01-24-verifying-azure-active-directory-jwt-tokens-aspx. Therefore, I used HttpClient (c#) to obtain the public key from the URL below, every time when I need to validate the token.
https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}
I did get the result of the public key, which is a string array in x5c key. Below is my code:
public static string GetPublicKeyAsync(string kid)
{
using (var client = new HttpClient())
{
HttpResponseMessage response = client.GetAsync("https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}").Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
// by calling .Result you are synchronously reading the result
string responseBody = responseContent.ReadAsStringAsync().Result;
JObject json = JObject.Parse(responseBody);
string c = json["keys"].ToString();
List<ADPublic> allPublicKeys = JsonConvert.DeserializeObject<List<ADPublic>>(json["keys"].ToString());
foreach (ADPublic key in allPublicKeys)
{
if (key.kid == kid)
{
string certificateString = key.x5c[0];
var certificate = new X509Certificate2(Convert.FromBase64String(certificateString));
string pkey = Convert.ToBase64String(certificate.PublicKey.EncodedKeyValue.RawData);
var x509SecurityKey = new X509SecurityKey(certificate)
{
KeyId = key.kid
};
return pkey;
}
}
}
return null;
}
}
When I pass my public key over to the validation method. I always get an error at the line (PublicKeyFactory.CreateKey(keyBytes) method ):
Please refer to the verify method I mentioned in the beginning of this question for the code below:
// call my get public key function...
string key = GetPublicKeyAsync(headerData["kid"].ToString());
var keyBytes = Convert.FromBase64String(key); // your key here
**AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);**
Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger Parameter name: obj
I think I am almost there, but missing the last part, could you please help? Thank you very much!
I got the same error when using certificate.PublicKey.EncodedKeyValue.RawData to get the public key. I referred to this issue and finally succeeded.
The code shows getting AsymmetricKeyParameter from x5c string. Decode(...) function is referred to in the article.
string token = "<the token that you want to verify>";
string certificateString = "<the first x5c of the first key from https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}>";
byte[] buffer = Convert.FromBase64String(certificateString);
X509CertificateParser parser = new X509CertificateParser();
var _certificate = parser.ReadCertificate(buffer);
AsymmetricKeyParameter publicKey = _certificate.GetPublicKey();
string result = Decode(token, publicKey);
Console.WriteLine("result: " + result);
I'm trying to decrypt a MP4 file that is encrypted using public key in pkcs8 format in Node JS and private key.
I tried this code:
var decrypted = crypto.privateDecrypt({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, buffer);
I'm getting the following error:
Error: error:0406506C:rsa routines:rsa_ossl_private_decrypt:data greater than mod len
I'm currently using this Java code that is working. I would like to swich to Node JS
final byte[] cekWrapped = Base64.getDecoder().decode(conn.getHeaderField("x-amz-meta-x-amz-key").getBytes());
final byte[] iv = Base64.getDecoder().decode(conn.getHeaderField("x-amz-meta-x-amz-iv").getBytes());
// Now we need to decrypt the envelope
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(DECRYPT_MODE, privateKey);
final byte[] decryptedSymmetricKeyBytes = cipher.doFinal(cekWrapped);
final SecretKeySpec cek = new SecretKeySpec(decryptedSymmetricKeyBytes, "AES");
// Once we have the symmetric master key, we can decrypt the contents
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try {
cipher.init(DECRYPT_MODE, cek, new IvParameterSpec(iv));
} catch (final InvalidKeyException e) {
System.out.println(
"Invalid key exception. Please make sure that Java Cryptography Extensions with unlimited " +
"jurisdiction are installed");
}
try (final InputStream in = conn.getInputStream()) {
try (final CipherInputStream cis = new CipherInputStream(in, cipher)) {
try (final FileOutputStream fos = new FileOutputStream(destination.toFile())) {
byte[] b = new byte[1024];
int bytesRead;
while ((bytesRead = cis.read(b)) >= 0) {
fos.write(b, 0, bytesRead);
}
}
}
}
RSA uses the difficulty of factoring large semi-primes as it's trap-door function.
As part of the RSA crypto-system two primes are generated during public/private key creation.
We call the two primes p and q. They multiply to form n, which is the modulus (a semi-prime).
Although RSA can be used to to encrypt data directly (via c=m^e mod{n}), you cannot encrypt data larger than the modulus directly.
Therefore if you are using 2048-bit primes to generate n, you can only encrypt 4096-bits of information directly with the keys (in practice even a little less).
Instead, for large data you need to generate a random symmetric key and send it to your counterpart encrypting it with their RSA public key, and then use the agreed key with a symmetric cipher like AES-GCM or xSalsa20.
Really though, you should move to using ECDH over elliptic curves.
tldr: If you encrypt some data with RSA, where data.length > modulus.length, you're gonna get nonsense when you decrypt it.
is it possible or is there any overload to get a less than 32 characters of GUID ?
currently i am using this statement but its giving me error
string guid = new Guid("{dddd-dddd-dddd-dddd}").ToString();
i want a key of 20 characters
You can use a ShortGuid. Here is an example of an implementation.
It's nice to use ShortGuids in URLs or other places visible to an end user.
The following code:
Guid guid = Guid.NewGuid();
ShortGuid sguid1 = guid; // implicitly cast the guid as a shortguid
Console.WriteLine( sguid1 );
Console.WriteLine( sguid1.Guid );
Will give you this output:
FEx1sZbSD0ugmgMAF_RGHw
b1754c14-d296-4b0f-a09a-030017f4461f
This is the code for an Encode and Decode method:
public static string Encode(Guid guid)
{
string encoded = Convert.ToBase64String(guid.ToByteArray());
encoded = encoded
.Replace("/", "_")
.Replace("+", "-");
return encoded.Substring(0, 22);
}
public static Guid Decode(string value)
{
value = value
.Replace("_", "/")
.Replace("-", "+");
byte[] buffer = Convert.FromBase64String(value + "==");
return new Guid(buffer);
}
Use Jeff Attwood's ASCII85...
http://www.codinghorror.com/blog/2005/10/equipping-our-ascii-armor.html
and
http://www.codinghorror.com/blog/2005/10/c-implementation-of-ascii85.html
I have a website in asp.net 2.0, As I need to use CCNOW payment integration to make a payment but for this I'll have to send request to CCNOW in MD5 format but I can't able to generate my values to CCNOW MD5 format. So, could you please any one have a script/function that will convert given string into MD5?
MD5 isn't a "format," is a hashing algorithm. Use the MD5 class. Assuming you're using C#, it would look something like this:
static string getMd5Hash(string input)
{
// Create a new instance of the MD5CryptoServiceProvider object.
MD5 md5Hasher = MD5.Create();
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
public static string GetMD5(string value) {
MD5 md5 = MD5.Create();
byte[] md5Bytes = System.Text.Encoding.Default.GetBytes(value);
byte[] cryString = md5.ComputeHash(md5Bytes);
string md5Str = string.Empty;
for (int i = 0; i < cryString.Length; i++) {
md5Str += cryString[i].ToString("X");
}
return md5Str;
}
Call it with:
GetMD5(stringToConvert);
I am doing encryption and decryption using AES Algorithm with bouncy castle
My encryption and decryption works ok but it gives me error when my plain text size is bigger
even sometimes it is giving non decrypted data
public static boolean setEncryptionKey(String keyText)
{
byte[] keyBytes = keyText.getBytes();
key = new KeyParameter(keyBytes);
engine = new AESFastEngine();
cipher = new PaddedBufferedBlockCipher(engine);
return true;
}
Encryption:
public static String encryptString(String plainText)
{
byte[] plainArray = plainText.getBytes();
cipher.init(true, key);
byte[] cipherBytes = new byte[cipher.getOutputSize(plainArray.length)];
int cipherLength = cipher.processBytes(plainArray, 0, plainArray.length, cipherBytes, 0);
cipher.doFinal(cipherBytes, cipherLength);
String cipherString = new String(cipherBytes);
return cipherString;
}
Decryption:
public static String decryptString(String encryptedText)
{
byte[] cipherBytes = encryptedText.getBytes();
cipher.init(false, key);
byte[] decryptedBytes = new byte[cipher.getOutputSize(cipherBytes.length)];
int decryptedLength = cipher.processBytes(cipherBytes, 0, cipherBytes.length, decryptedBytes, 0);
cipher.doFinal(decryptedBytes, decryptedLength);
String decryptedString = new String(decryptedBytes);
int index = decryptedString.indexOf("\u0000");
if (index >= 0)
{
decryptedString = decryptedString.substring(0, index);
}
return decryptedString;
}
This decryption is giving me following error
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
at org.bouncycastle.crypto.paddings.PKCS7Padding.padCount(+30)
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(+190)
at com.NewCrypto.decryptString(NewCrypto.java:103)
at com.New_Midlet.startApp(New_Midlet.java:23)
at javax.microedition.midlet.MIDletProxy.startApp(MIDletProxy.java:44)
at com.sun.midp.midlet.Scheduler.schedule(Scheduler.java:375)
at com.sun.midp.main.Main.runLocalClass(Main.java:477)
at com.sun.midp.main.Main.main(+80)
what could be the problem ?
The line
String cipherString = new String(cipherBytes);
is a bug. cipherBytes is a byte array with arbitrary values and cannot be converted to a string using any of the Java string decoders. You should just send/save the cipher as a byte array. If you must make it a string then you'll have to use an encoder. Base64 encoders are often used, as are Base16 (hex). You can use the Apache Commons Codec or my favorite, the Harder Base64 codec.