InvalidKeyException: invalid key format - kotlin

I'm trying to encrypt plain texts with public key.
My code keeps fail with InvalidKeyException.
This is my code:
private const val pubkey = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100f357429c22add0d547ee3e4e876f921a0114d1aaa2e6eeac6177a6a2e2565ce9593b78ea0ec1d8335a9f12356f08e99ea0c3455d849774d85f954ee68d63fc8d6526918210f28dc51aa333b0c4cdc6bf9b029d1c50b5aef5e626c9c8c9c16231c41eef530be91143627205bbbf99c2c261791d2df71e69fbc83cdc7e37c1b3df4ae71244a691c6d2a73eab7617c713e9c193484459f45adc6dd0cba1d54f1abef5b2c34dee43fc0c067ce1c140bc4f81b935c94b116cce404c5b438a0395906ff0133f5b1c6e3b2bb423c6c350376eb4939f44461164195acc51ef44a34d4100f6a837e3473e3ce2e16cedbe67ca48da301f64fc4240b878c9cc6b3d30c316b50203010001"
private val cipher: Cipher
init {
val bytesKey = Base64.getDecoder().decode(pubkey)
val keySpec = X509EncodedKeySpec(bytesKey)
val keyFactory = KeyFactory.getInstance("RSA")
val publicKey = keyFactory.generatePublic(keySpec) // where exception has been thrown
cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
}
fun encrypt(plainText: String): String {
val bytePlain = cipher.doFinal(plainText.toByteArray())
return Base64.getEncoder().encodeToString(bytePlain)
}

pubkey contains the hex encoded key, i.e. for the initialization of bytesKey a hex decoding is necessary and no Base64 decoding.
Here you can find an implementation for a hex decoding for Kotlin.
Note also that when instantiating the cipher, it is better to specify the padding besides the algorithm, e.g. RSA/ECB/PKCS1Padding, otherwise the provider dependent default padding is applied, which may be a different one than you expect.

Related

How do I know if my data was decrypted correctly?

Im using the Cipher class in the java.security library to encrypt and decrypt data. Im specifically using RSA encryption to encrypt AES parameters and message byte length, the AES cipher is what I'm using to encrypt the message itself. Currently I have a problem that I can't be sure if the provided encrypted AES parameters and message length were encrypted with my RSA key, or if they were encrypted at all and not just random jiberish.
val rsaCipher = Cipher.getInstance("RSA")
rsaCipher.init(Cipher.DECRYPT_MODE, privateKey)
val rsaDecoded = rsaCipher.doFinal(encryptedAesParametersAndMessageLength)
val aesParameters = ByteArray(48)
val messageLength = ByteArray(4)
for (i in aesParameters.indices) {
aesParameters[i] = rsaDecoded[i]
}
for (i in messageLength.indices) {
messageLength[i] = rsaDecoded[i + aesParameters.size]
}
val length = ByteBuffer.wrap(messageLength).order(ByteOrder.LITTLE_ENDIAN).int
val aesEncryptor = AesEncryptor(aesParameters)
//bytes of the encryptedMessage
//TODO Make sure this doesn't result in an error
val encryptedMessage = ByteArray(length)
When trying to decrypt AES parameters and message length using the RSA key it doesn't throw any kind of exception if the data wasn't decrypted correctly. Eventually this leads to a memory overflow error because the message length is too large. Is it safe to maybe put a 4 bytes(holding some constant int value) after these parameters that i can check to make sure the data was decrypted correctly, or would that risk my apps safety?

How do I form the SSH private key signature in JavaScript?

I am using an in-browser library, SSHy, for SSHing to devices. I'm currently working on adding support for publickey authentication, but I keep getting an error from the server about an invalid signature. I'm able to send the first SSH_MSG_USERAUTH_REQUEST without the signature and get back a SSH_MSG_USERAUTH_PK_OK. But when I send the next message with the signature, I always get a SSH_MSG_USERAUTH_FAILURE.
I'm doing the signing with another library (sshpk-browser) and forming the signature below using SSHy based on the SSH schema.
Can anyone see any potential issues with how I am forming the signature?
const decodedPublicKey = config.privateKey.toPublic().toString('ssh', { hashAlgo: 'sha512' }).split(' ')[1];
const publicKey = atob(decodedPublicKey);
var m = new SSHyClient.Message();
m.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST));
m.add_string(this.termUsername);
m.add_string('ssh-connection');
m.add_string('publickey');
m.add_boolean(true); // has signature
m.add_string('rsa-sha2-512'); // public key algorithm name
m.add_string(publicKey); // public key
// Create signature
var sigMsg = new SSHyClient.Message();
sigMsg.add_string(SSHyClient.kex.sessionId);
sigMsg.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST));
sigMsg.add_string(this.termUsername);
sigMsg.add_string('ssh-connection');
sigMsg.add_string('publickey');
sigMsg.add_boolean(true); // has signature
sigMsg.add_string('rsa-sha2-512');
sigMsg.add_string(publicKey);
const sigMsgString = sigMsg.toString();
// Sign signature
const sign = config.privateKey.createSign('sha512');
sign.update(sigMsgString);
const signature = sign.sign();
m.add_string(atob(signatureToString)); // signature
this.parceler.send(m);

How to read RSA public key from file/string in java/kotlin using BouncyCastle

I am trying to parse a public key in the RSA format from kotlin. I have the key in a string whose contents are like this:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwAzOKC8d0o0dcv1KqILLehASGgOWyjlAc+adazix6ThhX7QeD3Qw
HzxPpbwsJrVPIEMEIN383awIqnCfIL+AbCQPL13XaUCCS74wC5a84X1r6hcI5XO1
9CPAn+jBKmTr4hPaHWKxuhfO3PcXxGfQdXyqNT96bCYnAYaeSECohFjqDbe+RFcL
1lIns2GtQPMh1/uDyhPA+8HSguREWn+Ac3I2c0wtrzZa6R4nruPgIi6XbWRqAskr
tzbO2Xy6O1DcERH9tg+es/pbrWHRHrwEmLXorj3iGqkJJBUmLeW6B5EjmIgiukdJ
dw7bLTNcwf2n0BLJy/bgnhcw4TMOzUadSQIDAQAB
-----END RSA PUBLIC KEY-----
I found a lot of code examples to do this that involve trimming out the BEGIN and END, using String.replace() but that seemed hacky to me. The BouncyCastle code seems to handle this already including the ability to create a parsers for the different types of "files" it encounters. I'm trying this:
try {
val parser = PEMParser(StringReader(publicKeyString))
val pemObject = parser.readPemObject()
val pemContent = pemObject.content
val key = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(pemContent)
serviceLogger.info("Key object: {}", key)
} catch (e: Exception) {
serviceLogger.error("Could not generate key from keyspec", e)
}
I get as far as a pemContent (an array of bytes) without a problem, but when I try to actually parse that into an RSAPublicKey I get this:
java.lang.IllegalArgumentException: failed to construct sequence from byte[]: DEF length 3 object truncated by 2
What I can't figure out is if I'm calling RSAPublicKey.getInstance() correctly - with the content of the entire PemObject - or is this exception telling me that there's something wrong with my key.
The examples I have been able to find on this are pretty old, and the APIs seem to have evolved to the point that I shouldn't have to be chopping up strings manually.
I doubt this really helps the matter, but I'm generating this file in go from an rsa keypair:
func PublicKeyToPemBytes(prvkey *rsa.PrivateKey) ([]byte, error) {
var pubkey *rsa.PublicKey
pubkey = &prvkey.PublicKey
pubkey_bytes := x509.MarshalPKCS1PublicKey(pubkey)
if pubkey_bytes == nil {
return nil, errors.New("Public key could not be serialized")
}
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return pubkey_pem, nil
}
The go rsa.PublicKey object contains an N and an E. It gives me a file like listed above, and the base64 decoding results in the exact same length, 270 bytes.
dave_thompson_085 was right. This code actually does work. He asked me if I altered the data, which made me look more closely at it and I realized I was doing a .toUpperCase() on the base64. The result was still perfectly valid base64 with some bits flipped on here and there. I shouldn't have been doing that .toUpperCase() at all, I just didn't see it until he said that. Bouncy does work.

itextsharp signing pdf with signed hash

I'm trying to sign a pdf through a signing service. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a signing certificate, intermediate certificate, OCSP response, and TimeStampToken. However, I already get stuck trying to sign the pdf with the signatureValue.
I have read Bruno's white paper, browsed the internet excessively, and tried many different ways, but the signature keeps coming up as invalid.
My latest attempt:
First, prepare pdf
PdfReader reader = new PdfReader(src);
FileStream os = new FileStream(dest, FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Certificate = signingCertificate;
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
string hashAlgorithm = "SHA-256";
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
PdfSignatureAppearance appearance2 = stamper.SignatureAppearance;
Stream stream = appearance2.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(stream, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
Hash byte[] sh and convert to string as follows
private static String sha256_hash(Byte[] value)
{
using (SHA256 hash = SHA256.Create())
{
return String.Concat(hash.ComputeHash(value).Select(item => item.ToString("x2"))).ToUpper();
}
}
and send to signing service. The received hex encoded signatureValue I then convert to bytes
private static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
Finally, create signature
private void CreateSignature(string src, string dest, byte[] sig)
{
PdfReader reader = new PdfReader(src); // src is now prepared pdf
FileStream os = new FileStream(dest, FileMode.Create);
IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
MakeSignature.SignDeferred(reader, "Signature1", os, external);
reader.Close();
os.Close();
}
private class MyExternalSignatureContainer : IExternalSignatureContainer
{
protected byte[] sig;
public MyExternalSignatureContainer(byte[] sig)
{
this.sig = sig;
}
public byte[] Sign(Stream s)
{
return sig;
}
public void ModifySigningDictionary(PdfDictionary signDic) { }
}
What am I doing wrong? Help is very much appreciated. Thanks!
Edit: Current state
Thanks to help from mkl and following Bruno's deferred signing example I've gotten past the invalid signature message. Apparently I don't receive a full chain from the signing service, but just an intermediate certificate, which caused the invalid message. Unfortunately, the signature still has flaws.
I build the chain like this:
List<X509Certificate> certificateChain = new List<X509Certificate>
{
signingCertificate,
intermediateCertificate
};
In the sign method of MyExternalSignatureContainer I now construct and return the signature container:
public byte[] Sign(Stream s)
{
string hashAlgorithm = "SHA-256";
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
byte[] ocspResponse = Convert.FromBase64String("Base64 encoded DER representation of the OCSP response received from signing service");
byte[] hash = DigestAlgorithms.Digest(s, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocspResponse, null, CryptoStandard.CMS);
string messageDigest = Sha256_hash(sh);
// messageDigest sent to signing service
byte[] signatureAsByte = StringToByteArray("Hex encoded SignatureValue received from signing service");
sgn.SetExternalDigest(signatureAsByte, null, "RSA");
ITSAClient tsaClient = new MyITSAClient();
return sgn.GetEncodedPKCS7(hash, tsaClient, ocspResponse, null, CryptoStandard.CMS);
}
public class MyITSAClient : ITSAClient
{
public int GetTokenSizeEstimate()
{
return 0;
}
public IDigest GetMessageDigest()
{
return new Sha256Digest();
}
public byte[] GetTimeStampToken(byte[] imprint)
{
string hashedImprint = HexEncode(imprint);
// Hex encoded Imprint sent to signing service
return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
}
}
Still get these messages:
"The signer's identity is unknown because it has not been included in the list of trusted identities and none or its parent
certificates are trusted identities"
"The signature is timestamped, but the timestamp could not be verified"
Further help is very much appreciated again!
"What am I doing wrong?"
The problem is that on one hand you start constructing a CMS signature container using a PdfPKCS7 instance
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
and for the calculated document digest hash retrieve the signed attributes to be
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
to send them for signing.
So far so good.
But then you ignore the CMS container you started constructing but instead inject the naked signature bytes you got from your service into the PDF.
This cannot work as your signature bytes don't sign the document directly but instead they sign these signed attributes (and, therefore, indirectly the document as the document hash is one of the signed attributes). Thus, by ignoring the CMS container under construction you dropped the actually signed data...
Furthermore, the subfilter ADBE_PKCS7_DETACHED you use promises that the embedded signature is a full CMS signature container, not a few naked signature bytes, so the format also is wrong.
How to do it instead?
Instead of injecting the naked signature bytes you got from your service into the PDF as is, you have to set them as external digest in the PdfPKCS7 instance in which you originally started constructing the signature container:
sgn.SetExternalDigest(sig, null, ENCRYPTION_ALGO);
(ENCRYPTION_ALGO must be the encryption part of the signature algorithm, I assume in your case "RSA".)
and then you can retrieve the generated CMS signature container:
byte[] encodedSig = sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
Now this is the signature container to inject into the document using MyExternalSignatureContainer:
IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSig);
MakeSignature.SignDeferred(reader, "Signature1", os, external);
Remaining issues
Having corrected your code Adobe Reader still warns about your signatures:
"The signer's identity is unknown because it has not been included in the list of trusted identities and none or its parent certificates are trusted identities"
This warning is to be expected and correct!
The signer's identity is unknown because your signature service uses merely a demo certificate, not a certificate for production use:
As you see the certificate is issued by "GlobalSign Non-Public HVCA Demo", and non-public demo issuers for obvious reasons must not be trusted (unless you manually add them to your trust store for testing purposes).
"The signature is timestamped, but the timestamp could not be verified"
There are two reasons why Adobe does not approve of your timestamp:
On one hand, just like above, the timestamp certificate is a non-public, demo certificate ("DSS Non-Public Demo TSA Responder"). Thus, there is no reason for the verifier to trust your timestamp.
On the other hand, though, there is an actual error in your timestamp'ing code, you apply the hashing algorithm twice! In your MyITSAClient class you have
public byte[] GetTimeStampToken(byte[] imprint)
{
string hashedImprint = Sha256_hash(imprint);
// hashedImprint sent to signing service
return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
}
The imprint parameter of your GetTimeStampToken implementation is already hashed, so you have to hex encode these bytes and send them for timestamp'ing. But you apply your method Sha256_hash which first hashes and then hex encodes this new hash.
Thus, instead of applying Sha256_hash merely hex encode the imprint!

Windows 8 Metro RSA Encryption: AsymmetricKeyAlgorithmProvider ImportPublicKey Fails

I am attempting to pass some encrypted data between a Win 8 Metro app and a RESTful WCF service. Initially the Metro app requests a public key and the WCF service returns it as a raw Stream as to avoid any pesky formatting issues. The Base 64 encoded public key is decoded in the metro app into a byte array. Here is where the problem occurs. When I attempted to call AsymmetricKeyAlgorithmProvider.ImportPublicKey I get the error "ASN1 bad tag value met".
I am using RSA PKCS1 for the encryption. Here is the relevant code:
WCF Service
string keyName = "This is passed in via a parameter";
var key = !CngKey.Exists(keyName) ? CngKey.Create(CngAlgorithm2.Rsa, keyName) : CngKey.Open(keyName);
// Create the RSA container to get keys and then dispose
using (var rsaCng = new RSACng(key) { EncryptionPaddingMode = AsymmetricPaddingMode.Pkcs1, KeySize = 2048 })
{
byte[] publicBlob = rsaCng.Key.Export(CngKeyBlobFormat.GenericPublicBlob);
publicKey = Convert.ToBase64String(publicBlob);
}
Metro App
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
EDIT 1: More information below
Exporting the public key from a 2048bit key pair from the WCF service yields a 283 bit length key blob, while exporting the same type of public key from the Metro app is only 270 bits. When I import the Metro generated public key it succeeds. Any idea why the WCF service has 13 extra bits on its public key? I think those extra 13 bits are causing the failure.
Here is the Metro code that yields the shorter public key blob:
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey standardKeyPair = provider.CreateKeyPair(2048);
byte[] standardKey = standardKeyPair.ExportPublicKey(CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey).ToArray();
Quite late, but maybe it will help you or saves someone's time...
Change the type of blob type during import. It's really wierd, but I had success with it, after experimenting.
Your code in WCF may stay as it is.
Change just the Metro code:
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.BCryptPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
So the only change here is the BCryptPublicKey during the importing. Then it works. But do not ask me why :-).