Validate android app certificate - android

I want to check if installed android application has self signed or trusted certificate. The problem is that my code listed below raises CertificateException for all installed (including Google made) apps. Could you help me to find a problem why it doesn't work correctly?
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
List<ResolveInfo> runningServices = getPackageManager().queryIntentActivities(intent, 0);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("AndroidCaStore");
ks.load(null, null);
tmf.init(ks);
X509TrustManager trustManager = (X509TrustManager)tmf.getTrustManagers()[0];
PackageManager manager = getPackageManager();
try {
for (ResolveInfo runningService : runningServices) {
PackageInfo info = manager.getPackageInfo(runningService.activityInfo.packageName,
PackageManager.GET_SIGNATURES);
Signature signature = info.signatures[0];
Signature[] arrSignatures = info.signatures;
for (Signature sig : arrSignatures) {
byte[] rawCert = sig.toByteArray();
InputStream certStream = new ByteArrayInputStream(rawCert);
CertificateFactory certFactory;
X509Certificate x509Cert;
try {
certFactory = CertificateFactory.getInstance("X509");
x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);
trustManager.checkServerTrusted(new X509Certificate[] { x509Cert }, "RSA");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}

The answer is pretty obvious: we can't validate android app certificate using trusted certificates chain (what normally do ssl guts to check web resource certificate), because they are self-signed. That is why checkServerTrusted function raises CertificateException during such check. This book gives following information:
Because a process is tied to a package name, and a package name is
tied to its signature, signatures play a role in securing the data
belonging to a package. A package is typically signed with a
self-signed PKI (Public Key Infrastructure) certificate. A certificate
identifies who the author of the package is. These certificates need
not be issued by a certificate authority. This means the information
in the certificate is not approved or validated by any authority. This
means one can create a certificate that says that their name is
Google. The only assurance is that this package name is reserved to
that user if no one had claimed it in the marketplace before, and any
subsequent updates to that package are given only to that user
(identified by that certificate).

Related

How to generate PKCS#7 signature from digest?

I want to sign the pdf using pdf digest. I have created the hash using below code,
byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
md = MessageDigest.getInstance("SHA256","BC");
while((numOfBytesRead = content.read(buffer)) != -1 ){
md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();
At the end I need to attach this signature to my pdf. I have found one solution Create pkcs7 signature from file digest, but the algorithm used in the link is SHA256withRSA. My privatekey is genearted using EC algorithm and I need to use SHA256withECDSA.Is it possible to just sign the Hash using SHA256withECDSA and attach the signature to the pdf using PDFBox ExternalSigning Interface.
There are several situations in which Adobe calls a signer's certificate invalid even though apparently it is valid; in the case at hand in particular:
Key usage or Extended key usage values not appropriate
PAdES signature misses an ESS signing-certificate-v2 attribute
Key usage or Extended key usage values not appropriate
This is based on the information the OP first published as an answer
I tried below code still the pdf says Signature is invalid. Can you please check the code,
[...]
I have attached the pdf . Pdf file created
Indeed, Adobe Reader says the signature is invalid, but look more closely:
It says "Document has not been modified since this signature was applied" - This means that the signature is mathematically correct!
The issue is that the "Signer's certificate is invalid", and the reason for this can be seen when digging into the signature property dialogues:
Thus, the problem is that your signer certificate is Not valid for usage.
This is due to the highlighted attribute, while the Key Usage Digital Signature is ok, the "Extended key usage" 1.3.6.1.5.5.8.2.2 (OID for IPSEC Protection) is not!
According to the Adobe Digital Signatures Guide for IT, Adobe Acrobat accepts only
one or more of the following Key usage values (if any)
nonRepudiation
signTransaction (11.0.09 only)
digitalSignature (11.0.10 and later)
and one or more of the following Extended key usage values (if any)
emailProtection
codeSigning
anyExtendedKeyUsage
1.2.840.113583.1.1.5 (Adobe Authentic Documents Trust)
Due to its IPSEC Protection extended key usage value, therefore, your certificate is not considered valid for signing PDF documents.
This probably only is an issue in legacy ISO 32000-1 signatures, probably not in PAdES signatures.
PAdES signature misses an ESS signing-certificate-v2 attribute
This is based on the information the OP first published as an answer
I have created 2 pdf files, PDFA is signed using the digest of the pdf content with below code,
[...]
PDFA
PDFB is created with the same private key and certificate, but instead of digest I am using pdf document content directly which gives me valid signed pdf, PDFB code below,
[...]
PDFB
I think something is missing in the signing part of PDFA which I couldn't figure out.
Here the main difference is not whether you explicitly calculate the hash yourself or allow it to be calculated implicitly, the main difference is that the signature in PDFB includes an ESS signing-certificate-v2 attribute while the one in PDFA does not. This attribute is generated between
//PAdES - PDF Advanced Electronic Signature
and
//PAdES-end
As the comments already hint, this is only necessary for PAdES signatures, not for legacy ISO 32000-1 ones. The answer the OP took his original code from referred to creating a legacy ISO 32000-1 signature (and, therefore, works alright) while the OP creates a PAdES signature.
The presence of an ESS signing certificate attribute is required by the PAdES specification ETSI EN 319 142-1:
e) Generators shall use either the signing certificate or the signing-certificate v2 attribute, depending on the hash function, in accordance with ETSI EN 319 122-1.
(ETSI EN 319 142-1, section 6.3 PAdES baseline signatures)
It references the CAdES specification ETSI EN 319 122-1 which in turn requires
h) Requirement for SPO: ESS signing-certificate. The ESS signing-certificate attribute shall be used if the SHA-1 hash algorithm is used.
i) Requirement for SPO: ESS signing-certificate-v2. The ESS signing-certificate-v2 attribute shall be used when another hash algorithms than SHA-1 is used.
(ETSI EN 319 122-1, section 6.3 Requirements on components and services)
I tried below code still the pdf says Signature is invalid. Can you please check the code,
System.out.println("Hash Signing started");
List<Certificate> certList = getFormatCertificate(strCertificate);
PrivateKey privateKey;
CMSSignedData s = null;
Security.addProvider(new BouncyCastleProvider());
byte[] signature = null;
try {
privateKey = loadPrivateKey(strPrivatekey);
byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
md = MessageDigest.getInstance("SHA256","BC");
while((numOfBytesRead = content.read(buffer)) != -1 ){
md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();
// Separate signature container creation step
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(digest)));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
//AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
gen.addSignerInfoGenerator(builder.build(
new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
new JcaX509CertificateHolder(cert)));
//DErse
// gen.addSignerInfoGenerator(builder.build(
// new JcaContentSignerBuilder(sha256withRSA,
// new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
// .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
// new JcaX509CertificateHolder(cert)));
gen.addCertificates(certs);
s = gen.generate(new CMSAbsentContent(), false);
System.out.println("Hash sign completed");
signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("GeneralSecurityException ::"+e.toString());
} catch (OperatorCreationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("OperatorCreationException ::"+e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("IOException ::"+e.toString());
} catch (CMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("CMSException ::"+e.toString());
}finally{
return signature;
}
I have attached the pdf . Pdf file created
#Mkl/Tilman : I have created 2 pdf files, PDFA is signed using the digest of the pdf content with below code,
System.out.println("Hash Signing started");
List<Certificate> certList = getFormatCertificate(strCertificate);
PrivateKey privateKey;
CMSSignedData s = null;
Security.addProvider(new BouncyCastleProvider());
byte[] signature = null;
try {
privateKey = loadPrivateKey(strPrivatekey);
/*byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
//md = MessageDigest.getInstance("SHA256","BC");
md = MessageDigest.getInstance("SHA-256");
while((numOfBytesRead = content.read(buffer)) != -1 ){
md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();*/
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
byte[] digest = md.digest(IOUtils.toByteArray(content));
// Separate signature container creation step
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(digest)));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
//AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
gen.addSignerInfoGenerator(builder.build(
new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
new JcaX509CertificateHolder(cert)));
//DErse
// gen.addSignerInfoGenerator(builder.build(
// new JcaContentSignerBuilder(sha256withRSA,
// new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
// .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
// new JcaX509CertificateHolder(cert)));
gen.addCertificates(certs);
s = gen.generate(new CMSAbsentContent(), false);
System.out.println("Hash sign completed");
signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("GeneralSecurityException ::"+e.toString());
} catch (OperatorCreationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("OperatorCreationException ::"+e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("IOException ::"+e.toString());
} catch (CMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("CMSException ::"+e.toString());
}finally{
return signature;
}
PDFA
PDFB is created with the same private key and certificate, but instead of digest I am using pdf document content directly which gives me valid signed pdf, PDFB code below,
SignatureInterface signatureInterface = new SignatureInterface() {
#SuppressWarnings("rawtypes")
#Override
public byte[] sign(InputStream content) throws IOException {
try {
byte[] certificateByte = null;
Store certs = new JcaCertStore(certificates);
//PAdES - PDF Advanced Electronic Signature
//ESS - Enhanced Security Services
//ASN1 - Abstract Syntax Notation One-standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way
// Generating certificate hash
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(certificates.get(certificates.size()-1).getEncoded());
byte[] certHash = md.digest();
// Generating certificate hash ends
System.out.println("Cert hash generated");
//ESSCertIDv2 identifies the certificate from the hash
ESSCertIDv2 essCert1 =
new ESSCertIDv2(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), certHash);
ESSCertIDv2[] essCert1Arr =
{
essCert1
};
SigningCertificateV2 scv2 = new SigningCertificateV2(essCert1Arr);
Attribute certHAttribute =
new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(certHAttribute);
AttributeTable at = new AttributeTable(v);
//Create a standard attribute table from the passed in parameters - certhash
CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(at){
protected Hashtable createStandardAttributeTable(Map parameters)
{
Hashtable result = super.createStandardAttributeTable(parameters);
result.remove(CMSAttributes.signingTime);
return result;
}
};
//PAdES-end
System.out.println("CMSAttributeTableGenerator generated");
SignerInfoGeneratorBuilder genBuild =
new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
genBuild.setSignedAttributeGenerator(attrGen);
//Get single certificate
org.spongycastle.asn1.x509.Certificate certas1 = org.spongycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificates.get(certificates.size()-1).getEncoded()));
// ContentSigner interface creates SHA256withECDSA signer using PvtKey
ContentSigner sha1Signer = new JcaContentSignerBuilder(signerAlgorithm).build(privateKey);
//Creates SignerInfoGenerator using X.509 cert and ContentSigner
SignerInfoGenerator sifGen = genBuild.build(sha1Signer, new X509CertificateHolder(certas1));
// CMSSignedDataGenerator generates a pkcs7-signature message
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addCertificates(certs);
gen.addSignerInfoGenerator(sifGen);
//Creates CMS message from PDF
CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
//Generate a CMS Signed Data object which can be carrying a detached CMS signature
//msg - content to be signed
CMSSignedData signedData = gen.generate(msg, false);
System.out.println("CMSSignedData is done");
return signedData.getEncoded();
} catch (GeneralSecurityException e) {
throw new IOException(e);
} catch (CMSException e) {
throw new IOException(e);
} catch (OperatorCreationException e) {
throw new IOException(e);
}
}
};
System.out.println("CMSSignedData is done2");
PDDocument pdDocument = PDDocument.load(inputfile);
System.out.println("pdDocument loaded");
pdDocument.addSignature(signature, signatureInterface);
PDFB
I think something is missing in the signing part of PDFA which I couldn't figure out.

How to retrieve installed X509 certificate from Android keychain?

I installed a X509 certificate into andorid keychain using following code:
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_NAME, "My certificate");
installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, certificate.getEncoded());
startActivityForResult(installIntent, 2);
I get an toast mentioning "My Certificate is installed". Now when I am trying to fetch it back using following code:
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
if (ks != null) {
ks.load(null, null);
Enumeration aliases = ks.aliases();
List<String> alliasesNames = Collections.list(aliases);
for (String name : alliasesNames) {
if (ks.getCertificate(name) instanceof X509Certificate) {
X509Certificate certificate = (X509Certificate) ks.getCertificate(name);
if (certificate.getIssuerDN().getName().contains("My Certificate")) {
Log.d("CERTEXIST", "**********User Cert " + certificate.getIssuerDN().getName());
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (java.security.cert.CertificateException e) {
e.printStackTrace();
}
I am not able to find that installed x509 in this. Also I could see the installed x509 cert in User certificate in security settings of the device.
Also when I prompt user to choose a cert for server communication using:
KeyChain.choosePrivateKeyAlias(loginActivity, this,
new String[]{}, null, null, -1, null);
}
The prompt doesn't show my certificate. I am new to this certificates and key chain in Android.
I would like to know how to retrive the saved x509 cert and prompt that to user to select that certificate.
Any help is appreciated.
KeyChain.choosePrivateKeyAlias launches an antivity to prompt user to select the alias for a private key, but you have installed a certificate, not a private key, so your certificate will not be there.
KeyChain.createInstallIntent() can be used to install X509 certificates or PKCS#12 files, containing both private key and certificates. If you need to install a private key+certificate for authentication you need to provide a p12 file.
byte pkcs12Data[] = ...
installIntent.putExtra(KeyChain._PKCS12, pkcs12Data);

How to read SHA and MD5 fingerprint programmatically in Android

Hello I want to read SHA and MD5 fingerprint value of keystore programmatically of my app from which it was signed.
I'll take either SHA or MD5 value as key for security. This key I will use in the code to encrypt something and decrypt same at server end.
Is there any way to find this or is there any way to do same using different good approach. This should be in such a way nobody other can find this key.
Thanks in advance.
PackageInfo info;
try {
info = getPackageManager().getPackageInfo(
"com.your.package.name", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md;
md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String hash_key = new String(Base64.encode(md.digest(), 0));
}
} catch (NameNotFoundException e1) {
} catch (NoSuchAlgorithmException e) {
} catch (Exception e) {
}
try this:
/**
*
* #param pkg packageName
* #return
*/
public String getSingInfo (String pkg) {
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
Signature[] signs = packageInfo.signatures;
Signature sign = signs[0];
String s = getMd5(sign);
return "md5:" + s ;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private String getMd5 (Signature signature) {
return encryptionMD5(signature.toByteArray());
}
public static String encryptionMD5(byte[] byteStr) {
MessageDigest messageDigest = null;
StringBuffer md5StrBuff = new StringBuffer();
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(byteStr);
byte[] byteArray = messageDigest.digest();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
} else {
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return md5StrBuff.toString();
}
Find the path of your application's APK file by calling Context.getPackageCodePath()
Copy that APK to a writeable directory of your choice
Use apk-parser to get the information you need from the APK (see example below)
That library is able to decompress the APK file and parse all of its content. An example extracted from the apk-parser's Github page, tailored to your needs:
try {
ApkParser apkParser = new ApkParser(new File(filePath));
ApkSignStatus signStatus = apkParser.verifyApk(); // not needed
List<CertificateMeta> certs = apkParser.getCertificateMetas();
for (CertificateMeta certificateMeta : certs) {
System.out.println(certificateMeta.getCertMd5());
}
} catch (Exception e) {
e.printStackTrace();
}
Added below solution in-case if someone is looking for it for the first time, and don't know how to get it from studio as well.
Many times we search and get suggestion links.
Easiest Way
Open Android Studio
Open Your Project
Click on Gradle (From Right Side Panel, you will see Gradle Bar)
Click on Refresh (Click on Refresh from Gradle Bar, you will see List Gradle scripts of your Project)
Click on Your Project (Your Project Name form List (root))
Click on Tasks
Click on android
Double Click on signingReport (You will get SHA1 and MD5 in Run Bar)
Check Screenshot below:

Obtain Private Key from inside app

Is there any way to get the private key that current APK was signed with?
The operation is completely safe, since any modification (injection) to app would need a new signing and new key pair. So if private key (that developer is aware of that) is accessible to the running code, it's only accessible to the original code not a malicious one.
X509Certificate allows access to PublicKey, but I need access to PrivateKey.
public static X509Certificate GetCertificate(Context context) {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch(CertificateException e) {
e.printStackTrace();
}
X509Certificate c = null;
try {
c = (X509Certificate) cf.generateCertificate(input);
} catch(CertificateException e) {
}
return c;
}
Edit:
A side question can be:
Is there any way to ensure that currently running code is running inside the APK with the original certificate?
(Everybody have access to public key, so it's not a good candidate to check against, but private key is only known by original developer which can protect it (in some way) and check package certification against that.)
The private key is used only for signing the APK and never (in theory) leaves the place of signing. So it's not possible to recover it from the APK.
Update: and no, the code itself can't check the validity of its own image. If the image has been altered, it's trivial to remove the check as well.

SDK for Android:How facebook check the Hash key?

When we want to use Facebook SDK for Android as our SSO solution, we need to put our Android application signature into our Facebook application settings (Step 5 of Facebook sdk for android).
And that signature should be generated by running the keytool that comes with the Android SDK.
I am curious how facebook verify this signature?
After more than one year, I think I'd better answer my question.
Android's app can get other app's signature by:
public String WriteSignature(String packageName)
{
PackageManager pm = this.getPackageManager();
String sig = "";
PackageInfo pi = null;
try {
pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (NameNotFoundException e1) {
e1.printStackTrace();
}
try {
for (Signature signature : pi.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
sig = Base64.encodeToString(md.digest(), Base64.DEFAULT);
Log.d(ACTIVITY_TAG, sig);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return sig;
}
For facebook app to function we need to provide Facebook "App Id"
For android platform we need to provide package name, class name & "HashKey
App will mostly have 2 HashKey for "Debug" & "Release" version of app
When app access facebook, SDK internally generate & compare the HashKey with one submitted during the facebook app

Categories

Resources