I have read this two posts: One and Two, but I still have question.
I use KeyStore (Android 9) to generate an AES key, and use isInsideSecureHardware() method to check whether the key isInsideSecureHardware. I got return False. Sample code can be found here, and here.
public boolean isInsideSecureHardware ()
Returns true if the key resides inside secure hardware (e.g., Trusted Execution Environment (TEE) or Secure Element (SE)). Key material of such keys is available in plaintext only inside the secure hardware and is not exposed outside of it.
Thus, I want to further confirm whether my phone device (Huawei P20) supports TEE.
Question:
If the phone supports TEE, the key generated by KeyStore will be store into TEE automatically? Do I Need any manually configuration in Java? I heard that keys will be automatically stored in TEE, as long as you use KeyStore.getInstance(), KeyGenerator
.getInstance(algorithm, KeyStore Name). But I am not sure this is True or Not?
If the answer of Q1 is "Need manually configuration", it becomes the reason of isInsideSecureHardware() returns False, right? If the answer of Q1 is "automatically", ignore Q2.
Any method to directly check whether the phone supports TEE, in Java?
#JensV is correct: if you set setIsStrongBoxBacked on the keyGenParameterSpecBuilder, key generation will fail with a StrongBoxUnavailableException if StrongBox is not supported. However, the intermediate case - where there is a TEE (i.e. keys are generated and used within secure HW), but no support for StrongBox - is more tricky to discern.
In general, the way to go is to actually generate a key on the device, and then perform HW key attestation on it at the server - consulting the signed key properties to examine the exact degree of HW backing:
generate a nonce (random byte string) ON The SERVER, pass it to the device
generate a key on the device, requesting HW attestation by calling setAttestationChallenge on the KeyGenParameterSpec builder and passing in the nonce you get from the server (DO NOT USE A NONCE PRODUCED ON THE DEVICE)
request the attestation chain for the key from the Android Key Store
pass the attestation data (cert chain) to your server
verify the attestation (signature) chain on your server
confirm that the root cert matches a published Google root cert
confirm that no cert in the chain hasn been revoked (check against CRL # https://android.googleapis.com/attestation/status)
examine the properties of the Google Key Attestation extension (OID 1.3.6.1.4.1.11129.2.1.17) of the leaf cert
confirm the nonce matches (attestationChallenge)
consult the attestationSecurityLevel of KeyDescription
SecurityLevel ::= ENUMERATED {
Software (0),
TrustedEnvironment (1),
StrongBox (2),
}
TrustedEnvironment and StrongBox both correspond to hardware-backed keys and crypto operations.
From the Android keystore system docs:
Supported devices running Android 9 (API level 28) or higher installed can have a StrongBox Keymaster, an implementation of the Keymaster HAL that resides in a hardware security module. The module contains the following:
[...]
* Secure storage.
[...]
When checking keys stored in the StrongBox Keymaster, the system corroborates a key's integrity with the Trusted Execution Environment (TEE).
[...]
When generating or importing keys using the KeyStore class, you indicate a preference for storing the key in the StrongBox Keymaster by passing true to the setIsStrongBoxBacked() method.
In my understanding that means when you generate a Key and call keyGenParameterSpecBuilder.setIsStrongBoxBacked(true) for the key configuration you can ensure that it's backed by a TEE. If there is no TEE available, it'll throw a StrongBoxUnavailableException.
So to check if there's a TEE available you could just attempt to generate a key this way and see if it works.
Related
I have a token issued by Azure AD. I need to verify the token in my API, which is running on IBM platform.
I am writing the token verifier in Java,using
claims = Jwts.parser().setSigningKeyResolver(signingKeyResolver).parseClaimsJws(accessToken);
The signingKeyResolver is returning a public key. No issues.
I use the following code to get the public key :
BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(key.getN()));
BigInteger exponent = new BigInteger(1, Base64.getUrlDecoder().decode(key.getE()));
RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA");
Provider provider = factory.getProvider();
PublicKey pubkey = factory.generatePublic(publicSpec);
return factory.generatePublic(publicSpec);
Can any one throw some light on Why the signature is invalid ? I observe one thing when I create the factory, the Provider name shows IBMJCEPLUS. Does it have any impact on the key generation ? If so, how do I create the correct factory for Microsoft issued keys ?
Looking for a general advice in creating the factory..
Several things.
the sample is incomplete, and seems mixed up. Is this the role of consumer doing a signature verification?
You are generating a public key which is not an expected behaviour for the verify method of a consumer, you typically 'use' the JWK retrieved from the JWKS URL provided by the JWT producer (AzureAD) not generate a new public key that was not used to generate the signature? To verify a signature, you need to reproduce the same signature by having all the inputs the server used when they generated it. A signature is simply a representation of the payload to ensure the payload was not modified, i.e. integrity check. This is the purpose of the signature and why you must have the same inputs to generate it on the client, wither you gain the inputs used out-of-band somehow (not how AzureAD works) or you use the JWKS to retrieve the appropriate public key for the verify method.
It appears you also expect there to be encrypted claims, based on the RSA reference in your example. If this is intended you would also require you to decipher the payload to access the cleartext. This requires you to have implemented the appropriate PKI. As a consumer (client) the private key must be generated and never shared (or retrieved). Encryption implemented correctly ensures that only the client intended to read the cleartext data 'can' decipher it with the private key, which it itself generated for this purpose, and only it (the client) has control of the private key. Have you the appropriate PKI to do the encrypted JWT properly? Or are you expecting to only utilise the HMAC-based JWT that has no encryption at all and relies on only signature verification (and therefore all claims are public, not protected).
To summaries, there are 2 distinct modes of JWT; 1) HMAC-based that uses signatures where the same inputs are needed on client to verify the signature as was used on server to generate the signature, and this is for integrity only, there is no encryption to protect data. 2) encryption-based JWT that uses a public key for signature verification and the private key used by clients to decipher the encrypted claims data.
1 requires PKI, 2 doesn't.
2 has no data protection and is useful for stateless claims that are intended to be public and therefore verification of the 'claims' before use is how you gain security assurance. Whereas 1 offers the security characteristic of confidentiality and if the PKI is done correctly (private keys are never shared) then the confidential claims data can have more private use cases.
I am looking for a way to validate a java.security.cert.X509Certificate object that is sent from a third party. The certificate provider is using dns or ldap to fetch the certificate. I have included a link with additional information on how the certificate is being retrieved.
http://wiki.directproject.org/w/images/2/24/Certificate_Discovery_for_Direct_Project_Implementation_Guide_v4.1.pdf
I also need to know protocols and default ports that would be used in any of the verification steps. The certificate needs to meet the following criteria from page 13 section 4 of this document:
http://wiki.directproject.org/w/images/e/e6/Applicability_Statement_for_Secure_Health_Transport_v1.2.pdf
Has not expired.
Has a valid signature with a valid message digest
Has not been revoked
Binding to the expected entity
Has a trusted certificate path
Item 1 is straight forward to compare the dates from the getNotAfter and getNotBefore methods on the certificate object to the current date or to use the checkValidity method which throws a checked exception.
For item #2, I see a method to get the signature, but I am unsure how to generate the message digest and verify that the signature and message digest are both valid.
For item #3, The certification revocation list seems to be mixed with some other data by calling this method on the certificate getExtensionValue("2.5.29.31"). Retrieving the certification revocation list data seems possible over http, and ocsp seems to be based on http. I haven't been able to find how to do this in java.
For item #4, I am not sure what binding means in the context of certificates, or what is involved in verifying it.
For item #5, It looks like the data for intermediary certificates is mixed with some other data by calling this method on the certificate getExtensionValue("1.3.6.1.5.5.7.1.1"). CertPathValidator looks like it may be able to help verify this information once the certificates data is retrieved over http.
Certificate validation is a complex task. You can perform all the validations you need manually (expiration, revocation, certification chain) using native Java 8 support or Bouncycastle. But the option that I would recommend is to use a specific library that has already taken into account all the possibilities.
Take a look to DSS documentation and Certificate Verification example
// Trusted certificates sources, root and intermediates (#5 )
CertificateSource trustedCertSource = null;
CertificateSource adjunctCertSource = null;
// The certificate to be validated
CertificateToken token = DSSUtils.loadCertificate(new File("src/main/resources/keystore/ec.europa.eu.1.cer"));
// Creates a CertificateVerifier using Online sources. It checks the revocation status with the CRL lists URLs or OCSP server extracted from the certificate #3
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setAdjunctCertSource(adjunctCertSource);
cv.setTrustedCertSource(trustedCertSource);
// Creates an instance of the CertificateValidator with the certificate
CertificateValidator validator = CertificateValidator.fromCertificate(token);
validator.setCertificateVerifier(cv);
// We execute the validation (#1, #2, #3, #5)
CertificateReports certificateReports = validator.validate();
//The final result. You have also a detailedReport and DiagnosticData
SimpleCertificateReport simpleReport = certificateReports.getSimpleReport();
The validation will perform all the steps you indicate, including expiration, signing of the certificate, revocation, and checking the chain of trust (including the download of intermediate certificates).
Step # 4 I don't know exactly what you mean. I suppose to validate that the certificate corresponds to one of the certification entities of the trusted list
To load the trusted certificate sources see this
CertificatePool certPool = new CertificatePool();
CommonCertificateSource ccc = new CommonCertificateSource(certPool);
CertificateToken cert = DSSUtils.loadCertificate(new File("root_ca.cer"));
CertificateToken adddedCert = ccc.addCertificate(cert);
I will split the answer to 3 pieces. The first is the background , the second is the choice of library , implementation code (references that i had used for my implementation with due credit)
In the past i had implemented a very similar use case. I had IOT fabrications done by a vendor and to onboard them i had implement the same X509 verification process that you have mentioned.
Implementation :
For my implementation i had refered the following . You can include BC as your defaultProvider (Security.setProvider) and use the following code . The code directly solves 1,3,5. The code is here : https://nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/
Now coming to 2 , the answer to that depends on how you will get the certificate from your client and what additional data will be provided by the application.
The high level flow of that is as follows
a) Client produces the certificate
b) Client does a Digest using some algorithm that is acceptable. SHA256 are quite popular, you can increase the strength based on the needs and how much compute you have. Once the client creates the digest , to prove he is the owner of the certificate you can get the digest signed using the private key of the device. This can be then transmitted to the verifier application
c) Once you have the certificate and the signature , you can then use the Certificate and Public key associated with it to verify the signature applying the same digest and then verifying the signature.A good reference is here : http://www.java2s.com/Code/Java/Security/SignatureSignAndVerify.htm
I am not a 100% sure on what 4 means. But if it means proof of identity (who is producing the certificate is bound to be who they are , signing and verifying will provide the same)
Though you can realize the use cases using java security API's , I used bouncycastle core API for realizing the use cases. Bouncycastle API's are far more rich and battle tested especially for weird EC curve algorithms we had to use & you will find that many folks swear by BouncyCastle.
Hope this helps!
Question:
Does Tomcat 9 Realm <CredentialHandler> need the proper algorithm parameters to hash a password and authenticate a user from a password?
My webapp seems to be able to authenticate a user no matter what parameters I pass the PBKDF2WithHmacSHA512 algorithm, even when the attributes on the <CredentialHandler> element are different, or without any attributes.
Is this the expected behavior of Tomcat's Realm? If so, how can this be? Is the Realm able to deduce the parameters from the composition of the hash?
Background:
Tomcat 9 offers container-managed security with a Realm based on Java's SecretKeyFactory algorithms.
I am using PBKDF2WithHmacSHA512, which has options:
Iterations
Key Length
Salt Length
My CredentialHandler as defined in my webapp's context.xml is
<CredentialHandler
className="org.apache.catalina.realm.SecretKeyCredentialHandler"
algorithm="PBKDF2WithHmacSHA512"
iterations="100000"
keyLength="256"
saltLength="16">
</CredentialHandler>
A Tomcat installation offers CLI access to the hashing algorithm at CATALINA_HOME/bin/digest.[bat|sh]. (See the Tomcat 9 Realm Configuration HOW-To for more details.)
No matter the options I pass to the CLI hashing algorithm, Tomcat is able to authenticate the user correctly from the DataSource Realm (MySQL database). The password can be successfully authenticated against both of the following:
Example #1 matches <CredentialHandler>:
$ $CATALINA_HOME/bin/digest.sh -a PBKDF2WithHmacSHA512 -i 100000 -s
16 -k 256 -h org.apache.catalina.realm.SecretKeyCredentialHandler passw0rd
passw0rd:d0c315b015272b531b0a82cec220d4a1$100000$7ac32ed573fe81e75f611a46622573515
ad11d731dcae4839973ae2702774c51
Example #2 different parameters:
$ $CATALINA_HOME/bin/digest.sh -a PBKDF2WithHmacSHA512 -i 100 -s 1 -k 128
-h org.apache.catalina.realm.SecretKeyCredentialHandler passw0rd
passw0rd:47$100$0e4790b617fa24ee324d55bed38ad4b0
Also See
Tomcat - Understanding CredentialHandler
Yes, this is the expected behavior of Tomcat Realm with a SecretKeyCredentialHandler as CredentialHandler defined. Tomcat does not need the iterations, salt or key parameters in context.xml, to authenticate a user from a password.
How is this possible?
If you look into the tomcat documentation of the SecretKeyCredentialHandler, you will notice that the stored passwords is defined as:
salt $ iterationCount $ encodedCredential - a hex encoded salt, iteration code and a hex encoded credential, each separated by $.
The salt and iterationCount used for encryption are part of the stored password. Tomcat don't uses the values of the CredentialHandler-Tag from context.xml for decryption. It uses the values from the password itself. If you look inside your two generated passwords, you will find salt and iterationCount in it, formated in the defined pattern.
Why do i have to set the values in context.xml anyway?
You don't have to. For decryption, only the algorithm-attribute value from context.xml it used. So, for what are the other attributes? Reading carefully the Tomcat documentation gives the answer:
The CredentialHandler can also be used to generate a new stored version of a given credential that would be required, for example, when adding a new user to a Realm or when changing a user's password.
and
iterations - The number of iterations to use when creating a new
stored credential from a clear text credential.
saltLength - The length of the randomly generated salt to use when
creating a new stored credential from a clear text credential.
keyLength - The length of key to generate for the stored
credential. If not specified, a default of 160 is used.
These CredentialHandler-Tag attributs from context.xml are used when creating a new password via CredentialHandler (see method mutate in API). Its used for encryption to create a new password, not for decryption of existing passwords.
I am trying to digitally sign an http - web response. Essentially, I create the HTML and multipart content-type response, sign the response then append the digital signature to the response. I think I am close but off a few steps as this is not a true PGP signature since the appended signature is actually HEXtoString. Big thing is to be able to represent the signature correctly so that response can be interpreted correctly. Could use some suggestions here as I am fairly green with this. Thanks in advance.. below is snippets of code I am using now.
StringBuffer myResponse = new StringBuffer("");
myResponse.append(getHttpHeader());
KeyPair pair2 = loadKeyPair();//loads a key pair from generated files
if (signer==null)
signer = Signature.getInstance("MD5withRSA");
signer.initSign(pair2.getPrivate());
signer.update(message.getBytes());
byte[] b = signer.sign();
FileOutputStream sigfos = new FileOutputStream(getFileLocation(0,localTest));
sigfos.write(b);
sigfos.close();
//verify
signer.initVerify(pair2.getPublic());//pubKey);
signer.update(message.getBytes());
if (signer.verify(b)){
myResponse.append(message);
}
StringBuffer signed= new StringBuffer("");
signed.append(boundary);
signed.append(CRLF);
signed.append("content-type: application/pgp-signature");
signed.append(CRLF);
signed.append("-----BEGIN PGP MESSAGE-----");
signed.append(CRLF);
signed.append("Version: 1");//update this
signed.append(CRLF);
signed.append(CRLF);
signed.append(digSignature);//generated as HexString representation of signed file from above
signed.append(CRLF);
signed.append("-----END PGP MESSAGE-----");
signed.append(CRLF);
signed.append(boundary+"--");
myResponse.append (signed);
ServletOutputStream.println(myResponse);
The resulting "signature" that is transmitted is a byte-hashing hexToString representation of the signed files. I am using standard java classes, but not sure if other libraries would give me a true PGP representation with characters outside of the 0-9a-f representation. ideas??
How is the verification code downloaded to the client? More details about the application in question? If it's a verification script downloaded via HTTP then the scheme is fundamentally broken. You probably need to use SSL, especially if you already argued as such.
Without knowing more about your system, it sounds like an adversary in a man-in-the-middle attack need only to:
Replace the public key in the verification code with their own.
Resign all "secure" communications with their own signature.
Your script sees nothing wrong because the public key it checks was modified by the adversary.
Not to mention all communication is in plain-text (so hopefully no personal/sensitive information being transmitted?)
SSL works around this problem because all the certificates have to be signed by a root certificate authority trusted by / installed with the web browser. CAs are supposed to only issue certificates for domains to people that control/own them; therefore, the previous attack would not work.
Now, if your client is installed in a trusted fashion such that an adversary cannot tamper with it, then you can continue with your scheme and still be secure. For example, if the client is installed on a client PC by hand, or delivered securely some other way (like via SSL, and/or using code signing).
(I did notice a reference to MD5 hashing. Do not use MD5 hashes; MD5 has been broken.)
This issue is due to a NAESB-EDI standard. Where a file has been submitted in an http request and we are required to produce a particular response. We are using SSL and the original payload is supposed to be encrypted. The response is plain html (of 4 items) with an additional digital signature of the response. What I have figured to do is to create the response, have existing pgp software create the signature based upon the generated response and then append the signature to the response. Thus I am not using MD5 anymore and I am not exposing keys to public use (except to those that we specifically trade). So James answer is partially correct and without SSL, this offers little if any protection against sniffing since the response is clear text. Yet without the required information in the request, they would not even get a proper response. Likely wouldnt get a response (let alone a proper one).
I've managed to use Sun's MSCAPI provider in my application. The problem I'm having now is that it always pops up a window, asking for a password, even though I've provided it in the code. This is a problem, because I need the cryptography functionality in a webservice.
Here's the code I have now:
String alias = "Alias to my PK";
char[] pass = "MyPassword".toCharArray();
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, pass);
Provider p = ks.getProvider();
Signature sig = Signature.getInstance("SHA1withRSA",p);
PrivateKey key = (PrivateKey) ks.getKey(alias, pass)
sig.initSign(key);
sig.update("Testing".getBytes());
sig.sign();
This is working great, but I get a popup asking for the password when the last line is run. How do I prevent that?
The MSCAPI provider does not support providing the password to CAPI:
A compatibility mode is supported for applications that assume a password must be supplied. It permits (but ignores) a non-null password. The mode is enabled by default. (1)
To set the password through CAPI, you must call CryptSetKeyParam with the undocumented KP_KEYEXCHANGE_PIN or KP_SIGNATURE_PIN and hope your underlying hardware token provider supports it. (They are not completely undocumented - the documentation for Windows CE and Windows Mobile mention them (2) and they are included in the header files).
My guess is that Windows is popping up the pop up.
Import your key again using the Certificate Import Wizard, but make sure that you don't check the following option on the "Password" screen.
[_] Enable strong private key protection. You will be prompted every time the private key is used by an application if you enable this option.
I resolved this problem setting the provider as follow:
signeData = gen.generate(content, ks.getProvider());
Where
ks is a KeyStore and
genis a CMSSignedDataGenerator