diff --git a/routes/pgp.go b/routes/pgp.go new file mode 100644 index 0000000..d82dd31 --- /dev/null +++ b/routes/pgp.go @@ -0,0 +1,87 @@ +package routes + +import ( + "bytes" + "encoding/hex" + "net/http" + "time" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/packet" + + "github.com/gin-gonic/gin" +) + +type PGPKey struct { + FullName, Name, Comment, Email string + CreationTime time.Time + PublicKeyAlgorithm packet.PublicKeyAlgorithm + Fingerprint [20]byte + KeyID uint16 +} + +func GetOpenPGPInformationEncoded(c *gin.Context) { + query := c.Copy().Request.Body + + block, err := armor.Decode(query) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to parse body.", + }) + return + } + pkt := packet.NewReader(block.Body) + entity, err := openpgp.ReadEntity(pkt) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to parse body.", + }) + return + } + if len(entity.Identities) > 1 { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "No identities found in PGP key.", + }) + return + } + var key *PGPKey + for name, identity := range entity.Identities { + key = &PGPKey{ + FullName: name, + Name: identity.UserId.Name, + Comment: identity.UserId.Comment, + Email: identity.UserId.Email, + CreationTime: entity.PrimaryKey.CreationTime, + PublicKeyAlgorithm: entity.PrimaryKey.PubKeyAlgo, + Fingerprint: entity.PrimaryKey.Fingerprint, + KeyID: uint16(entity.PrimaryKey.KeyId), + } + break + } + buf := new(bytes.Buffer) + closer, err := armor.Encode(buf, block.Type, block.Header) + defer closer.Close() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to re-encode armor..", + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "status": true, + "fullName": key.FullName, + "name": key.Name, + "comment": key.Comment, + "email": key.Email, + "creationTime": key.CreationTime, + "publicKeyAlgorithm": key.PublicKeyAlgorithm, + "fingerprint": hex.EncodeToString(key.Fingerprint[:]), + "keyID": key.KeyID, + "armor": buf.String(), + }) +} diff --git a/routes/x509.go b/routes/x509.go new file mode 100644 index 0000000..2f36df5 --- /dev/null +++ b/routes/x509.go @@ -0,0 +1,366 @@ +package routes + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/sha1" + "crypto/tls" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "io/ioutil" + "net/http" + + "github.com/gin-gonic/gin" +) + +// GetCertificateInformationEncoded handler function for providing raw data to be parsed +func GetCertificateInformationEncoded(c *gin.Context) { + query := c.Copy().Request.Body + data, err := ioutil.ReadAll(query) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to parse body.", + }) + return + } + block, _ := pem.Decode(data) + if block == nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to decode PEM.", + }) + return + } + certificate, err := x509.ParseCertificate(block.Bytes) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Unable to parse x509 data.", + }) + return + } + + var validationType string + for _, value := range certificate.PolicyIdentifiers { + if value.String() == "2.23.140.1.1" { + validationType = "EV" + } else if value.String() == "2.23.140.1.2.2" { + validationType = "OV" + } else if value.String() == "2.23.140.1.2.1" { + validationType = "DV" + } + } + + keyUsages := []int{} + keyUsagesText := []string{} + extendedKeyUsages := []int{} + extendedKeyUsagesText := []string{} + for _, value := range certificate.ExtKeyUsage { + switch value { + case 0: + // All Usages + extendedKeyUsages = append(extendedKeyUsages, 0) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Any/All Usages") + break + case 1: + // TLS Web Server Authentication + extendedKeyUsages = append(extendedKeyUsages, 1) + extendedKeyUsagesText = append(extendedKeyUsagesText, "TLS Web Server Authentication") + break + case 2: + // TLS Web Client Authentication + extendedKeyUsages = append(extendedKeyUsages, 2) + extendedKeyUsagesText = append(extendedKeyUsagesText, "TLS Web Client Authentication") + break + case 3: + // Code Signing + extendedKeyUsages = append(extendedKeyUsages, 3) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Code Signing") + break + case 4: + // Email Protection + extendedKeyUsages = append(extendedKeyUsages, 4) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Email Protection (S/MIME)") + default: + break + } + } + + if certificate.KeyUsage&x509.KeyUsageCRLSign != 0 { + keyUsages = append(keyUsages, 0) + keyUsagesText = append(keyUsagesText, "CRL Signing") + } + if certificate.KeyUsage&x509.KeyUsageCertSign != 0 { + keyUsages = append(keyUsages, 1) + keyUsagesText = append(keyUsagesText, "Certificate Signing") + } + if certificate.KeyUsage&x509.KeyUsageContentCommitment != 0 { + keyUsages = append(keyUsages, 2) + keyUsagesText = append(keyUsagesText, "Content Commitment") + } + if certificate.KeyUsage&x509.KeyUsageDataEncipherment != 0 { + keyUsages = append(keyUsages, 3) + keyUsagesText = append(keyUsagesText, "Data Encipherment") + } + if certificate.KeyUsage&x509.KeyUsageDecipherOnly != 0 { + keyUsages = append(keyUsages, 4) + keyUsagesText = append(keyUsagesText, "Decipher Only") + } + if certificate.KeyUsage&x509.KeyUsageDigitalSignature != 0 { + keyUsages = append(keyUsages, 5) + keyUsagesText = append(keyUsagesText, "Digital Signature") + } + if certificate.KeyUsage&x509.KeyUsageEncipherOnly != 0 { + keyUsages = append(keyUsages, 6) + keyUsagesText = append(keyUsagesText, "Encipher Only") + } + if certificate.KeyUsage&x509.KeyUsageKeyAgreement != 0 { + keyUsages = append(keyUsages, 7) + keyUsagesText = append(keyUsagesText, "Key Agreement") + } + if certificate.KeyUsage&x509.KeyUsageKeyEncipherment != 0 { + keyUsages = append(keyUsages, 8) + keyUsagesText = append(keyUsagesText, "Key Encipherment") + } + + sum := sha1.Sum(certificate.Raw) + + var bitLength int + + switch certificate.PublicKeyAlgorithm { + case x509.RSA: + if rsaKey, ok := certificate.PublicKey.(*rsa.PublicKey); ok { + bitLength = rsaKey.N.BitLen() + } else { + panic("expected rsa.PublicKey for type x509.RSA") + } + case x509.ECDSA: + if ecdsaKey, ok := certificate.PublicKey.(*ecdsa.PublicKey); ok { + bitLength = ecdsaKey.Params().BitSize + } else { + panic("expected ecdsa.PublicKey for type x509.ECDSA") + } + case x509.Ed25519: + bitLength = ed25519.PublicKeySize + } + + c.JSON(http.StatusOK, gin.H{ + "status": true, + "subject": gin.H{ + "commonName": certificate.Subject.CommonName, + "organization": certificate.Subject.Organization, + "organizationalUnit": certificate.Subject.OrganizationalUnit, + "locality": certificate.Subject.Locality, + "country": certificate.Subject.Country, + }, + "issuer": gin.H{ + "commonName": certificate.Issuer.CommonName, + "organization": certificate.Issuer.Organization, + "organizationalUnit": certificate.Issuer.OrganizationalUnit, + "locality": certificate.Issuer.Locality, + "country": certificate.Issuer.Country, + }, + "aia": gin.H{ + "issuingCertificateURL": certificate.IssuingCertificateURL, + "ocspServer": certificate.OCSPServer, + }, + "validationType": validationType, + "signatureAlgorithm": certificate.SignatureAlgorithm.String(), + "publicKeyAlgorithm": certificate.PublicKeyAlgorithm.String(), + "serialNumber": certificate.SerialNumber.String(), + "notBefore": certificate.NotBefore, + "notAfter": certificate.NotAfter, + "keyUsage": keyUsages, + "keyUsageAsText": keyUsagesText, + "extendedKeyUsage": extendedKeyUsages, + "extendedKeyUsageAsText": extendedKeyUsagesText, + "san": certificate.DNSNames, + "emailAddresses": certificate.EmailAddresses, + "fingerprint": hex.EncodeToString(sum[:]), + "bitLength": bitLength, + "pem": string(pem.EncodeToMemory(block)), + }) +} + +// GetCertificateInfo handler +func GetCertificateInfo(c *gin.Context) { + query := c.Query("q") + resp, err := tls.Dial("tcp", query+":443", &tls.Config{InsecureSkipVerify: true, PreferServerCipherSuites: true}) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "status": false, + "message": "Could not establish connection with server.", + }) + return + } + cipherSuite := tls.CipherSuiteName(resp.ConnectionState().CipherSuite) + v := resp.ConnectionState().Version + var tlsVersion string + if v == tls.VersionSSL30 { + tlsVersion = "SSLv3" + } else if v == tls.VersionTLS10 { + tlsVersion = "TLSv1" + } else if v == tls.VersionTLS11 { + tlsVersion = "TLSv1.1" + } else if v == tls.VersionTLS12 { + tlsVersion = "TLSv1.2" + } else if v == tls.VersionTLS13 { + tlsVersion = "TLSv1.3" + } else { + tlsVersion = "unknown" + } + certificate := resp.ConnectionState().PeerCertificates[0] + rootCertificate := resp.ConnectionState().PeerCertificates[len(resp.ConnectionState().PeerCertificates)-1] + + var validationType string + for _, value := range certificate.PolicyIdentifiers { + if value.String() == "2.23.140.1.1" { + validationType = "EV" + } else if value.String() == "2.23.140.1.2.2" { + validationType = "OV" + } else if value.String() == "2.23.140.1.2.1" { + validationType = "DV" + } + } + + keyUsages := []int{} + keyUsagesText := []string{} + extendedKeyUsages := []int{} + extendedKeyUsagesText := []string{} + for _, value := range certificate.ExtKeyUsage { + switch value { + case 0: + // All Usages + extendedKeyUsages = append(extendedKeyUsages, 0) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Any/All Usages") + break + case 1: + // TLS Web Server Authentication + extendedKeyUsages = append(extendedKeyUsages, 1) + extendedKeyUsagesText = append(extendedKeyUsagesText, "TLS Web Server Authentication") + break + case 2: + // TLS Web Client Authentication + extendedKeyUsages = append(extendedKeyUsages, 2) + extendedKeyUsagesText = append(extendedKeyUsagesText, "TLS Web Client Authentication") + break + case 3: + // Code Signing + extendedKeyUsages = append(extendedKeyUsages, 3) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Code Signing") + break + case 4: + // Email Protection + extendedKeyUsages = append(extendedKeyUsages, 4) + extendedKeyUsagesText = append(extendedKeyUsagesText, "Email Protection (S/MIME)") + default: + break + } + } + + if certificate.KeyUsage&x509.KeyUsageCRLSign != 0 { + keyUsages = append(keyUsages, 0) + keyUsagesText = append(keyUsagesText, "CRL Signing") + } + if certificate.KeyUsage&x509.KeyUsageCertSign != 0 { + keyUsages = append(keyUsages, 1) + keyUsagesText = append(keyUsagesText, "Certificate Signing") + } + if certificate.KeyUsage&x509.KeyUsageContentCommitment != 0 { + keyUsages = append(keyUsages, 2) + keyUsagesText = append(keyUsagesText, "Content Commitment") + } + if certificate.KeyUsage&x509.KeyUsageDataEncipherment != 0 { + keyUsages = append(keyUsages, 3) + keyUsagesText = append(keyUsagesText, "Data Encipherment") + } + if certificate.KeyUsage&x509.KeyUsageDecipherOnly != 0 { + keyUsages = append(keyUsages, 4) + keyUsagesText = append(keyUsagesText, "Decipher Only") + } + if certificate.KeyUsage&x509.KeyUsageDigitalSignature != 0 { + keyUsages = append(keyUsages, 5) + keyUsagesText = append(keyUsagesText, "Digital Signature") + } + if certificate.KeyUsage&x509.KeyUsageEncipherOnly != 0 { + keyUsages = append(keyUsages, 6) + keyUsagesText = append(keyUsagesText, "Encipher Only") + } + if certificate.KeyUsage&x509.KeyUsageKeyAgreement != 0 { + keyUsages = append(keyUsages, 7) + keyUsagesText = append(keyUsagesText, "Key Agreement") + } + if certificate.KeyUsage&x509.KeyUsageKeyEncipherment != 0 { + keyUsages = append(keyUsages, 8) + keyUsagesText = append(keyUsagesText, "Key Encipherment") + } + + sum := sha1.Sum(certificate.Raw) + + var bitLength int + + switch certificate.PublicKeyAlgorithm { + case x509.RSA: + if rsaKey, ok := certificate.PublicKey.(*rsa.PublicKey); ok { + bitLength = rsaKey.N.BitLen() + } else { + panic("expected rsa.PublicKey for type x509.RSA") + } + case x509.ECDSA: + if ecdsaKey, ok := certificate.PublicKey.(*ecdsa.PublicKey); ok { + bitLength = ecdsaKey.Params().BitSize + } else { + panic("expected ecdsa.PublicKey for type x509.ECDSA") + } + } + + c.JSON(http.StatusOK, gin.H{ + "status": true, + "subject": gin.H{ + "commonName": certificate.Subject.CommonName, + "organization": certificate.Subject.Organization, + "organizationalUnit": certificate.Subject.OrganizationalUnit, + "locality": certificate.Subject.Locality, + "country": certificate.Subject.Country, + }, + "issuer": gin.H{ + "commonName": certificate.Issuer.CommonName, + "organization": certificate.Issuer.Organization, + "organizationalUnit": certificate.Issuer.OrganizationalUnit, + "locality": certificate.Issuer.Locality, + "country": certificate.Issuer.Country, + }, + "root": gin.H{ + "commonName": rootCertificate.Issuer.CommonName, + "organization": rootCertificate.Issuer.Organization, + "organizationalUnit": rootCertificate.Issuer.OrganizationalUnit, + "locality": rootCertificate.Issuer.Locality, + "country": rootCertificate.Issuer.Country, + }, + "aia": gin.H{ + "issuingCertificateURL": certificate.IssuingCertificateURL, + "ocspServer": certificate.OCSPServer, + }, + "validationType": validationType, + "signatureAlgorithm": certificate.SignatureAlgorithm.String(), + "publicKeyAlgorithm": certificate.PublicKeyAlgorithm.String(), + "serialNumber": certificate.SerialNumber.String(), + "notBefore": certificate.NotBefore, + "notAfter": certificate.NotAfter, + "keyUsage": keyUsages, + "keyUsageAsText": keyUsagesText, + "extendedKeyUsage": extendedKeyUsages, + "extendedKeyUsageAsText": extendedKeyUsagesText, + "san": certificate.DNSNames, + "emailAddresses": certificate.EmailAddresses, + "fingerprint": hex.EncodeToString(sum[:]), + "bitLength": bitLength, + "connection": gin.H{ + "tlsVersion": tlsVersion, + "cipherSuite": cipherSuite, + }, + }) +}