December 15, 2009
In the last few months I am working as full-time consultant for the training initiatives in Telerik. It is really an exciting company and I really enjoy my job as manager of the technical training and university relations. I still can’t believe that Telerik is really perfectly organized company, beyond my expectations and even dreams. It is unusual company with specific culture, structure and traditions. Unlike traditional companies our CEO sits behind me along with few developers, not in a separate “big boss room”. I will blog about Telerik in details another day. Now let me present my main work at Telerik.
Telerik Academy
Today I launched a new long-term training project called “Telerik Academy“. Telerik Academy is an initiative of Telerik Corporation aiming to provide free real-world practical training for young people who want to turn into skillful .NET software engineers and join the company development teams. The training complements the traditional University education with the cutting edge industry technologies from Microsoft and best practices in software engineering.
During the Telerik Academy training program highly motivated young people are attracted and candidates covering the program requirements are trained free of charge in the fundamentals of computer programming, logical algorithmic thinking, data structures, algorithms, problem solving and object-oriented concepts. The best of them are selected through exams and interviews and continue to the next level of training – .NET Development Essentials course, covering the .NET Framework class library, databases, SQL Server and LINQ, Web technologies, ASP.NET and AJAX, Windows Forms, WPF and Silverlight, as well as software engineering. Having successfully finished this stage the top students qualify for an additional specialized course in one of the following technological directions used in Telerik: ASP.NET & AJAX, Windows Presentation Foundation (WPF) and Silverlight, Windows Forms and Data-Centric Development. Finally, those who graduate will be presented with the opportunity to join the Telerik team and help us deliver exceptional software to the world.
Telerik Pays to Students to Study .NET Technologies
Telerik pays its trainees at Telerik Academy to learn the .NET technologies for few months and provides job for the best graduates. We are growing even during the global financial crisis and Telerik Academy is our way to grow with highly-motivated and skillful junior developers. Our learning track takes 6 months:
- Foundations of C# Programming (Part 1) – 100 people, free of charge, 1 month, 8 hours/week, staring in February
- Foundations of C# Programming (Part 2) – 50 people (filtered by exam), free of charge, 1 month, 12 hours/week, staring in Marh
- .NET Programming Essentials – 15-20 students, 3 months, 4 hours/day, staring in April
- ASP.NET / Windows Forms / Silverlight & WPF / Data-Centric – 10-15 students, paid to be trained, 1 month, 8 hours/day
- All successfully graduated people start work in Telerik
Program Requirements
The minimum requirements for any candidate are:
- Ability and willingness to work at full time (8 hours/daily) – after the first 2 months
- High motivation to become software engineering professional
- Technical English
- Computer literacy
- Long term commitment
Apply for Telerik Academy
To apply for free 6-months .NET training visit the Telerik Academy official web site.
Tags: Academy, ASP, development, free software development, fundamentals of computer programming, NET, Software, software development courses, telerik, traditional university education
December 1, 2009
Starting from March 2009 I left the National Academy for Software Development (NASD). NASD was really nice project and it was running successfully for few years but the idea changed so much that from some point in 2008 I was no longer able to enjoy doing this work.
Why I left NASD?
1) My dream was about running a software development training center where people get 100% free training and this was really achieved in 2006, 2007 and 2008. At some point the trainings became paid and I was no longer motivated to participate.
2) I really enjoy teaching young and motivated people in software technologies and software engineering and I really do this job well but NASD was fast growing (30 employees in 2008) and at some moment the things got out of my control. Initially I teached all the courses and the teaching quality was superior. Later we employed junior trainers who was good but not the same level like me. Some business partners of mine told me that they want me to teach the people personally. This was not possible because in 2007 and 2008 I was team leader of 5 to 7 software development projects in the same time and coordinator of all training courses. At some moments we had 8 courses running in parallel. We employed few developers who was working on projects in the day and on trainings in the evenings. Most of them were really good developers and good trainers, but people still asked me to teach the people myself. This was the second reason to leave NASD and to find a different way to conduct software development trainings.
What will Happen with NASD?
Please don’t ask me this question. I really don’t know and this is out of my control! NASD was founded by me (with few partners) but I was never been CEO of NASD Ltd. and I even had a minor share of it all the time. I left NASD when the trainings became paid and some students needed to pay for some courses that were initially offered as free. I didn’t like this to happen and I left this company. Now my former partners are driving it. I am no longer involved in any activity that NASD is performing.
Free Trainings, New Academy or What is Next?
This is a good question. In November 2009 I joined Telerik Corporation to organize software development trainings for producing skillful .NET software engineers. Now I lead the project called Telerik Academy that is bringing me back to my initial idea of creating a software development academy. We provide free software development courses and I personally teach all the students in Telerik Academy. I feel closer to my dreams about teaching motivated young people in software engineering and technologies free of charge and preparing them for the real world industrial software development. Note that Telerik Academy is not a successor of NASD. I just moved to a new job. I believe the results will be outstanding like in the first days of NASD.
Tags: Academy, control, development, idea, nasd, nice project, Software, software development projects, software development training, teaching quality
For one of my recent projects I needed to implement X.509 certificate validation library that validates a certificate across given set of trusted root certificated and a set of intermediate certificate. Initially I thought this is a problem that has already out-of-the-box solution in BouncyCastle but the CRL verification was found to be unpleasant to implement and not available out-of-the-box.
The task was formulated as follows: given a X.509 certificate and a set of trusted root certificates and a set of intermediate certificates to build a certification chain (if possible) and to extract the CRL distribution point from the certificate (if available) and to check whether the certificate is not revoked. It was required to support HTTP, HTPS, FTP and LDAP based distribution points.
I will not get into more details because I don’t heve too much time but I think the code is clear enough, so most developers will be able to read, understand and use it:
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
/**
* Class for building a certification chain for given certificate and verifying
* it. Relies on a set of root CA certificates and intermediate certificates
* that will be used for building the certification chain. The verification
* process assumes that all self-signed certificates in the set are trusted
* root CA certificates and all other certificates in the set are intermediate
* certificates.
*
* @author Svetlin Nakov
*/
public class CertificateVerifier {
/**
* Attempts to build a certification chain for given certificate and to verify
* it. Relies on a set of root CA certificates and intermediate certificates
* that will be used for building the certification chain. The verification
* process assumes that all self-signed certificates in the set are trusted
* root CA certificates and all other certificates in the set are intermediate
* certificates.
*
* @param cert - certificate for validation
* @param additionalCerts - set of trusted root CA certificates that will be
* used as "trust anchors" and intermediate CA certificates that will be
* used as part of the certification chain. All self-signed certificates
* are considered to be trusted root CA certificates. All the rest are
* considered to be intermediate CA certificates.
* @return the certification chain (if verification is successful)
* @throws CertificateVerificationException - if the certification is not
* successful (e.g. certification path cannot be built or some
* certificate in the chain is expired or CRL checks are failed)
*/
public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert,
Set<x509certificate> additionalCerts)
throws CertificateVerificationException {
try {
// Check for self-signed certificate
if (isSelfSigned(cert)) {
throw new CertificateVerificationException(
"The certificate is self-signed.");
}
// Prepare a set of trusted root CA certificates
// and a set of intermediate certificates
Set</x509certificate><x509certificate> trustedRootCerts = new HashSet</x509certificate><x509certificate>();
Set</x509certificate><x509certificate> intermediateCerts = new HashSet</x509certificate><x509certificate>();
for (X509Certificate additionalCert : additionalCerts) {
if (isSelfSigned(additionalCert)) {
trustedRootCerts.add(additionalCert);
} else {
intermediateCerts.add(additionalCert);
}
}
// Attempt to build the certification chain and verify it
PKIXCertPathBuilderResult verifiedCertChain =
verifyCertificate(cert, trustedRootCerts, intermediateCerts);
// Check whether the certificate is revoked by the CRL
// given in its CRL distribution point extension
CRLVerifier.verifyCertificateCRLs(cert);
// The chain is built and verified. Return it as a result
return verifiedCertChain;
} catch (CertPathBuilderException certPathEx) {
throw new CertificateVerificationException(
"Error building certification path: " +
cert.getSubjectX500Principal(), certPathEx);
} catch (CertificateVerificationException cvex) {
throw cvex;
} catch (Exception ex) {
throw new CertificateVerificationException(
"Error verifying the certificate: " +
cert.getSubjectX500Principal(), ex);
}
}
/**
* Checks whether given X.509 certificate is self-signed.
*/
public static boolean isSelfSigned(X509Certificate cert)
throws CertificateException, NoSuchAlgorithmException,
NoSuchProviderException {
try {
// Try to verify certificate signature with its own public key
PublicKey key = cert.getPublicKey();
cert.verify(key);
return true;
} catch (SignatureException sigEx) {
// Invalid signature --> not self-signed
return false;
} catch (InvalidKeyException keyEx) {
// Invalid key --> not self-signed
return false;
}
}
/**
* Attempts to build a certification chain for given certificate and to verify
* it. Relies on a set of root CA certificates (trust anchors) and a set of
* intermediate certificates (to be used as part of the chain).
* @param cert - certificate for validation
* @param trustedRootCerts - set of trusted root CA certificates
* @param intermediateCerts - set of intermediate certificates
* @return the certification chain (if verification is successful)
* @throws GeneralSecurityException - if the verification is not successful
* (e.g. certification path cannot be built or some certificate in the
* chain is expired)
*/
private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set</x509certificate><x509certificate> trustedRootCerts,
Set</x509certificate><x509certificate> intermediateCerts) throws GeneralSecurityException {
// Create the selector that specifies the starting certificate
X509CertSelector selector = new X509CertSelector();
selector.setCertificate(cert);
// Create the trust anchors (set of root CA certificates)
Set<trustanchor> trustAnchors = new HashSet</trustanchor><trustanchor>();
for (X509Certificate trustedRootCert : trustedRootCerts) {
trustAnchors.add(new TrustAnchor(trustedRootCert, null));
}
// Configure the PKIX certificate builder algorithm parameters
PKIXBuilderParameters pkixParams =
new PKIXBuilderParameters(trustAnchors, selector);
// Disable CRL checks (this is done manually as additional step)
pkixParams.setRevocationEnabled(false);
// Specify a list of intermediate certificates
CertStore intermediateCertStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCerts), "BC");
pkixParams.addCertStore(intermediateCertStore);
// Build and verify the certification chain
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
PKIXCertPathBuilderResult result =
(PKIXCertPathBuilderResult) builder.build(pkixParams);
return result;
}
}
------------------------
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.X509Extensions;
/**
* Class that verifies CRLs for given X509 certificate. Extracts the CRL
* distribution points from the certificate (if available) and checks the
* certificate revocation status against the CRLs coming from the
* distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs.
*
* @author Svetlin Nakov
*/
public class CRLVerifier {
/**
* Extracts the CRL distribution points from the certificate (if available)
* and checks the certificate revocation status against the CRLs coming from
* the distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs.
*
* @param cert the certificate to be checked for revocation
* @throws CertificateVerificationException if the certificate is revoked
*/
public static void verifyCertificateCRLs(X509Certificate cert)
throws CertificateVerificationException {
try {
List<string> crlDistPoints = getCrlDistributionPoints(cert);
for (String crlDP : crlDistPoints) {
X509CRL crl = downloadCRL(crlDP);
if (crl.isRevoked(cert)) {
throw new CertificateVerificationException(
"The certificate is revoked by CRL: " + crlDP);
}
}
} catch (Exception ex) {
if (ex instanceof CertificateVerificationException) {
throw (CertificateVerificationException) ex;
} else {
throw new CertificateVerificationException(
"Can not verify CRL for certificate: " +
cert.getSubjectX500Principal());
}
}
}
/**
* Downloads CRL from given URL. Supports http, https, ftp and ldap based URLs.
*/
private static X509CRL downloadCRL(String crlURL) throws IOException,
CertificateException, CRLException,
CertificateVerificationException, NamingException {
if (crlURL.startsWith("http://") || crlURL.startsWith("https://")
|| crlURL.startsWith("ftp://")) {
X509CRL crl = downloadCRLFromWeb(crlURL);
return crl;
} else if (crlURL.startsWith("ldap://")) {
X509CRL crl = downloadCRLFromLDAP(crlURL);
return crl;
} else {
throw new CertificateVerificationException(
"Can not download CRL from certificate " +
"distribution point: " + crlURL);
}
}
/**
* Downloads a CRL from given LDAP url, e.g.
* ldap://ldap.infonotary.com/dc=identity-ca,dc=infonotary,dc=com
*/
private static X509CRL downloadCRLFromLDAP(String ldapURL)
throws CertificateException, NamingException, CRLException,
CertificateVerificationException {
Hashtable</string><string , String> env = new Hashtable</string><string , String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
DirContext ctx = new InitialDirContext(env);
Attributes avals = ctx.getAttributes("");
Attribute aval = avals.get("certificateRevocationList;binary");
byte[] val = (byte[])aval.get();
if ((val == null) || (val.length == 0)) {
throw new CertificateVerificationException(
"Can not download CRL from: " + ldapURL);
} else {
InputStream inStream = new ByteArrayInputStream(val);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509CRL crl = (X509CRL)cf.generateCRL(inStream);
return crl;
}
}
/**
* Downloads a CRL from given HTTP/HTTPS/FTP URL, e.g.
* http://crl.infonotary.com/crl/identity-ca.crl
*/
private static X509CRL downloadCRLFromWeb(String crlURL)
throws MalformedURLException, IOException, CertificateException,
CRLException {
URL url = new URL(crlURL);
InputStream crlStream = url.openStream();
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509CRL crl = (X509CRL) cf.generateCRL(crlStream);
return crl;
} finally {
crlStream.close();
}
}
/**
* Extracts all CRL distribution point URLs from the "CRL Distribution Point"
* extension in a X.509 certificate. If CRL distribution point extension is
* unavailable, returns an empty list.
*/
public static List</string><string> getCrlDistributionPoints(
X509Certificate cert) throws CertificateParsingException, IOException {
byte[] crldpExt = cert.getExtensionValue(
X509Extensions.CRLDistributionPoints.getId());
if (crldpExt == null) {
List</string><string> emptyList = new ArrayList</string><string>();
return emptyList;
}
ASN1InputStream oAsnInStream = new ASN1InputStream(
new ByteArrayInputStream(crldpExt));
DERObject derObjCrlDP = oAsnInStream.readObject();
DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP;
byte[] crldpExtOctets = dosCrlDP.getOctets();
ASN1InputStream oAsnInStream2 = new ASN1InputStream(
new ByteArrayInputStream(crldpExtOctets));
DERObject derObj2 = oAsnInStream2.readObject();
CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2);
List</string><string> crlUrls = new ArrayList</string><string>();
for (DistributionPoint dp : distPoint.getDistributionPoints()) {
DistributionPointName dpn = dp.getDistributionPoint();
// Look for URIs in fullName
if (dpn != null) {
if (dpn.getType() == DistributionPointName.FULL_NAME) {
GeneralName[] genNames = GeneralNames.getInstance(
dpn.getName()).getNames();
// Look for an URI
for (int j = 0; j < genNames.length; j++) {
if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) {
String url = DERIA5String.getInstance(
genNames[j].getName()).getString();
crlUrls.add(url);
}
}
}
}
}
return crlUrls;
}
}
------------------------
import java.security.cert.PKIXCertPathBuilderResult;
/**
* This class keeps the result from the certificate verification
* process. If the the certificate is verified as valid, the built
* certification chain is stored in the Result property. If the
* certificate is invalid, the problem is stored in the Exception
* property.
*
* @author Svetlin Nakov
*/
public class CertificateVerificationResult {
private boolean valid;
private PKIXCertPathBuilderResult result;
private Throwable exception;
/**
* Constructs a certificate verification result for valid
* certificate by given certification path.
*/
public CertificateVerificationResult(
PKIXCertPathBuilderResult result) {
this.valid = true;
this.result = result;
}
/**
* Constructs a certificate verification result for invalid
* certificate by given exception that keeps the problem
* occurred during the verification process.
*/
public CertificateVerificationResult(Throwable exception) {
this.valid = false;
this.exception = exception;
}
public boolean isValid() {
return valid;
}
public PKIXCertPathBuilderResult getResult() {
return result;
}
public Throwable getException() {
return exception;
}
}
------------------------
/**
* This class wraps an exception that could be thrown during
* the certificate verification process.
*
* @author Svetlin Nakov
*/
public class CertificateVerificationException extends Exception {
private static final long serialVersionUID = 1L;
public CertificateVerificationException(String message, Throwable cause) {
super(message, cause);
}
public CertificateVerificationException(String message) {
super(message);
}
}
I hope the above coude could be useful to anybody trying to build and validate X.509 certificate chain and check the CRL revocation status.
Tags: crl distribution point, crlURL, intermediate certificates, java security, org, return, root ca certificates, root certificates, security, set