I'm currently trying to generate a CMSSignedData using only a certificate data and a signed data which is generated from an external software (so I don't have access to the private key and the signature generation is transparent for me). What i need is the encoded data to add it to a SMIME signature content.
Searching on web I find out a solution with BouncyCastle that uses the private key and the data to be signed to get a CMSSignedData. But it don't fix to my problem because I don't have access to private key and the signature is already generated.
Is there any way to create a CMS object using only the certificate data, the public key and the previously generated signature?
Any approach or library i can use or try?
CMS (Cryptographic Message Syntax) is specified in RFC 5652. The SignedData type is an ASN.1 structure, which basically consists of the signed data (encapContentInfo), the signature certificate (and chain) and the signature itself (part of signerInfos):
SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
signerInfos SignerInfos }
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
So, what you have to do now is to create this ASN.1 structure out of the data you have. If you don't already know the algorithms that were used for the signature, you can certainly obtain that information from the operator of the external signing software.
Bouncy Castle is generally a good choice for ASN.1 encoding and in addition you can use the source code of Bouncy Castle that normally creates a CMSSignedData object (CMSSignedDataGenerator) as a template.
Related
Use case:
I have a use case wherein client generates private and public key , sends the base 64 encoded public key to the server.
On server side I will encrypt a message using this public key and send the encrypted message to client , which the client decrypts using its private key.The algorithm agreed upon is 'RSA'.
The problem is on server side I am seeing that certain keys are working using X509EncodedKeySpec as key spec
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
While some keys throw exception (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence) using X509EncodedKeySpec but work using RSAPublicKeySpec:
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
So, what I came to understand is that client and server need to agree whether to use:
PKCS #1 or X.509 for encoding the key . My question is which one is better for my use case? Any guidelines when to use which format?
There's very little difference. The key format Java calls X.509, more exactly known as the ASN.1 structure SubjectPublicKeyInfo (or SPKI) defined in X.509 or equivalently and more conveniently in RFC5280 sec 4.1, is a quite simple way to handle a large and flexible set of algorithms: it consists of a substructure AlgorithmIdentifier which identifies the algorithm and its parameters if applicable, then an opaque BIT STRING which contains the actual key information (encoded) in a format depending on (the algorithm identified by) the AlgorithmIdentifier.
For RSA, the algorithm-dependent part is the ASN.1 structure RSAPublicKey defined in PKCS1 or more conveniently RFC8017 appendix A.1.1 and its earlier versions, and duplicated in RFC3279 sec 2.3.1. Thus for RSA the X.509 (SPKI) format contains the PKCS1 format, and since RSA doesn't have parameters (or at least key-related parameters), the only real difference is that the X.509 format explicitly specifies that the key is RSA -- which in your application you already know.
You have already discovered that vanilla (Oracle-was-Sun-now-OpenJDK) Java crypto, aka JCA Java Cryptographic Architecture, directly supports only the X.509 (SPKI) format, which is a minor advantage. However if you use BouncyCastle it is much easier to convert back and forth than the code in your Q; you simply use the org.bouncycastle.asn1.x509.SubjectPublicKeyInfo class to add or discard the AlgorithmIdentifier:
// test data source
KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spkienc));
// extract PKCS1 part of original SPKI
byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));
// rebuild SPKI from the PKCS1
AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spki2enc));
See my answer to the similar golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() and especially the links to:
Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
Generating RSA keys in PKCS#1 format in Java
Problem transmiting a RSA public key, javaME , bouncy castle
If you don't have BouncyCastle, it's a little harder; you need to write a partial ASN.1 parser or generator. Full ASN.1 processing is rather complicated, but for this case you need only a small subset that isn't too bad. (Yeah, that's faint praise.) I may add this later if I have more time.
A much bigger potential issue is that your key is not authenticated. The hard part of public key distribution, much harder than tiny format details, is making sure that only the legitimate key is distributed. If an attacker can substitute their publickey for the correct one, then the victim encrypts the supposedly secret data in a way the attacker can easily read, and all your fancy cryptography code is completely worthless.
This is why most actual systems don't distribute bare publickeys, but instead certificates that allow verifying the key is the correct key. There are a few certificate schemes, but the most widespread by far is X.509 and its Internet profile PKIX -- in fact the RFCs I referenced above, 5280 and 3279, are part of PKIX. SSL-now-TLS uses X.509. Code-signing uses X.509. S/MIME email uses X.509. (PGP/GPG uses a different kind of certificates, not X.509, but still certificates.) And (vanilla) Java directly supports X.509 certificates just as well or even better than it does "X.509" (SPKI) publickeys.
For my application, I'm trying to sign some byte contents using java.security.Signature class. The problem I'm having is that signature is never generated at a fixed length. For instance, sometimes it is generated at a length of 135, 136 or 137 bytes. Is there a way to specify the length or some padding at the end? Any other ideas or comments are appreciated.
private byte[] ecdsaSign(ECPrivateKey key, byte[] content) throws Exception {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", "SC");
ecdsaSign.initSign(key);
ecdsaSign.update(content);
byte[] signature = ecdsaSign.sign();
return signature;
}
For ECDSA Java crypto uses the ASN.1 DER encoding standardized by X9.62, SEC1 and rfc 3279 sec 2.2.3, which varies slightly in length. This is covered in more detail on another Stack: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-ASN.1 and https://crypto.stackexchange.com/questions/33095/shouldnt-a-signature-using-ecdsa-be-exactly-96-bytes-not-102-or-103 and https://crypto.stackexchange.com/questions/37528/why-do-openssl-elliptic-curve-digital-signatures-differ-by-one-byte
This is also true for DSA, but not RSA, where signatures (and cryptograms since RSA supports both signature and encryption) are fixed length for a given key, as defined by I2OS and OS2I in PKCS1.
If you want a different encoding, such as the fixed-length one used by PKCS11 (and your provider name "SC" suggests that possibility), you must convert it.
Added 2019-10: you no longer have to do it yourself in Java; BouncyCastle since 1.61 (2019-02) correctly supports this, as does SunEC in Java 9 up (2018-12). See later near-dupe Java ECDSAwithSHA256 signature with inconsistent length .
I am just familiarizing myself with the Android Keystore API.
I found out that the following features are available:
At least on some devices the Android Keystore is hardware backed, meaning that crypto operations run in a secure environment (TEE).
When the keystore is hardware backed, private RSA keys as well as secret symmetric keys that have been created within the Keystore can
be configured to never leave the Keystore and the raw keys cannot be
read out even with root access.
I am wondering now if the following is possible:
Generate a Public/Private key pair where the private key never leaves the Keystore
Upload the public key of this pair to a server
On the server: create a random symmetric AES key and encrypt it with the public RSA key uploaded by the user
On the device: Download this encrypted AES key
Import it into the hardware backed Keystore such that it is decrypted in there with the private key of the pair and stored under a
new alias
Use this new key alias to perform symmetric encryption and decryption
1-4 should be possible, the missing link for me now is point 5. in this list. Can some one help me out and tell me if this is possible at all and/or point me to the correct API reference?
I found this:
https://android.googlesource.com/platform/development/+/master/samples/Vault/src/com/example/android/vault/SecretKeyWrapper.java
But it looks to me as if the unwrapping of the secret key happens in the normal environment and the decrypted AES key would be available in the App, which would not satisfy my security requirements.
Update:
I created a small test project using the linked SecretKeyWrapper and here are two code snippets:
The first one does the following:
Create a random AES key (not within in the keystore, this is what would happen on a server later). Obviously the raw key can be retrieved from the generated SecretKey object what isn't a problem since the server can know the key.
Encrypt/wrap the key with a RSA public key that was created in the client's Android Keystore (this would also happen on a server).
Decrypt the key again with the RSA private key (this would happen on the client and actually happens within the TEE in the example).
Snippet 1:
SecretKeyWrapper secretKeyWrapper = new SecretKeyWrapper(this,"testKeyRsa");
// Generate a random AES key (not in the keystore) [1]
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKeyGenerated = keyGen.generateKey();
byte[] secretKeyGeneratedRaw = secretKeyGenerated.getEncoded();
// wrap this key with the RSA key from the keystore [2]
byte[] wrappedKey = secretKeyWrapper.wrap(secretKeyGenerated);
// unwrap it again with the RSA key from the keystore [3]
SecretKey unwrappedKey = secretKeyWrapper.unwrap(wrappedKey);
// the raw key can be read again [4]
byte[] unwrappedKeyRaw = secretKeyGenerated.getEncoded();
What I want to achieve is that the unwrapped key from [3] is stored in the Keystore with a new alias without returning the raw key. Of course I could easily import the SecretKey object into the Keystore here, but the problem is, that at this point the raw key can be retrieved from the object with the statement [4] what induces a security flaw. It is clear that the unwrapping/decryption already happens in the Keystore/TEE, since the private RSA key that is used for the decryption lives in the Keystore and cannot be retrieved.
If I compare this to the situation where a random secret AES key is created in the keystore, I notice that different types (implementing the SecretKey Interface) are returned. In the above example, the type is SecretKeySpec, whereas for keys which are returned from the Android Keystore (see snippet 2 below), "opaque" types are used where the getEncoded() method always returns null. In the following example, the type of keyAesKeystore is AndroidKeyStoreSecretKey.
Snippet 2:
// create a new AES key in the keystore
KeyGenerator keyGenAndroid = KeyGenerator.getInstance("AES","AndroidKeyStore");
keyGenAndroid.init(
new KeyGenParameterSpec.Builder("testKeyAes",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
SecretKey keyAesKeystore = keyGenAndroid.generateKey();
// this returns null
byte[] keyAesKeystoreRaw = keyAesKeystore.getEncoded();
So to rephrase the question: Is it somehow possible to securely import a RSA wrapped AES key into the Android Keystore without revealing the secret key to the application?
Update 2:
#Robert makes the absolutely valid in the answer below that it actually does not matter if the unwrapping happens in the TEE or in the Rich OS (App) since the App (or a tampered version) could always later (after intercepting the wrapped key) just "use" the private RSA key from the Keystore to unwrap the AES key (without the need to access the raw private key at all).
Here is another thought though:
I found that it is possible to set Key Protection Parameters for keys in the Android Keystore (see here).
The linked implementation for the SecretKeyWrapper does not use such protection parameters. After changing the generateKeyPair method as follows and adding the PURPOSE_DECRYPT and PURPOSE_ENCRYPT properties everything still works.
private static void generateKeyPair(Context context, String alias)
throws GeneralSecurityException {
final Calendar start = new GregorianCalendar();
final Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 100);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build();
final KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
gen.initialize(keyGenParameterSpec);
gen.generateKeyPair();
}
I can now protect the RSA key such that it cannot be used for decryption by removing the PURPOSE_DECRYPT property. As expected the Cipher.unwrap method stops working and throws an Incompatible purpose exception then.
So what I would need then is a protection property where the plain decrypt functionality is blocked but which allows such a "secure import functionality" I am looking for. Something like a "PURPOSE_IMPORT" which apparently does not exist.
What you're looking for now exists, as of API level 28 (Android Pie). To use you you need to:
Create a wrapping key pair, an RSA key pair with purpose PURPOSE_WRAP_KEY. You should also generate an attestation for the public key to verify that the private key is a keystore key, in secure hardware.
Send the public key (and attestation) from your app to the server that will provide the wrapped symmetric key.
On the server, you need to wrap the symmetric key. This involves more than just encrypting it, because the wrapper needs to contain not just the key material but also the authorization list that defines how the key may be used. This is done by packaging the key and authorization info in an ASN.1 DER-encoded structure, per the schema documented here. There's some sample wrapping code in the CTS test. Note that if this format seems excessively complicated (e.g. the optional "masking key"), it's because in a future Android release there will be a corresponding secure export function and the use cases for that require the additional complexity. The secure export function didn't make it into Q, but will probably make it into R.
Send the wrapped key to the app, which must create a WrappedKeyEntry and use Keystore.setEntry() to store it.
This should work on any device with API level 28. However, if the device has a Keymaster version < 4 (see the attestation certificate to find out what version of Keymaster is present), then the unwrapping operation will return the wrapped key material to Android userspace. Keymaster version 4 (or above) will keep the unwrapped material in secure hardware, but because lower versions don't have support for the wrapped key feature, it has to be sort of emulated.
What happens if you have a lower Keymaster version is that when you create a PURPOSE_WRAP_KEY key pair, what is actually requested of the secure hardware is a PURPOSE_DECRYPT key pair. Then when you do the import, the keystore daemon uses this PURPOSE_DECRYPT private key to decrypt the secret from the wrapper, then it imports the secret into the secure hardware and wipes the userspace memory that held it. So, the key material exists in the keystore daemon's memory for a fraction of a millisecond. Again, if the device has Keymaster version 4+ it is only unwrapped inside the secure hardware and never leaves.
What you want to achieve is not possible by simply using the AndroidKeystore. What you need is custom code that runs within the TEE.
The reason for this is simple: When you have set-up your app with an asymmetric key-pair stored in the AndroidKeystore and you receive the wrapped AES key it does not matter if the unwrapping takes place inside or outside the AndroidKeystore:
All keys of an app stored in the AndroidKeystore are usable by the app in the normal environment. Is is by design as you wouldn't be able to use them otherwise.
Hence if the asymmetric key-pair is usable by the app the app is always able to unwrap the received wrapped AES key (using code in the normal environment). Therefore it does not make any difference where the unwrapping takes place. You can not guarantee that someone had copied the wrapped AES key when the app received it and then unwrapped it using the asymmetric key from the AndroidKeystore.
I am trying to generate a digital signature using an etoken.
I am using BouncyCastle library and the signature is being generated.
The problem is that I want it in pkcs7 format which also contains tags like '-----BEGIN PKCS7-----' and ends with '-----END PKCS7-----'.But the signature that I generate doesn't contain these tags.
I am not sure where I'm going wrong.
Here is my code..
CMSProcessable content = new CMSProcessableByteArray(contentbytes);
CMSSignedData signedData = signGen.generate(content, securityProviderName);
byte[] signeddata = signedData.getEncoded();
BASE64Encoder encoder = new BASE64Encoder();
digitalSignature = encoder.encode(signeddata);
Now I need the data to be in pkcs7 format.Is there anything wrong with the code? Or do I need to add anything to this? Please help.. Thank you.
PKCS#7, usually called CMS (Cryptographic Message Syntax) is a container format that is specified using ASN.1 and encoded using BER/DER (Basic/Distinguished Encoding Rules). BER/DER are methods of performing binary encoding.
What you are talking about is PEM format, which was specified for Privacy Enhanced Mail. It is sometimes also called an ASCII armor as it makes it possible for binary data to be send over text interfaces. It consists of a header and footer that also identify the data, as well as the encoding to base 64 of the DER encoded blob.
What you can do is to implement the header and footer generation yourself, or you can take a look at the PemWriter class within the Bouncy Castle for Java functionality. The specifications of the format can be found here.
I am very new to Bouncy Castle.I want to know is there any way using Bouncy Castle API that i can get original content if i only have signature using X509Certificate with public key.I am not finding any way as we have only public key.We don't have private key which is used to sign the message.
If one has created a detached signature and you have only that signature (and no original data) then you are out of luck.
If the signature is wrapping, then surely you can get original data back. Signed data doesn't require a private key to be verified and extracted. Public key (actually the certificate with the key) can be included in the wrapping signature in which case you don't even need the certificate separately.