Nakov.com

Thoughts on Software Engineering

  • About
  • Books
  • Courses
  • Videos
  • Presentations
  • Research
  • Publications
  • Others
  • Contacts
  • RSS Feed
  • Home

Categories

  • .net (81)
  • blog (330)
  • bulgarian (203)
  • career (21)
  • contests (28)
  • courses (64)
  • english (131)
  • HTML5 (6)
  • java (44)
  • seminars (81)
  • НЛП (7)
  • предприемачество (3)

Networked Blogs

Follow this blog

Recent Posts

  • Представяне на NASA Space Apps Challenge на 2 април 2013
  • Как да презентираме вдъхновяващо с майсторлък? Мурафетите на Наков
  • Пролетен прием в софтуерната академия: 500 нови студента от април
  • Безплатен курс “Бизнес умения за софтуерни инженери” – от 27 март
  • 580 продължават безплатното си обучение в софтуерната академия след изпитите по CSS и C# част 2

Partners

Intro C# Programming Book by Svetlin Nakov
Telerik Academy

My Projects

  • GWT Advanced Table
  • Internet Programming with Java Book
  • Intro C# Programming Book
  • Intro Java Programming Book
  • Java For Digitally Signing Documents In Web Book
  • Programming for .NET Framework Book
  • Software University

Useful Links

  • Bulgarian Association of Software Developers (BASD)
  • Free Java and Java EE Course
  • NLP Club Bulgaria
  • Stefan Kanev's Blog
  • Telerik Academy
  • Telerik Kids Academy
  • Telerik School Academy

Tags

AJAX ASP.NET C# CSS development HTML Java JavaScript NET Programming Software SQL telerik Академия на Телерик Академия на Телерик за ученици академия академия за софтуерни инженери безплатен курс безплатни курсове безплатни уроци безплатно безплатно обучение курс обучение програмиране разработка на софтуер семинар софтуерна академия състезание телерик

Most Viewed Posts

  • Rejected a Program Manager Position at Microsoft Dublin – My Successful Interview at Microsoft
  • Svetlin Nakov – About Me
  • Innovations in Software Тest Automation – конференция за QA инженери – 25.11.2011
  • Online AES Encryption Tool
  • Disable Certificate Validation in Java SSL Connections
  • My Interview at Google in Zurich
  • Native SQL Queries in Entity Framework
  • JAX-RS, @Path, @PathParam and Optional Parameters
  • Svetlin Nakov – Books
  • NHibernate Lazy Loading BLOB column

Author: Svetlin Nakov

December 1, 2009

  • Svejo.net
  • Tweet

X.509 Certificate Validation in Java: Build and Verify Chain and Verify CLR with Bouncy Castle

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

Previews (17,235), Views (14,258), Comments (11)

11 Comments »

  1. Great example, well written and complete. Many thanks …the code answered many questions I had regarding CRL’s.

    Comment by Rod — December 22, 2010 @ 17:20

  2. Great Great Example. I second Rod’s comment. I have one question though. Do we need to add the certificate in question or the certificate to be validated in the intermediate cert list? I will really appreciate if you help me with this question.
    Thank you so much anyway for this.
    Regards,
    Dpak

    Comment by Dpak — March 17, 2011 @ 05:47

  3. In the additional set of certificates you need to add the trusted Root CA certificates and all intermediate certificates. The certificate that need to be validated does not need to be in this set.

    Regards, Svetlin

    Comment by nakov — March 24, 2011 @ 00:35

  4. Are you sure that the certificate that need to be validated does not need to be in set of intermediate certificates? If it is correct I have probably something wrong and I don’t understand what. Can you explain it to me please?

    I set up this testing scenario:
    RCA (CN=ca.org) created as root CA
    ICA1 (CN=ica.org) created as intermediate CA signed by RCA
    ICA2 (CN=ica2.org) created as intermediate CA signed by ICA1
    UC (CN=user.org) created as server certificate signed ICA2

    When I don’t add UC (CN=user.org) certificate to intermediate certificates set (aC) I get
    CertificateVerificationException: Error building certification path: CN=user.org, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU

    When I add also user.org certificate to intermediate certificates set everything is ok.

    I verified user.org with openssl and it is OK:
    $ (cat rca.pem ; cat ica1.pem ; cat ica2.pem ) > ca.pem
    $ openssl verify -CAfile ca.pem user.pem
    user.pem: OK

    Code is:

    Set aC = new HashSet();
    aC.add(RCA);
    aC.add(ICA1);
    aC.add(ICA2);
    PKIXCertPathBuilderResult ver = CertificateVerifier.verifyCertificate(UC, aC);
    /* ^ here I get CertificateVerificationException */

    vs.

    Set aC = new HashSet();
    aC.add(RCA);
    aC.add(ICA1);
    aC.add(ICA2);
    aC.add(UC);
    PKIXCertPathBuilderResult ver = CertificateVerifier.verifyCertificate(UC, aC);
    /* ^ here it is OK */

    Thank you very much.

    Comment by Tomas — May 1, 2011 @ 19:50

  5. Thank you, Nakov. I work in a CA, and I ws searching for validation using BC (we have our framework), just out of sheer curiosity…
    It is a great example. Covers, from what I’ve seen, pretty much all of it.
    Grand job!

    Comment by NoproblemBabe — February 7, 2012 @ 20:09

  6. [...] will check client certificate against available CA certificate stored in JKS file. There is a good tutorial on this case, but still, the code is not working on my computer. So, I tried to manually develop my [...]

    Pingback by X.509 Certificate Validation Using Java « codeautomate blog — February 21, 2012 @ 08:07

  7. Hi, I’m trying to user your code to verify the certificate against list of CA stored in X509Certificate. I extract the X509Certificate in my keystore and validate using your code, but it is not working. Do your code have any limitation regarding certificate stored in java keystore (JKS)? Right now, I’ve built another code to verify my cert chain: http://codeautomate.org/blog/2012/02/certificate-validation-using-java/

    Comment by hervin — February 21, 2012 @ 08:45

  8. Great Example

    Comment by Aijaz — March 9, 2012 @ 14:29

  9. This code doesn’t work (at least with the latest bouncycastle. The certStore in line 150 has to contain all certificates (trusted, intermediate and certToVerify). If this is fixed, the code seems to work. So it’s still a great example, thank you!

    Comment by Andreas Sahlbach — February 4, 2013 @ 14:37

  10. The code doesn’t work (There’s no main class in CertificateVerifier ).

    Comment by Asma — April 7, 2013 @ 16:53

  11. Thanks – It works fine for me after tidying the code up a bit and in my case dealing with the case where the CRL URL had been moved – just needed to check the http connnection response code for 301/302 and deal with it .. altering the funcion downloadCRLFromWeb in the CRL verifier. Again, thanks for a very nice example. (for comment nº 10 .. there’s no main class as it’s supposed to used in a package (.jar)

    Comment by Paul Crocker — May 19, 2013 @ 02:21

RSS feed for comments on this post. TrackBack URL

Leave a comment

Top Posts

  • Семинар “Как да си намерим работа в ИТ индустрията?” – CV, cover letter, интервю

  • Университет като за софтуерни инженери: къде да учим програмиране след 12 клас? (класацията на Наков)

  • Rejected a Program Manager Position at Microsoft Dublin – My Successful Interview at Microsoft

  • My Interview at Google in Zurich

Translation

Recent Posts

  • Представяне на NASA Space Apps Challenge на 2 април 2013
  • Как да презентираме вдъхновяващо с майсторлък? Мурафетите на Наков
  • Пролетен прием в софтуерната академия: 500 нови студента от април
  • Безплатен курс “Бизнес умения за софтуерни инженери” – от 27 март
  • 580 продължават безплатното си обучение в софтуерната академия след изпитите по CSS и C# част 2

Recent Comments

  • official website on My Interview at Google in Zurich: It's been a good week for guys who are disappointed with Farmacia On Line. The...
  • extra resources on Семинар “Как да си намерим работа в ИТ индустрията?” – CV, cover letter, интервю: While farmacia on line has won wide acceptance, it has served Southeast Asian doctors for...
  • http://theprostitutiontimes.blogspot.com on Нов безплатен курс по уеб дизайн с HTML 5, CSS и JavaScript – от март в академията на Телерик: Instead, it's diverted hundreds of millions of dollars to two children with autism for" pain...
  • Paul Crocker on X.509 Certificate Validation in Java: Build and Verify Chain and Verify CLR with Bouncy Castle: Thanks - It works fine for me after tidying the code up a bit and...
  • look at this web-site on Безплатните курсове в Академията на Телерик за софтуерни инженери – какво да очакваме за 2011-2012?: But beyond the financial implications it is the most logical thing in the world, but...

Archives

  • March 2013 (4)
  • February 2013 (5)
  • January 2013 (7)
  • December 2012 (1)
  • November 2012 (11)
  • October 2012 (8)
  • September 2012 (8)
  • August 2012 (2)
  • July 2012 (10)
  • June 2012 (1)
  • May 2012 (9)
  • April 2012 (9)
  • March 2012 (9)
  • February 2012 (10)
  • January 2012 (8)
  • December 2011 (5)
  • November 2011 (12)
  • October 2011 (18)
  • September 2011 (16)
  • August 2011 (7)
  • July 2011 (7)
  • June 2011 (2)
  • May 2011 (3)
  • April 2011 (10)
  • March 2011 (8)
  • February 2011 (5)
  • January 2011 (7)
  • December 2010 (3)
  • November 2010 (17)
  • October 2010 (8)
  • September 2010 (4)
  • August 2010 (2)
  • July 2010 (4)
  • June 2010 (3)
  • May 2010 (4)
  • April 2010 (2)
  • March 2010 (1)
  • February 2010 (2)
  • January 2010 (4)
  • December 2009 (3)
  • November 2009 (6)
  • October 2009 (3)
  • September 2009 (6)
  • July 2009 (4)
  • June 2009 (1)
  • May 2009 (3)
  • December 2008 (2)
  • November 2008 (2)
  • September 2008 (1)
  • August 2008 (5)
  • July 2008 (2)
  • June 2008 (4)
  • May 2008 (2)
  • April 2008 (1)
  • March 2008 (2)
  • February 2008 (2)
  • January 2008 (1)
  • December 2007 (4)
  • November 2007 (7)
  • October 2007 (3)
  • September 2007 (9)
  • August 2007 (5)

RSS Academy Forums

  • Answered: Победители в NASA Space Apps Challenge
  • Answered: Visual Studio 2012 Problem
  • Answered: Каква е функцията на жълта/зелена лента във Visual Studio?
  • Answered: Родно производство
  • Answered: Една сбъдната мечта

navigation:

Home About Books Courses Presentations Videos Research Publications Others Contacts
Svetlin Nakov @ Google+

My Projects

  • GWT Advanced Table
  • Internet Programming with Java Book
  • Intro C# Programming Book
  • Intro Java Programming Book
  • Java For Digitally Signing Documents In Web Book
  • Programming for .NET Framework Book
  • Software University

Useful Links

  • Bulgarian Association of Software Developers (BASD)
  • Free Java and Java EE Course
  • NLP Club Bulgaria
  • Stefan Kanev's Blog
  • Telerik Academy
  • Telerik Kids Academy
  • Telerik School Academy

Categories

  • .net
  • blog
  • bulgarian
  • career
  • contests
  • courses
  • english
  • HTML5
  • java
  • seminars
  • НЛП
  • предприемачество

Recent Posts

  • Представяне на NASA Space Apps Challenge на 2 април 2013
  • Как да презентираме вдъхновяващо с майсторлък? Мурафетите на Наков
  • Пролетен прием в софтуерната академия: 500 нови студента от април
  • Безплатен курс “Бизнес умения за софтуерни инженери” – от 27 март
  • 580 продължават безплатното си обучение в софтуерната академия след изпитите по CSS и C# част 2

Copyright © 1999 - 2013 Svetlin Nakov