I'm looking to write something that can enumerate and use (to sign) certificates in CurrentUser/My and LocalMachine/My, but I haven't been able to find anything for the Windows cert store, only Java's own secret store. This link looks promising, but I can only use what ships with Java.
I found this question asked on SO before, but it's from five years ago, which is a long time in computer years. Thanks!
Start Java with -Djavax.net.ssl.trustStoreType=WINDOWS-ROOT.
See https://www.oracle.com/technical-resources/articles/javase/security.html for more information.
The cross-platform nature of the Java has its own downsides -- you cannot access some (or many) OS-specific things without external libraries. Windows certificate store is accessible only via CryptoAPI native functions which are not support by Java default installation.
You may take a look at this thread: Calling Win32 API method from Java
If you can use JNA, then you can use various Certificate and Certificate Store Functions in crypt32.dll to enumerate certificates and perform signing operations.
KeyStore keyStore = KeyStore.getInstance(getKeyStoreType(), "SunMSCAPI");
keyStore.load(null, null);
try {
Field field = keyStore.getClass().getDeclaredField("keyStoreSpi");
field.setAccessible(true);
KeyStoreSpi keyStoreVeritable = (KeyStoreSpi)field.get(keyStore);
field = keyStoreVeritable.getClass().getEnclosingClass().getDeclaredField("entries");
field.setAccessible(true);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Set accessible keyStoreSpi problem", e);
}
Enumeration enumeration = keyStore.aliases();
I picked from where Crypt32 left, used JNA to access the certificates using the same windows dialog that pops up if you were to use any windows specific program:
NativeLibrary cryptUI = NativeLibrary.getInstance("Cryptui");
NativeLibrary crypt32 = NativeLibrary.getInstance("Crypt32");
Function functionCertOpenSystemStore = crypt32.getFunction("CertOpenSystemStoreA");
Object[] argsCertOpenSystemStore = new Object[] { 0, "CA"};
HANDLE h = (HANDLE) functionCertOpenSystemStore.invoke(HANDLE.class, argsCertOpenSystemStore);
Function functionCryptUIDlgSelectCertificateFromStore = cryptUI.getFunction("CryptUIDlgSelectCertificateFromStore");
System.out.println(functionCryptUIDlgSelectCertificateFromStore.getName());
Object[] argsCryptUIDlgSelectCertificateFromStore = new Object[] { h, 0, 0, 0, 16, 0, 0};
Pointer ptrCertContext = (Pointer) functionCryptUIDlgSelectCertificateFromStore.invoke(Pointer.class, argsCryptUIDlgSelectCertificateFromStore);
Function functionCertGetNameString = crypt32.getFunction("CertGetNameStringW");
char[] ptrName = new char[128];
Object[] argsCertGetNameString = new Object[] { ptrCertContext, 5, 0, 0, ptrName, 128};
functionCertGetNameString.invoke(argsCertGetNameString);
System.out.println("Selected certificate is " + new String(ptrName));
Function functionCertFreeCertificateContext = crypt32.getFunction("CertFreeCertificateContext");
Object[] argsCertFreeCertificateContext = new Object[] { ptrCertContext};
functionCertFreeCertificateContext.invoke(argsCertFreeCertificateContext);
Function functionCertCloseStore = crypt32.getFunction("CertCloseStore");
Object[] argsCertCloseStore = new Object[] { h, 0};
functionCertCloseStore.invoke(argsCertCloseStore);
It is just a piece of code that works; feel free to apply your coding practices.
Related
how to add validation information on PDF during signing if signature use Certification
SigUtils.setMDPPermission(doc, signature, 1);
cause warning message on function tell addValidationInformation.validateSignature(inPath, outFile) :
PDF is certified to forbid changes, some readers may report the document as invalid despite that the PDF specification allows DSS additions
i call addValidationInformation function after signing doc, signing.signPDF();
what i have tried with this function :
private void makeLTV() {
try {
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
catalogDict.setNeedToBeUpdated(true);
byte[][] certs = new byte[certificateChain.length][];
for (int i = 0; i < certificateChain.length; i++) {
certs[i] = certificateChain[i].getEncoded();
}
// Assign byte array for storing certificate in DSS Store.
List<CRL> crlList = new ArrayList<CRL>();
List<OCSPResp> ocspList = new ArrayList<OCSPResp>();
for (int i = 0; i < certificateChain.length; i++) {
X509Certificate cert = (X509Certificate) certificateChain[i];
if (!cert.getIssuerDN().equals(cert.getSubjectDN())) {
X509Certificate issuerCert = (X509Certificate) certificateChain[i + 1];
if (issuerCert != null) {
OCSPResp ocspResp;
ocspResp = new GetOcspResp().getOcspResp(cert, issuerCert);
if (ocspResp != null) {
ocspList.add(ocspResp);
}
}
crlList.addAll(new DssHelper().readCRLsFromCert(cert));
}
}
byte[][] crls = new byte[crlList.size()][];
for (int i = 0; i < crlList.size(); i++) {
crls[i] = ((X509CRL) crlList.get(i)).getEncoded();
LogSystem.info("set CRL data");
}
byte[][] ocsps = new byte[ocspList.size()][];
for (int i = 0; i < ocspList.size(); i++) {
ocsps[i] = ocspList.get(i).getEncoded();
}
Iterable<byte[]> certifiates = Arrays.asList(certs);
COSDictionary dss = new DssHelper().createDssDictionary(certifiates, Arrays.asList(crls),
Arrays.asList(ocsps));
catalogDict.setItem(COSName.getPDFName("DSS"), dss);
} catch (Exception e) {
// TODO Auto-generated catch block
LogSystem.error(e.toString());
e.printStackTrace();
}
before doc.addSignature(signature, signatureInterface, signatureOptions);
PDF is certified to forbid changes, some readers may report the document as invalid despite that the PDF specification allows DSS additions
Well, the LTV level addition is indeed allowed to PDF documents even with restricted MDP permissions. See "Table 257 — Entries in the DocMDP transform parameters dictionary" of ISO 32000-2:
P number (Optional) The access permissions granted for this document. Changes to a PDF that are incremental updates which include only the data necessary to add DSS’s 12.8.4.3, "Document Security Store (DSS)" and/or document timestamps 12.8.5, "Document timestamp (DTS) dictionary" to the document shall not be considered as changes to the document as defined in the choices below.
So technically speaking you are allowed to add validation data to a PDF signature.
However, you need to take into account the practical aspect of this change. For example, the most commonly used application for reading electronic signatures Adobe, more likely, will invalidate such change. The problem here, that the "extension" of a signature with a validation data and/or a timestamp may involve other changes within a PDF document, which may be not considered as part of the allowed changes, as there is no formal guidance on either how to create such signature nor as validate them.
For your information, ETSI standardization group is currently working on a new set of standards that should provide a formal guidance for validation of different AdES signature formats. So maybe in the future this part will be clarified.
For the implementation part of the LTV level addition, I would recommend to add validation data to your signature after the production time of a first timestamp (signature-time-stamp or a PDF document timestamp), that will ensure that the revocation data is fresh and can be considered during the validation process (see "5.2.5 Revocation freshness checker" of EN 319 102-1).
For this you will need to add the revocation data to a signed document within a new incremental update (revision). For this you will need to use the method
pdDocument.saveIncremental(...)
executed on a signed PDDocument.
How could I express this java code in JRuby:
// Convert the store to a certificate chain
CertStore store = response.getCertStore();
Collection<? extends Certificate> certs = store
.getCertificates(null);
Certificate[] chain = new Certificate[certs.size()];
int i = 0;
for (Certificate certificate : certs) {
chain[i++] = certificate;
}
I have the "store" within JRuby and its recognised as a collection.
e.g.
puts store.type
#Collection
OK so the problem here is that I was passing in a regular expression and not a CertSelector object.
This code now works as expected.
store.get_certificates(nil)
According to several posts I've found out it's now possible to perform CAdES using BouncyCastle but there is hardly any documentation on the topic.
For starters I want to perform CAdES-BES without any optional signed attributes on a file with a file based certificate.
In response to dander:
I have something that might be helpful, you have your SignerInformation, you need to extend it, first you need to create an attribute from the timestamp, I'll assume you already have a TimeStampResponse as tspResp
TimeStampToken token = tsresp.getTimeStampToken();
Attribute timeStamp = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, new DERSet(ASN1Object.fromByteArray(token.getEncoded())));
Then you need to extend your SignerInformation
AttributeTable unsigned = signerInformation.getUnsignedAttributes();
Hashtable<ASN1ObjectIdentifier, Attribute> unsignedAttrHash = null;
if (unsigned == null) {
unsignedAttrHash = new Hashtable<ASN1ObjectIdentifier, Attribute>();
} else {
unsignedAttrHash = signerInformation.getUnsignedAttributes().toHashtable();
}
unsignedAttrHash.put(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, signatureTimeStamp);
SignerInformation newsi = SignerInformation.replaceUnsignedAttributes(si, new AttributeTable(
unsignedAttrHash));
I think that's about it.
Here is how I got the signin-certificate attribute
Attribute signingCertificateAttribute;
MessageDigest dig = MessageDigest.getInstance(DigestAlgorithm().getName(),
new BouncyCastleProvider());
byte[] certHash = dig.digest(SigningCertificate().getEncoded());
if (DigestAlgorithm() == DigestAlgorithm.SHA1) {
SigningCertificate sc = new SigningCertificate(new ESSCertID(certHash));
signingCertificateAttribute = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificate, new DERSet(sc));
} else {
ESSCertIDv2 essCert = new ESSCertIDv2(new AlgorithmIdentifier(DigestAlgorithm().getOid()), certHash);
SigningCertificateV2 scv2 = new SigningCertificateV2(new ESSCertIDv2[] { essCert });
signingCertificateAttribute = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2));
}
Hope it helps
CAdES is an extension of CMS (aka PKCS7), which is possible to do with BouncyCastle. RFC5126 contains everything needed for a CAdES signature, also, I recommend lookup info on ASN.1 since most of the parts are described in that format.
I am currently in hunt for the same answer you are looking for and found that the book Beginning Cryptography with Java by David Hook gives a lot of detailed information you might need.
Useful code could be found on "https://joinup.ec.europa.eu/"
Take a look on CAdESProfileBES.java.
Someone put the same code on Fork of the original SD - Digital Signature Service.
Is it possible to access certificates stored in the Local Machine store (rather than Current User) from a Java Servlet? I've tried using the MSCAPI provider opening the "Windows-MY" and "Windows-ROOT" stores, but neither contain certificates from the Local Machine store.
I used used JNA to access the certificates using the same windows dialog that pops up if you were to use any windows specific program - this may not answer your question but certainly lets you provide an option to access anything in a 'windows way':
NativeLibrary cryptUI = NativeLibrary.getInstance("Cryptui");
NativeLibrary crypt32 = NativeLibrary.getInstance("Crypt32");
Function functionCertOpenSystemStore = crypt32.getFunction("CertOpenSystemStoreA");
Object[] argsCertOpenSystemStore = new Object[] { 0, "CA"};
HANDLE h = (HANDLE) functionCertOpenSystemStore.invoke(HANDLE.class, argsCertOpenSystemStore);
Function functionCryptUIDlgSelectCertificateFromStore = cryptUI.getFunction("CryptUIDlgSelectCertificateFromStore");
System.out.println(functionCryptUIDlgSelectCertificateFromStore.getName());
Object[] argsCryptUIDlgSelectCertificateFromStore = new Object[] { h, 0, 0, 0, 16, 0, 0};
Pointer ptrCertContext = (Pointer) functionCryptUIDlgSelectCertificateFromStore.invoke(Pointer.class, argsCryptUIDlgSelectCertificateFromStore);
Function functionCertGetNameString = crypt32.getFunction("CertGetNameStringW");
char[] ptrName = new char[128];
Object[] argsCertGetNameString = new Object[] { ptrCertContext, 5, 0, 0, ptrName, 128};
functionCertGetNameString.invoke(argsCertGetNameString);
System.out.println("Selected certificate is " + new String(ptrName));
Function functionCertFreeCertificateContext = crypt32.getFunction("CertFreeCertificateContext");
Object[] argsCertFreeCertificateContext = new Object[] { ptrCertContext};
functionCertFreeCertificateContext.invoke(argsCertFreeCertificateContext);
Function functionCertCloseStore = crypt32.getFunction("CertCloseStore");
Object[] argsCertCloseStore = new Object[] { h, 0};
functionCertCloseStore.invoke(argsCertCloseStore);
It is just a piece of code that works; feel free to apply your coding practices.
The default JDK implementation is fairly limited. AFAIK it will only bring back RSA keys and certificates. It is not a general purpose adapter to MSCAPI. I have been able to get some certs back using the mechanism you describe.
As others have mentioned, the MSCAPI provider does not provide access to certificates and keys stored in the "Local Computer" certificate store. The reason for this is that MSCAPI uses the Microsoft CryptoAPI function CertOpenSystemStore to access the certificates and keys. The documentation for this function explicitly states that "Only current user certificates are accessible using this method, not the local machine store". You can follow this OpenJDK bug if you want to track progress on this issue.
If you want a proper solution to the problem you can purchase the commercial Pheox JCAPI library.
If you can live with a hack, I have created a simple utility that intercepts the JDKs call to CertOpenSystemStore and returns a handle to a virtual certificate store allowing read-only access to the certificates and keys in both the "Current User" and "Local Machine" certificate stores. This solved my problem, but be aware of the limitations of this utility.
The certificates you are looking for are in the java keystore file or are passed into tomcat when starting the server
http://tomcat.apache.org/tomcat-4.0-doc/ssl-howto.html
if you are trying to load them in your application, then look here for to make HTTPS requests, then the HTTPClient documentation will get you started
http://www.jdocs.com/httpclient/3.0.1/api-index.html?m=class&p=org.apache.commons.httpclient.contrib.ssl&c=AuthSSLProtocolSocketFactory&render=classic
not sure if this helps you out, but if you can provide more details, then you might be able to get a more specific answer
public class KeyStoreLookup {
public static void main(String args[]) {
try {
KeyStore ks =
KeyStore.getInstance(KeyStore.getDefaultType());
String fname = System.getProperty("user.home") +
File.separator + ".keystore";
FileInputStream fis = new FileInputStream(fname);
ks.load(fis, null);
if (ks.isKeyEntry(args[0])) {
System.out.println(args[0] +
" is a key entry in the keystore");
char c[] = new char[args[1].length()];
args[1].getChars(0, c.length, c, 0);
System.out.println("The private key for" + args[0] +
" is " + ks.getKey(args[0], c));
Certificate certs[] = ks.getCertificateChain(args[0]);
if (certs[0] instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) certs[0];
System.out.println(args[0] + " is really " +
x509.getSubjectDN());
}
if (certs[certs.length - 1] instanceof
X509Certificate) {
X509Certificate x509 = (X509Certificate)
certs[certs.length - 1];
System.out.println(args[0] + " was verified by " +
x509.getIssuerDN());
}
}
else if (ks.isCertificateEntry(args[0])) {
System.out.println(args[0] +
" is a certificate entry in the keystore");
Certificate c = ks.getCertificate(args[0]);
if (c instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) c;
System.out.println(args[0] + " is really " +
x509.getSubjectDN());
System.out.println(args[0] + " was verified by " +
x509.getIssuerDN());
}
}
else {
System.out.println(args[0] +
" is unknown to this keystore");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm developing on an application at the moment which contains quite a lot of personal user information - things like Facebook contacts, etc ... Now, one of the things I want to be able to do (and have done, quite effectively) is open up parts of the application to "3rd Party" applications, using Android's build-in inter-process communication protocol (AIDL). So far so good.
Here's the catch: because we're involved in handling quite a lot of personal information, we have to be quite careful about who can and can't access it; specifically, only "Trusted" applications should be able to do so. So the natural way to do this is to use a custom permission within the AndroidManifest.xml file where we declare the services. My problem is this: I want to be able to enact signature-level protection (similar to the normal "signature" permission level), but with a bit of a catch:
I don't only want application signed with our internal signature to be able to access the services. I'd like to be able to build a list of "trusted signatures" & at runtime (or if there's a better way, then maybe some other time?) be able to check incoming requests against this list of trusted keys.
This would satisfy the security constraints in the same way as the normal "signature" permission level I think - only programs on the "trusted keys list" would be able to access the services, and keys are hard to spoof (if possible at all?) - but with the added bonus that we wouldn't have to sign every application making use of the APIs with our internal team's key.
Is this possible at the moment in Android? And if so, are there any special requirements?
Thanks
I've now found the answer to this question, but I'll leave it for the sake of anyone looking in the future.
I opened up a discussion on android-security-discuss where it was answered. Link: http://groups.google.com/group/android-security-discuss/browse_thread/thread/e01f63c2c024a767
Short answer:
private boolean checkAuthorised(){
PackageManager pm = getPackageManager();
try {
for (Signature sig :
pm.getPackageInfo(pm.getNameForUid(getCallingUid()),
PackageManager.GET_SIGNATURES).signatures){
LogUtils.logD("Signature: " + sig.toCharsString());
if (Security.trustedSignatures.get(sig.toCharsString()) != null) {
return true;
}
}
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
LogUtils.logD("Couldn't find signature in list of trusted keys! Possibilities:");
for(String sigString : Security.trustedSignatures.keySet()){
LogUtils.logD(sigString);
}
/* Crash the calling application if it doesn't catch */
throw new SecurityException();
}
Where Security.trustedSignatures is a Map of the form:
Map<String,String>().put("public key","some description eg. name");
Put this method inside any code that is being called by the external process (ie. within your interface). Note that this will not have the desired effect inside the onBind() method of your RemoteService.
Great info jelford, but I would suggest instead of storing the entire string of the signature, to store/compare the SHA-1 of the certificate as shown in this answer from matreshkin.
This is similar to how Google handles the Maps Android API, and this will match the output shown via keytool.
private boolean checkAuthorized() throws SecurityException {
PackageManager pm = getPackageManager();
try {
PackageInfo packageInfo = pm.getPackageInfo(pm.getNameForUid(getCallingUid()),
PackageManager.GET_SIGNATURES);
Signature[] signatures = packageInfo.signatures;
byte[] certBytes = signatures[0].toByteArray();
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(certBytes));
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] encodedCert = md.digest(cert.getEncoded());
String hexString = byte2HexFormatted(encodedCert);
Log.d("public certificate SHA-1: " + hexString);
String trustedAppName = trustedCerts.get(hexString);
if (trustedAppName != null) {
Log.d("Found public certificate SHA-1 for " + trustedAppName);
return true;
}
} catch (Exception e) {
Log.e(e, "Unable to get certificate from client");
}
Log.w("Couldn't find signature in list of trusted certs!");
/* Crash the calling application if it doesn't catch */
throw new SecurityException();
}
public static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1)) str.append(':');
}
return str.toString();
}